Add support for docker testclusters (#20247)

This commit is contained in:
Nick Cabatoff 2023-04-24 14:25:50 -04:00 committed by GitHub
parent a889ba1205
commit 22b00eba12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2632 additions and 272 deletions

View File

@ -13,8 +13,8 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/helper/testhelpers/docker"
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/logical"
)

View File

@ -14,7 +14,7 @@ import (
nomadapi "github.com/hashicorp/nomad/api"
"github.com/hashicorp/vault/helper/testhelpers"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/logical"
"github.com/mitchellh/mapstructure"
)

View File

@ -17,11 +17,9 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/builtin/logical/pki"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/builtin/logical/pki"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/stretchr/testify/require"
)
@ -232,7 +230,7 @@ func CheckWithClients(t *testing.T, network string, address string, url string,
// Start our service with a random name to not conflict with other
// threads.
ctx := context.Background()
ctr, _, _, err := cwRunner.Start(ctx, true, false)
result, err := cwRunner.Start(ctx, true, false)
if err != nil {
t.Fatalf("Could not start golang container for wget/curl checks: %s", err)
}
@ -258,14 +256,14 @@ func CheckWithClients(t *testing.T, network string, address string, url string,
wgetCmd = []string{"wget", "--verbose", "--ca-certificate=/root.pem", "--certificate=/client-cert.pem", "--private-key=/client-privkey.pem", url}
curlCmd = []string{"curl", "--verbose", "--cacert", "/root.pem", "--cert", "/client-cert.pem", "--key", "/client-privkey.pem", url}
}
if err := cwRunner.CopyTo(ctr.ID, "/", certCtx); err != nil {
if err := cwRunner.CopyTo(result.Container.ID, "/", certCtx); err != nil {
t.Fatalf("Could not copy certificate and key into container: %v", err)
}
for _, cmd := range [][]string{hostPrimeCmd, wgetCmd, curlCmd} {
t.Logf("Running client connection command: %v", cmd)
stdout, stderr, retcode, err := cwRunner.RunCmdWithOutput(ctx, ctr.ID, cmd)
stdout, stderr, retcode, err := cwRunner.RunCmdWithOutput(ctx, result.Container.ID, cmd)
if err != nil {
t.Fatalf("Could not run command (%v) in container: %v", cmd, err)
}
@ -295,7 +293,7 @@ func CheckDeltaCRL(t *testing.T, network string, address string, url string, roo
// Start our service with a random name to not conflict with other
// threads.
ctx := context.Background()
ctr, _, _, err := cwRunner.Start(ctx, true, false)
result, err := cwRunner.Start(ctx, true, false)
if err != nil {
t.Fatalf("Could not start golang container for wget2 delta CRL checks: %s", err)
}
@ -313,14 +311,14 @@ func CheckDeltaCRL(t *testing.T, network string, address string, url string, roo
certCtx := docker.NewBuildContext()
certCtx["root.pem"] = docker.PathContentsFromString(rootCert)
certCtx["crls.pem"] = docker.PathContentsFromString(crls)
if err := cwRunner.CopyTo(ctr.ID, "/", certCtx); err != nil {
if err := cwRunner.CopyTo(result.Container.ID, "/", certCtx); err != nil {
t.Fatalf("Could not copy certificate and key into container: %v", err)
}
for index, cmd := range [][]string{hostPrimeCmd, wgetCmd} {
t.Logf("Running client connection command: %v", cmd)
stdout, stderr, retcode, err := cwRunner.RunCmdWithOutput(ctx, ctr.ID, cmd)
stdout, stderr, retcode, err := cwRunner.RunCmdWithOutput(ctx, result.Container.ID, cmd)
if err != nil {
t.Fatalf("Could not run command (%v) in container: %v", cmd, err)
}

View File

@ -10,8 +10,7 @@ import (
"testing"
"github.com/hashicorp/vault/builtin/logical/pki"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/stretchr/testify/require"
)
@ -67,11 +66,12 @@ func RunZLintContainer(t *testing.T, certificate string) []byte {
buildZLintContainer(t)
})
ctx := context.Background()
// We don't actually care about the address, we just want to start the
// container so we can run commands in it. We'd ideally like to skip this
// step and only build a new image, but the zlint output would be
// intermingled with container build stages, so its not that useful.
ctr, _, _, err := zRunner.Start(context.Background(), true, false)
result, err := zRunner.Start(ctx, true, false)
if err != nil {
t.Fatalf("Could not start golang container for zlint: %s", err)
}
@ -79,13 +79,13 @@ func RunZLintContainer(t *testing.T, certificate string) []byte {
// Copy the cert into the newly running container.
certCtx := docker.NewBuildContext()
certCtx["cert.pem"] = docker.PathContentsFromBytes([]byte(certificate))
if err := zRunner.CopyTo(ctr.ID, "/go/", certCtx); err != nil {
if err := zRunner.CopyTo(result.Container.ID, "/go/", certCtx); err != nil {
t.Fatalf("Could not copy certificate into container: %v", err)
}
// Run the zlint command and save the output.
cmd := []string{"/go/bin/zlint", "/go/cert.pem"}
stdout, stderr, retcode, err := zRunner.RunCmdWithOutput(context.Background(), ctr.ID, cmd)
stdout, stderr, retcode, err := zRunner.RunCmdWithOutput(ctx, result.Container.ID, cmd)
if err != nil {
t.Fatalf("Could not run command in container: %v", err)
}
@ -100,7 +100,7 @@ func RunZLintContainer(t *testing.T, certificate string) []byte {
}
// Clean up after ourselves.
if err := zRunner.Stop(context.Background(), ctr.ID); err != nil {
if err := zRunner.Stop(context.Background(), result.Container.ID); err != nil {
t.Fatalf("failed to stop container: %v", err)
}

View File

@ -11,8 +11,8 @@ import (
"testing"
"github.com/hashicorp/go-secure-stdlib/base62"
"github.com/hashicorp/vault/helper/testhelpers/docker"
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
"github.com/hashicorp/vault/sdk/logical"
rabbithole "github.com/michaelklishin/rabbit-hole/v2"

View File

@ -16,18 +16,16 @@ import (
"time"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
"github.com/hashicorp/vault/sdk/logical"
"golang.org/x/crypto/ssh"
"github.com/hashicorp/vault/builtin/credential/userpass"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
)
const (

3
changelog/20247.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
sdk: Add new docker-based cluster testing framework to the sdk.
```

View File

@ -14,8 +14,8 @@ import (
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/internalshared/configutil"
"github.com/hashicorp/vault/sdk/helper/docker"
)
func TestTransitWrapper_Lifecycle(t *testing.T) {

118
go.mod
View File

@ -1,11 +1,11 @@
module github.com/hashicorp/vault
// The go version directive value isn't consulted when building our production binaries,
// The go version directive value isn't consulted when building our production binaries,
// and the vault module isn't intended to be imported into other projects. As such the
// impact of this setting is usually rather limited. Note however that in some cases the
// Go project introduces new semantics for handling of go.mod depending on the value.
//
// The general policy for updating it is: when the Go major version used on the branch is
// The general policy for updating it is: when the Go major version used on the branch is
// updated. If we choose not to do so at some point (e.g. because we don't want some new
// semantic related to Go module handling), this comment should be updated to explain that.
//
@ -23,9 +23,9 @@ replace github.com/hashicorp/vault/api/auth/userpass => ./api/auth/userpass
replace github.com/hashicorp/vault/sdk => ./sdk
require (
cloud.google.com/go/monitoring v1.8.0
cloud.google.com/go/spanner v1.41.0
cloud.google.com/go/storage v1.27.0
cloud.google.com/go/monitoring v1.12.0
cloud.google.com/go/spanner v1.44.0
cloud.google.com/go/storage v1.28.1
github.com/Azure/azure-storage-blob-go v0.14.0
github.com/Azure/go-autorest/autorest v0.11.28
github.com/Azure/go-autorest/autorest/adal v0.9.20
@ -49,8 +49,6 @@ require (
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/denisenkom/go-mssqldb v0.12.2
github.com/docker/docker v23.0.1+incompatible
github.com/docker/go-connections v0.4.0
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74
github.com/dustin/go-humanize v1.0.0
github.com/fatih/color v1.13.0
@ -182,8 +180,8 @@ require (
github.com/pkg/errors v0.9.1
github.com/posener/complete v1.2.3
github.com/pquerna/otp v1.2.1-0.20191009055518-468c2dd2b58d
github.com/prometheus/client_golang v1.11.1
github.com/prometheus/common v0.30.0
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/common v0.37.0
github.com/rboyer/safeio v0.2.1
github.com/ryanuber/columnize v2.1.0+incompatible
github.com/ryanuber/go-glob v1.0.0
@ -191,45 +189,45 @@ require (
github.com/sethvargo/go-limiter v0.7.1
github.com/shirou/gopsutil/v3 v3.22.6
github.com/stretchr/testify v1.8.2
go.etcd.io/bbolt v1.3.6
go.etcd.io/bbolt v1.3.7
go.etcd.io/etcd/client/pkg/v3 v3.5.7
go.etcd.io/etcd/client/v2 v2.305.0
go.etcd.io/etcd/client/v2 v2.305.5
go.etcd.io/etcd/client/v3 v3.5.7
go.mongodb.org/atlas v0.24.0
go.mongodb.org/mongo-driver v1.11.3
go.opentelemetry.io/otel v1.11.2
go.opentelemetry.io/otel/sdk v1.11.2
go.opentelemetry.io/otel/trace v1.11.2
go.opentelemetry.io/otel v1.14.0
go.opentelemetry.io/otel/sdk v1.14.0
go.opentelemetry.io/otel/trace v1.14.0
go.uber.org/atomic v1.10.0
go.uber.org/goleak v1.1.12
go.uber.org/goleak v1.2.1
golang.org/x/crypto v0.6.0
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
golang.org/x/net v0.7.0
golang.org/x/oauth2 v0.4.0
golang.org/x/net v0.8.0
golang.org/x/oauth2 v0.5.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.5.0
golang.org/x/term v0.5.0
golang.org/x/sys v0.6.0
golang.org/x/term v0.6.0
golang.org/x/tools v0.6.0
google.golang.org/api v0.109.0
google.golang.org/grpc v1.51.0
google.golang.org/api v0.110.0
google.golang.org/grpc v1.53.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
google.golang.org/protobuf v1.28.1
gopkg.in/ory-am/dockertest.v3 v3.3.4
gopkg.in/square/go-jose.v2 v2.6.0
gotest.tools/gotestsum v1.9.0
honnef.co/go/tools v0.4.3
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5
layeh.com/radius v0.0.0-20190322222518-890bc1058917
mvdan.cc/gofumpt v0.3.1
nhooyr.io/websocket v1.8.7
)
require (
cloud.google.com/go v0.107.0 // indirect
cloud.google.com/go/compute v1.14.0 // indirect
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.8.0 // indirect
cloud.google.com/go/kms v1.8.0 // indirect
cloud.google.com/go/iam v0.12.0 // indirect
cloud.google.com/go/kms v1.9.0 // indirect
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go v67.2.0+incompatible // indirect
@ -258,10 +256,8 @@ require (
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/apache/arrow/go/arrow v0.0.0-20210818145353-234c94e4ce64 // indirect
github.com/aws/aws-sdk-go-v2 v1.8.0 // indirect
@ -279,24 +275,24 @@ require (
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/centrify/cloud-golang-sdk v0.0.0-20210923165758-a8c48d049166 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible // indirect
github.com/circonus-labs/circonusllhist v0.1.3 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/cloudfoundry-community/go-cfclient v0.0.0-20210823134051-721f0e559306 // indirect
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect
github.com/containerd/containerd v1.6.19 // indirect
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/containerd/continuity v0.3.0 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/coreos/go-oidc/v3 v3.1.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/couchbase/gocb/v2 v2.3.3 // indirect
github.com/couchbase/gocbcore/v10 v10.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@ -305,16 +301,18 @@ require (
github.com/digitalocean/godo v1.7.5 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dnephin/pflag v1.0.7 // indirect
github.com/docker/cli v20.10.18+incompatible // indirect
github.com/docker/cli v20.10.20+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v23.0.4+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect
github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/envoyproxy/go-control-plane v0.10.3 // indirect
github.com/envoyproxy/protoc-gen-validate v0.9.1 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.3.1 // indirect
github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7 // indirect
github.com/gammazero/workerpool v0.0.0-20190406235159-88d534f22b56 // indirect
@ -326,7 +324,7 @@ require (
github.com/go-openapi/analysis v0.20.0 // indirect
github.com/go-openapi/errors v0.20.1 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/loads v0.20.2 // indirect
github.com/go-openapi/runtime v0.19.24 // indirect
github.com/go-openapi/spec v0.20.3 // indirect
@ -340,13 +338,13 @@ require (
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/flatbuffers v2.0.0+incompatible // indirect
github.com/google/flatbuffers v2.0.8+incompatible // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/gophercloud/gophercloud v0.1.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
@ -386,7 +384,7 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lib/pq v1.10.6 // indirect
@ -396,13 +394,13 @@ require (
github.com/mattn/go-ieproxy v0.0.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mediocregopher/radix/v4 v4.1.1 // indirect
github.com/miekg/dns v1.1.41 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/hashstructure v1.1.0 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mongodb-forks/digest v1.0.4 // indirect
@ -411,20 +409,20 @@ require (
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect
github.com/nwaples/rardecode v1.1.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/opencontainers/runc v1.1.4 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
github.com/opencontainers/runc v1.1.6 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/oracle/oci-go-sdk/v60 v60.0.0 // indirect
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.8 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/pquerna/cachecontrol v0.1.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
@ -463,19 +461,19 @@ require (
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
gopkg.in/resty.v1 v1.12.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.25.3 // indirect
k8s.io/apimachinery v0.25.3 // indirect
k8s.io/client-go v0.25.3 // indirect
k8s.io/klog/v2 v2.70.1 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/api v0.26.2 // indirect
k8s.io/apimachinery v0.26.2 // indirect
k8s.io/client-go v0.26.2 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

1199
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ import (
"testing"
"github.com/Azure/azure-storage-blob-go/azblob"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
)
type Config struct {

View File

@ -13,7 +13,7 @@ import (
"time"
"github.com/gocql/gocql"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
)
type containerConfig struct {

View File

@ -11,7 +11,7 @@ import (
consulapi "github.com/hashicorp/consul/api"
goversion "github.com/hashicorp/go-version"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
)
type Config struct {

View File

@ -11,7 +11,7 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
clientv3 "go.etcd.io/etcd/client/v3"
)

View File

@ -12,7 +12,7 @@ import (
"testing"
"cloud.google.com/go/storage"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)

View File

@ -9,7 +9,7 @@ import (
"testing"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/ldaputil"
)

View File

@ -14,7 +14,7 @@ import (
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
)
type Config struct {

View File

@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"

View File

@ -11,7 +11,7 @@ import (
"os"
"testing"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
)
const mssqlPassword = "yourStrong(!)Password"

View File

@ -11,7 +11,7 @@ import (
"strings"
"testing"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
)
type Config struct {

View File

@ -11,7 +11,7 @@ import (
"os"
"testing"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
)
func PrepareTestContainer(t *testing.T, version string) (func(), string) {

View File

@ -10,7 +10,7 @@ import (
aero "github.com/aerospike/aerospike-client-go/v5"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/physical"
)

View File

@ -12,7 +12,7 @@ import (
"testing"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/physical"
)

View File

@ -15,7 +15,7 @@ import (
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/physical"
)

View File

@ -15,7 +15,7 @@ import (
"github.com/go-test/deep"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/physical"

View File

@ -14,9 +14,9 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/helper/testhelpers/docker"
dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing"
"github.com/hashicorp/vault/sdk/helper/docker"
influx "github.com/influxdata/influxdb1-client/v2"
"github.com/stretchr/testify/require"
)

View File

@ -12,11 +12,11 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/helper/testhelpers/docker"
"github.com/hashicorp/vault/helper/testhelpers/postgresql"
"github.com/hashicorp/vault/sdk/database/dbplugin/v5"
dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing"
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
"github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/template"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -1,12 +1,13 @@
module github.com/hashicorp/vault/sdk
// The Go version directive for the sdk package should be updated whenever the
// the root go.mod for the hashicorp/vault project gets updated.
go 1.20
go 1.19
require (
github.com/armon/go-metrics v0.4.1
github.com/armon/go-radix v1.0.0
github.com/cenkalti/backoff/v3 v3.2.2
github.com/docker/docker v23.0.4+incompatible
github.com/docker/go-connections v0.4.0
github.com/evanphx/json-patch/v5 v5.6.0
github.com/fatih/structs v1.1.0
github.com/go-ldap/ldap/v3 v3.4.1
@ -14,6 +15,7 @@ require (
github.com/golang/protobuf v1.5.2
github.com/golang/snappy v0.0.4
github.com/hashicorp/errwrap v1.1.0
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-hclog v1.4.0
github.com/hashicorp/go-immutable-radix v1.3.1
github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0
@ -38,42 +40,56 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/pierrec/lz4 v2.6.1+incompatible
github.com/ryanuber/go-glob v1.0.0
github.com/stretchr/testify v1.8.1
github.com/stretchr/testify v1.8.2
go.uber.org/atomic v1.9.0
golang.org/x/crypto v0.6.0
golang.org/x/text v0.7.0
google.golang.org/grpc v1.51.0
golang.org/x/net v0.8.0
golang.org/x/text v0.8.0
google.golang.org/grpc v1.53.0
google.golang.org/protobuf v1.28.1
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/frankban/quicktest v1.10.0 // indirect
github.com/frankban/quicktest v1.11.3 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/patternmatcher v0.5.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
github.com/opencontainers/runc v1.1.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.8.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.4.0 // indirect
)

View File

@ -1,6 +1,10 @@
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -19,11 +23,22 @@ github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v23.0.4+incompatible h1:Kd3Bh9V/rO+XpTP/BLqM+gx8z7+Yb0AA2Ibj+nNo4ek=
github.com/docker/docker v23.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@ -31,8 +46,8 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -45,6 +60,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -55,9 +72,9 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
@ -113,8 +130,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
github.com/hashicorp/vault/api v1.9.0 h1:ab7dI6W8DuCY7yCU8blo0UCYl2oHre/dloCmzMWg9w8=
github.com/hashicorp/vault/api v1.9.0/go.mod h1:lloELQP4EyhjnCQhF8agKvWIVTmxbpEJj70b98959sM=
github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLOAV38=
github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@ -122,10 +139,14 @@ github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZ
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -156,15 +177,29 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8=
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/opencontainers/runc v1.1.6 h1:XbhB8IfG/EsnhNvZtNdLB0GBw92GYEFvKlhaJk9jUgA=
github.com/opencontainers/runc v1.1.6/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
@ -196,6 +231,8 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@ -208,26 +245,40 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -237,29 +288,41 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
@ -280,3 +343,5 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=

View File

@ -5,6 +5,7 @@ package docker
import (
"archive/tar"
"bufio"
"bytes"
"context"
"encoding/base64"
@ -16,11 +17,13 @@ import (
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/client"
@ -30,33 +33,61 @@ import (
"github.com/hashicorp/go-uuid"
)
const DockerAPIVersion = "1.40"
type Runner struct {
DockerAPI *client.Client
RunOptions RunOptions
}
type RunOptions struct {
ImageRepo string
ImageTag string
ContainerName string
Cmd []string
Entrypoint []string
Env []string
NetworkID string
CopyFromTo map[string]string
Ports []string
DoNotAutoRemove bool
AuthUsername string
AuthPassword string
LogConsumer func(string)
ImageRepo string
ImageTag string
ContainerName string
Cmd []string
Entrypoint []string
Env []string
NetworkName string
NetworkID string
CopyFromTo map[string]string
Ports []string
DoNotAutoRemove bool
AuthUsername string
AuthPassword string
OmitLogTimestamps bool
LogConsumer func(string)
Capabilities []string
PreDelete bool
PostStart func(string, string) error
LogStderr io.Writer
LogStdout io.Writer
}
func NewDockerAPI() (*client.Client, error) {
return client.NewClientWithOpts(client.FromEnv, client.WithVersion(DockerAPIVersion))
}
func NewServiceRunner(opts RunOptions) (*Runner, error) {
dapi, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.39"))
dapi, err := NewDockerAPI()
if err != nil {
return nil, err
}
if opts.NetworkName == "" {
opts.NetworkName = os.Getenv("TEST_DOCKER_NETWORK_NAME")
}
if opts.NetworkName != "" {
nets, err := dapi.NetworkList(context.TODO(), types.NetworkListOptions{
Filters: filters.NewArgs(filters.Arg("name", opts.NetworkName)),
})
if err != nil {
return nil, err
}
if len(nets) != 1 {
return nil, fmt.Errorf("expected exactly one docker network named %q, got %d", opts.NetworkName, len(nets))
}
opts.NetworkID = nets[0].ID
}
if opts.NetworkID == "" {
opts.NetworkID = os.Getenv("TEST_DOCKER_NETWORK_ID")
}
@ -148,38 +179,112 @@ func (d *Runner) StartService(ctx context.Context, connect ServiceAdapter) (*Ser
return serv, err
}
type LogConsumerWriter struct {
consumer func(string)
}
func (l LogConsumerWriter) Write(p []byte) (n int, err error) {
// TODO this assumes that we're never passed partial log lines, which
// seems a safe assumption for now based on how docker looks to implement
// logging, but might change in the future.
scanner := bufio.NewScanner(bytes.NewReader(p))
scanner.Buffer(make([]byte, 64*1024), bufio.MaxScanTokenSize)
for scanner.Scan() {
l.consumer(scanner.Text())
}
return len(p), nil
}
var _ io.Writer = &LogConsumerWriter{}
// StartNewService will start the runner's configured docker container but with the
// ability to control adding a name suffix or forcing a local address to be returned.
// 'addSuffix' will add a random UUID to the end of the container name.
// 'forceLocalAddr' will force the container address returned to be in the
// form of '127.0.0.1:1234' where 1234 is the mapped container port.
func (d *Runner) StartNewService(ctx context.Context, addSuffix, forceLocalAddr bool, connect ServiceAdapter) (*Service, string, error) {
container, hostIPs, containerID, err := d.Start(context.Background(), addSuffix, forceLocalAddr)
if d.RunOptions.PreDelete {
name := d.RunOptions.ContainerName
matches, err := d.DockerAPI.ContainerList(ctx, types.ContainerListOptions{
All: true,
// TODO use labels to ensure we don't delete anything we shouldn't
Filters: filters.NewArgs(
filters.Arg("name", name),
),
})
if err != nil {
return nil, "", fmt.Errorf("failed to list containers named %q", name)
}
for _, cont := range matches {
err = d.DockerAPI.ContainerRemove(ctx, cont.ID, types.ContainerRemoveOptions{Force: true})
if err != nil {
return nil, "", fmt.Errorf("failed to pre-delete container named %q", name)
}
}
}
result, err := d.Start(context.Background(), addSuffix, forceLocalAddr)
if err != nil {
return nil, "", err
}
cleanup := func() {
if d.RunOptions.LogConsumer != nil {
rc, err := d.DockerAPI.ContainerLogs(ctx, container.ID, types.ContainerLogsOptions{
var wg sync.WaitGroup
consumeLogs := false
var logStdout, logStderr io.Writer
if d.RunOptions.LogStdout != nil && d.RunOptions.LogStderr != nil {
consumeLogs = true
logStdout = d.RunOptions.LogStdout
logStderr = d.RunOptions.LogStderr
} else if d.RunOptions.LogConsumer != nil {
consumeLogs = true
logStdout = &LogConsumerWriter{d.RunOptions.LogConsumer}
logStderr = &LogConsumerWriter{d.RunOptions.LogConsumer}
}
// The waitgroup wg is used here to support some stuff in NewDockerCluster.
// We can't generate the PKI cert for the https listener until we know the
// container's address, meaning we must first start the container, then
// generate the cert, then copy it into the container, then signal Vault
// to reload its config/certs. However, if we SIGHUP Vault before Vault
// has installed its signal handler, that will kill Vault, since the default
// behaviour for HUP is termination. So the PostStart that NewDockerCluster
// passes in (which does all that PKI cert stuff) waits to see output from
// Vault on stdout/stderr before it sends the signal, and we don't want to
// run the PostStart until we've hooked into the docker logs.
if consumeLogs {
wg.Add(1)
go func() {
// We must run inside a goroutine because we're using Follow:true,
// and StdCopy will block until the log stream is closed.
stream, err := d.DockerAPI.ContainerLogs(context.Background(), result.Container.ID, types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Timestamps: true,
Timestamps: !d.RunOptions.OmitLogTimestamps,
Details: true,
Follow: true,
})
if err == nil {
b, err := ioutil.ReadAll(rc)
wg.Done()
if err != nil {
d.RunOptions.LogConsumer(fmt.Sprintf("error reading container logs: %v", err))
} else {
_, err := stdcopy.StdCopy(logStdout, logStderr, stream)
if err != nil {
d.RunOptions.LogConsumer(fmt.Sprintf("error reading container logs, err=%v, read: %s", err, string(b)))
} else {
d.RunOptions.LogConsumer(string(b))
d.RunOptions.LogConsumer(fmt.Sprintf("error demultiplexing docker logs: %v", err))
}
}
}
}()
}
wg.Wait()
if d.RunOptions.PostStart != nil {
if err := d.RunOptions.PostStart(result.Container.ID, result.realIP); err != nil {
return nil, "", fmt.Errorf("poststart failed: %w", err)
}
}
cleanup := func() {
for i := 0; i < 10; i++ {
err := d.DockerAPI.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{Force: true})
if err == nil {
err := d.DockerAPI.ContainerRemove(ctx, result.Container.ID, types.ContainerRemoveOptions{Force: true})
if err == nil || client.IsErrNotFound(err) {
return
}
time.Sleep(1 * time.Second)
@ -190,7 +295,7 @@ func (d *Runner) StartNewService(ctx context.Context, addSuffix, forceLocalAddr
bo.MaxInterval = time.Second * 5
bo.MaxElapsedTime = 2 * time.Minute
pieces := strings.Split(hostIPs[0], ":")
pieces := strings.Split(result.addrs[0], ":")
portInt, err := strconv.Atoi(pieces[1])
if err != nil {
return nil, "", err
@ -198,6 +303,11 @@ func (d *Runner) StartNewService(ctx context.Context, addSuffix, forceLocalAddr
var config ServiceConfig
err = backoff.Retry(func() error {
container, err := d.DockerAPI.ContainerInspect(ctx, result.Container.ID)
if err != nil || !container.State.Running {
return backoff.Permanent(fmt.Errorf("failed inspect or container %q not running: %w", result.Container.ID, err))
}
c, err := connect(ctx, pieces[0], portInt)
if err != nil {
return err
@ -219,8 +329,8 @@ func (d *Runner) StartNewService(ctx context.Context, addSuffix, forceLocalAddr
return &Service{
Config: config,
Cleanup: cleanup,
Container: container,
}, containerID, nil
Container: result.Container,
}, result.Container.ID, nil
}
type Service struct {
@ -229,12 +339,18 @@ type Service struct {
Container *types.ContainerJSON
}
func (d *Runner) Start(ctx context.Context, addSuffix, forceLocalAddr bool) (*types.ContainerJSON, []string, string, error) {
type startResult struct {
Container *types.ContainerJSON
addrs []string
realIP string
}
func (d *Runner) Start(ctx context.Context, addSuffix, forceLocalAddr bool) (*startResult, error) {
name := d.RunOptions.ContainerName
if addSuffix {
suffix, err := uuid.GenerateUUID()
if err != nil {
return nil, nil, "", err
return nil, err
}
name += "-" + suffix
}
@ -259,6 +375,9 @@ func (d *Runner) Start(ctx context.Context, addSuffix, forceLocalAddr bool) (*ty
AutoRemove: !d.RunOptions.DoNotAutoRemove,
PublishAllPorts: true,
}
if len(d.RunOptions.Capabilities) > 0 {
hostConfig.CapAdd = d.RunOptions.Capabilities
}
netConfig := &network.NetworkingConfig{}
if d.RunOptions.NetworkID != "" {
@ -276,7 +395,7 @@ func (d *Runner) Start(ctx context.Context, addSuffix, forceLocalAddr bool) (*ty
"password": d.RunOptions.AuthPassword,
}
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
return nil, nil, "", err
return nil, err
}
opts.RegistryAuth = base64.URLEncoding.EncodeToString(buf.Bytes())
}
@ -287,46 +406,74 @@ func (d *Runner) Start(ctx context.Context, addSuffix, forceLocalAddr bool) (*ty
c, err := d.DockerAPI.ContainerCreate(ctx, cfg, hostConfig, netConfig, nil, cfg.Hostname)
if err != nil {
return nil, nil, "", fmt.Errorf("container create failed: %v", err)
return nil, fmt.Errorf("container create failed: %v", err)
}
for from, to := range d.RunOptions.CopyFromTo {
if err := copyToContainer(ctx, d.DockerAPI, c.ID, from, to); err != nil {
_ = d.DockerAPI.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{})
return nil, nil, "", err
return nil, err
}
}
err = d.DockerAPI.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
if err != nil {
_ = d.DockerAPI.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{})
return nil, nil, "", fmt.Errorf("container start failed: %v", err)
return nil, fmt.Errorf("container start failed: %v", err)
}
inspect, err := d.DockerAPI.ContainerInspect(ctx, c.ID)
if err != nil {
_ = d.DockerAPI.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{})
return nil, nil, "", err
return nil, err
}
var addrs []string
for _, port := range d.RunOptions.Ports {
pieces := strings.Split(port, "/")
if len(pieces) < 2 {
return nil, nil, "", fmt.Errorf("expected port of the form 1234/tcp, got: %s", port)
return nil, fmt.Errorf("expected port of the form 1234/tcp, got: %s", port)
}
if d.RunOptions.NetworkID != "" && !forceLocalAddr {
addrs = append(addrs, fmt.Sprintf("%s:%s", cfg.Hostname, pieces[0]))
} else {
mapped, ok := inspect.NetworkSettings.Ports[nat.Port(port)]
if !ok || len(mapped) == 0 {
return nil, nil, "", fmt.Errorf("no port mapping found for %s", port)
return nil, fmt.Errorf("no port mapping found for %s", port)
}
addrs = append(addrs, fmt.Sprintf("127.0.0.1:%s", mapped[0].HostPort))
}
}
return &inspect, addrs, c.ID, nil
var realIP string
if d.RunOptions.NetworkID == "" {
if len(inspect.NetworkSettings.Networks) > 1 {
return nil, fmt.Errorf("Set d.RunOptions.NetworkName instead for container with multiple networks: %v", inspect.NetworkSettings.Networks)
}
for _, network := range inspect.NetworkSettings.Networks {
realIP = network.IPAddress
break
}
} else {
realIP = inspect.NetworkSettings.Networks[d.RunOptions.NetworkName].IPAddress
}
return &startResult{
Container: &inspect,
addrs: addrs,
realIP: realIP,
}, nil
}
func (d *Runner) RefreshFiles(ctx context.Context, containerID string) error {
for from, to := range d.RunOptions.CopyFromTo {
if err := copyToContainer(ctx, d.DockerAPI, containerID, from, to); err != nil {
// TODO too drastic?
_ = d.DockerAPI.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{})
return err
}
}
return d.DockerAPI.ContainerKill(ctx, containerID, "SIGHUP")
}
func (d *Runner) Stop(ctx context.Context, containerID string) error {
@ -636,6 +783,10 @@ func (u BuildTags) Apply(cfg *types.ImageBuildOptions) error {
const containerfilePath = "_containerfile"
func (d *Runner) BuildImage(ctx context.Context, containerfile string, containerContext BuildContext, opts ...BuildOpt) ([]byte, error) {
return BuildImage(ctx, d.DockerAPI, containerfile, containerContext, opts...)
}
func BuildImage(ctx context.Context, api *client.Client, containerfile string, containerContext BuildContext, opts ...BuildOpt) ([]byte, error) {
var cfg types.ImageBuildOptions
// Build container context tarball, provisioning containerfile in.
@ -653,7 +804,7 @@ func (d *Runner) BuildImage(ctx context.Context, containerfile string, container
}
}
resp, err := d.DockerAPI.ImageBuild(ctx, tar, cfg)
resp, err := api.ImageBuild(ctx, tar, cfg)
if err != nil {
return nil, fmt.Errorf("failed to build image: %v", err)
}

View File

@ -0,0 +1,88 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package docker
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"sync"
"github.com/hashicorp/errwrap"
)
// ReloadFunc are functions that are called when a reload is requested
type ReloadFunc func() error
// CertificateGetter satisfies ReloadFunc and its GetCertificate method
// satisfies the tls.GetCertificate function signature. Currently it does not
// allow changing paths after the fact.
type CertificateGetter struct {
sync.RWMutex
cert *tls.Certificate
certFile string
keyFile string
passphrase string
}
func NewCertificateGetter(certFile, keyFile, passphrase string) *CertificateGetter {
return &CertificateGetter{
certFile: certFile,
keyFile: keyFile,
passphrase: passphrase,
}
}
func (cg *CertificateGetter) Reload() error {
certPEMBlock, err := ioutil.ReadFile(cg.certFile)
if err != nil {
return err
}
keyPEMBlock, err := ioutil.ReadFile(cg.keyFile)
if err != nil {
return err
}
// Check for encrypted pem block
keyBlock, _ := pem.Decode(keyPEMBlock)
if keyBlock == nil {
return errors.New("decoded PEM is blank")
}
if x509.IsEncryptedPEMBlock(keyBlock) {
keyBlock.Bytes, err = x509.DecryptPEMBlock(keyBlock, []byte(cg.passphrase))
if err != nil {
return errwrap.Wrapf("Decrypting PEM block failed {{err}}", err)
}
keyPEMBlock = pem.EncodeToMemory(keyBlock)
}
cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
if err != nil {
return err
}
cg.Lock()
defer cg.Unlock()
cg.cert = &cert
return nil
}
func (cg *CertificateGetter) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
cg.RLock()
defer cg.RUnlock()
if cg.cert == nil {
return nil, fmt.Errorf("nil certificate")
}
return cg.cert, nil
}

View File

@ -0,0 +1,967 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package docker
import (
"bufio"
"bytes"
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"math/big"
mathrand "math/rand"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
"github.com/docker/docker/api/types"
docker "github.com/docker/docker/client"
"github.com/hashicorp/go-cleanhttp"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/api"
dockhelper "github.com/hashicorp/vault/sdk/helper/docker"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/helper/testcluster"
uberAtomic "go.uber.org/atomic"
"golang.org/x/net/http2"
)
var (
_ testcluster.VaultCluster = &DockerCluster{}
_ testcluster.VaultClusterNode = &DockerClusterNode{}
)
// DockerCluster is used to managing the lifecycle of the test Vault cluster
type DockerCluster struct {
ClusterName string
RaftStorage bool
ClusterNodes []*DockerClusterNode
// Certificate fields
CACert *x509.Certificate
CACertBytes []byte
CACertPEM []byte
CACertPEMFile string
CAKey *ecdsa.PrivateKey
CAKeyPEM []byte
RootCAs *x509.CertPool
barrierKeys [][]byte
recoveryKeys [][]byte
tmpDir string
// rootToken is the initial root token created when the Vault cluster is
// created.
rootToken string
dockerAPI *docker.Client
ID string
Logger log.Logger
}
func (dc *DockerCluster) NamedLogger(s string) log.Logger {
return dc.Logger.Named(s)
}
func (dc *DockerCluster) ClusterID() string {
return dc.ID
}
func (dc *DockerCluster) Nodes() []testcluster.VaultClusterNode {
ret := make([]testcluster.VaultClusterNode, len(dc.ClusterNodes))
for i := range dc.ClusterNodes {
ret[i] = dc.ClusterNodes[i]
}
return ret
}
func (dc *DockerCluster) GetBarrierKeys() [][]byte {
return dc.barrierKeys
}
func testKeyCopy(key []byte) []byte {
result := make([]byte, len(key))
copy(result, key)
return result
}
func (dc *DockerCluster) GetRecoveryKeys() [][]byte {
ret := make([][]byte, len(dc.recoveryKeys))
for i, k := range dc.recoveryKeys {
ret[i] = testKeyCopy(k)
}
return ret
}
func (dc *DockerCluster) GetBarrierOrRecoveryKeys() [][]byte {
return dc.GetBarrierKeys()
}
func (dc *DockerCluster) SetBarrierKeys(keys [][]byte) {
dc.barrierKeys = make([][]byte, len(keys))
for i, k := range keys {
dc.barrierKeys[i] = testKeyCopy(k)
}
}
func (dc *DockerCluster) SetRecoveryKeys(keys [][]byte) {
dc.recoveryKeys = make([][]byte, len(keys))
for i, k := range keys {
dc.recoveryKeys[i] = testKeyCopy(k)
}
}
func (dc *DockerCluster) GetCACertPEMFile() string {
return dc.CACertPEMFile
}
func (dc *DockerCluster) Cleanup() {
dc.cleanup()
}
func (dc *DockerCluster) cleanup() error {
var result *multierror.Error
for _, node := range dc.ClusterNodes {
if err := node.cleanup(); err != nil {
result = multierror.Append(result, err)
}
}
return result.ErrorOrNil()
}
// RootToken returns the root token of the cluster, if set
func (dc *DockerCluster) RootToken() string {
return dc.rootToken
}
func (n *DockerClusterNode) Name() string {
return n.Cluster.ClusterName + "-" + n.NodeID
}
func (dc *DockerCluster) setupNode0(ctx context.Context) error {
client := dc.ClusterNodes[0].client
var resp *api.InitResponse
var err error
for ctx.Err() == nil {
resp, err = client.Sys().Init(&api.InitRequest{
SecretShares: 3,
SecretThreshold: 3,
})
if err == nil && resp != nil {
break
}
time.Sleep(500 * time.Millisecond)
}
if err != nil {
return err
}
if resp == nil {
return fmt.Errorf("nil response to init request")
}
for _, k := range resp.Keys {
raw, err := hex.DecodeString(k)
if err != nil {
return err
}
dc.barrierKeys = append(dc.barrierKeys, raw)
}
for _, k := range resp.RecoveryKeys {
raw, err := hex.DecodeString(k)
if err != nil {
return err
}
dc.recoveryKeys = append(dc.recoveryKeys, raw)
}
dc.rootToken = resp.RootToken
client.SetToken(dc.rootToken)
dc.ClusterNodes[0].client = client
err = testcluster.UnsealNode(ctx, dc, 0)
if err != nil {
return err
}
err = ensureLeaderMatches(ctx, client, func(leader *api.LeaderResponse) error {
if !leader.IsSelf {
return fmt.Errorf("node %d leader=%v, expected=%v", 0, leader.IsSelf, true)
}
return nil
})
status, err := client.Sys().SealStatusWithContext(ctx)
if err != nil {
return err
}
dc.ID = status.ClusterID
return err
}
func (dc *DockerCluster) clusterReady(ctx context.Context) error {
for i, node := range dc.ClusterNodes {
expectLeader := i == 0
err := ensureLeaderMatches(ctx, node.client, func(leader *api.LeaderResponse) error {
if expectLeader != leader.IsSelf {
return fmt.Errorf("node %d leader=%v, expected=%v", i, leader.IsSelf, expectLeader)
}
return nil
})
if err != nil {
return err
}
}
return nil
}
func (dc *DockerCluster) setupCA(opts *DockerClusterOptions) error {
var err error
var caKey *ecdsa.PrivateKey
if opts != nil && opts.CAKey != nil {
caKey = opts.CAKey
} else {
caKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return err
}
}
dc.CAKey = caKey
var caBytes []byte
if opts != nil && len(opts.CACert) > 0 {
caBytes = opts.CACert
} else {
serialNumber := mathrand.New(mathrand.NewSource(time.Now().UnixNano())).Int63()
CACertTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: "localhost",
},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
SerialNumber: big.NewInt(serialNumber),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(262980 * time.Hour),
BasicConstraintsValid: true,
IsCA: true,
}
caBytes, err = x509.CreateCertificate(rand.Reader, CACertTemplate, CACertTemplate, caKey.Public(), caKey)
if err != nil {
return err
}
}
CACert, err := x509.ParseCertificate(caBytes)
if err != nil {
return err
}
dc.CACert = CACert
dc.CACertBytes = caBytes
dc.RootCAs = x509.NewCertPool()
dc.RootCAs.AddCert(CACert)
CACertPEMBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
dc.CACertPEM = pem.EncodeToMemory(CACertPEMBlock)
dc.CACertPEMFile = filepath.Join(dc.tmpDir, "ca", "ca.pem")
err = os.WriteFile(dc.CACertPEMFile, dc.CACertPEM, 0o755)
if err != nil {
return err
}
marshaledCAKey, err := x509.MarshalECPrivateKey(caKey)
if err != nil {
return err
}
CAKeyPEMBlock := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: marshaledCAKey,
}
dc.CAKeyPEM = pem.EncodeToMemory(CAKeyPEMBlock)
return nil
}
func (n *DockerClusterNode) setupCert(ip string) error {
var err error
n.ServerKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return err
}
serialNumber := mathrand.New(mathrand.NewSource(time.Now().UnixNano())).Int63()
certTemplate := &x509.Certificate{
Subject: pkix.Name{
CommonName: n.Name(),
},
DNSNames: []string{"localhost", n.Name()},
IPAddresses: []net.IP{net.IPv6loopback, net.ParseIP("127.0.0.1"), net.ParseIP(ip)},
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
SerialNumber: big.NewInt(serialNumber),
NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(262980 * time.Hour),
}
n.ServerCertBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, n.Cluster.CACert, n.ServerKey.Public(), n.Cluster.CAKey)
if err != nil {
return err
}
n.ServerCert, err = x509.ParseCertificate(n.ServerCertBytes)
if err != nil {
return err
}
n.ServerCertPEM = pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: n.ServerCertBytes,
})
marshaledKey, err := x509.MarshalECPrivateKey(n.ServerKey)
if err != nil {
return err
}
n.ServerKeyPEM = pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: marshaledKey,
})
n.ServerCertPEMFile = filepath.Join(n.WorkDir, "cert.pem")
err = os.WriteFile(n.ServerCertPEMFile, n.ServerCertPEM, 0o755)
if err != nil {
return err
}
n.ServerKeyPEMFile = filepath.Join(n.WorkDir, "key.pem")
err = os.WriteFile(n.ServerKeyPEMFile, n.ServerKeyPEM, 0o755)
if err != nil {
return err
}
tlsCert, err := tls.X509KeyPair(n.ServerCertPEM, n.ServerKeyPEM)
if err != nil {
return err
}
certGetter := NewCertificateGetter(n.ServerCertPEMFile, n.ServerKeyPEMFile, "")
if err := certGetter.Reload(); err != nil {
return err
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{tlsCert},
RootCAs: n.Cluster.RootCAs,
ClientCAs: n.Cluster.RootCAs,
ClientAuth: tls.RequestClientCert,
NextProtos: []string{"h2", "http/1.1"},
GetCertificate: certGetter.GetCertificate,
}
n.tlsConfig = tlsConfig
err = os.WriteFile(filepath.Join(n.WorkDir, "ca.pem"), n.Cluster.CACertPEM, 0o755)
if err != nil {
return err
}
return nil
}
func NewTestDockerCluster(t *testing.T, opts *DockerClusterOptions) *DockerCluster {
if opts == nil {
opts = &DockerClusterOptions{}
}
if opts.ClusterName == "" {
opts.ClusterName = strings.ReplaceAll(t.Name(), "/", "-")
}
if opts.Logger == nil {
opts.Logger = logging.NewVaultLogger(log.Trace).Named(t.Name())
}
if opts.NetworkName == "" {
opts.NetworkName = os.Getenv("TEST_DOCKER_NETWORK_NAME")
}
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
t.Cleanup(cancel)
dc, err := NewDockerCluster(ctx, opts)
if err != nil {
t.Fatal(err)
}
return dc
}
func NewDockerCluster(ctx context.Context, opts *DockerClusterOptions) (*DockerCluster, error) {
api, err := dockhelper.NewDockerAPI()
if err != nil {
return nil, err
}
if opts == nil {
opts = &DockerClusterOptions{}
}
if opts.Logger == nil {
opts.Logger = log.NewNullLogger()
}
if opts.VaultBinary != "" {
f, err := os.Open(opts.VaultBinary)
if err != nil {
return nil, err
}
data, err := io.ReadAll(f)
if err != nil {
return nil, err
}
bCtx := dockhelper.NewBuildContext()
bCtx["vault"] = &dockhelper.FileContents{
Data: data,
Mode: 0o755,
}
containerFile := fmt.Sprintf(`
FROM %s:%s
COPY vault /bin/vault
`, opts.ImageRepo, opts.ImageTag)
// TODO would be nice to use current SHA as tag
newTag := opts.ImageTag + "-testing"
_, err = dockhelper.BuildImage(ctx, api, containerFile, bCtx,
dockhelper.BuildRemove(true), dockhelper.BuildForceRemove(true),
dockhelper.BuildPullParent(true),
dockhelper.BuildTags([]string{opts.ImageRepo + ":" + newTag}))
if err != nil {
return nil, err
}
newOpts := *opts
newOpts.ImageTag = newTag
opts = &newOpts
}
dc := &DockerCluster{
dockerAPI: api,
RaftStorage: true,
ClusterName: opts.ClusterName,
Logger: opts.Logger,
}
if err := dc.setupDockerCluster(ctx, opts); err != nil {
dc.Cleanup()
return nil, err
}
return dc, nil
}
// DockerClusterNode represents a single instance of Vault in a cluster
type DockerClusterNode struct {
NodeID string
HostPort string
client *api.Client
ServerCert *x509.Certificate
ServerCertBytes []byte
ServerCertPEM []byte
ServerCertPEMFile string
ServerKey *ecdsa.PrivateKey
ServerKeyPEM []byte
ServerKeyPEMFile string
tlsConfig *tls.Config
WorkDir string
Cluster *DockerCluster
Container *types.ContainerJSON
dockerAPI *docker.Client
runner *dockhelper.Runner
Logger log.Logger
cleanupContainer func()
RealAPIAddr string
ContainerNetworkName string
ContainerIPAddress string
}
func (n *DockerClusterNode) TLSConfig() *tls.Config {
return n.tlsConfig.Clone()
}
func (n *DockerClusterNode) APIClient() *api.Client {
// We clone to ensure that whenever this method is called, the caller gets
// back a pristine client, without e.g. any namespace or token changes that
// might pollute a shared client. We clone the config instead of the
// client because (1) Client.clone propagates the replicationStateStore and
// the httpClient pointers, (2) it doesn't copy the tlsConfig at all, and
// (3) if clone returns an error, it doesn't feel as appropriate to panic
// below. Who knows why clone might return an error?
cfg := n.client.CloneConfig()
client, err := api.NewClient(cfg)
if err != nil {
// It seems fine to panic here, since this should be the same input
// we provided to NewClient when we were setup, and we didn't panic then.
// Better not to completely ignore the error though, suppose there's a
// bug in CloneConfig?
panic(fmt.Sprintf("NewClient error on cloned config: %v", err))
}
client.SetToken(n.client.Token())
return client
}
// NewAPIClient creates and configures a Vault API client to communicate with
// the running Vault Cluster for this DockerClusterNode
func (n *DockerClusterNode) apiConfig() (*api.Config, error) {
transport := cleanhttp.DefaultPooledTransport()
transport.TLSClientConfig = n.TLSConfig()
if err := http2.ConfigureTransport(transport); err != nil {
return nil, err
}
client := &http.Client{
Transport: transport,
CheckRedirect: func(*http.Request, []*http.Request) error {
// This can of course be overridden per-test by using its own client
return fmt.Errorf("redirects not allowed in these tests")
},
}
config := api.DefaultConfig()
if config.Error != nil {
return nil, config.Error
}
config.Address = fmt.Sprintf("https://%s", n.HostPort)
config.HttpClient = client
config.MaxRetries = 0
return config, nil
}
func (n *DockerClusterNode) newAPIClient() (*api.Client, error) {
config, err := n.apiConfig()
if err != nil {
return nil, err
}
client, err := api.NewClient(config)
if err != nil {
return nil, err
}
client.SetToken(n.Cluster.RootToken())
return client, nil
}
// Cleanup kills the container of the node
func (n *DockerClusterNode) Cleanup() {
n.cleanup()
}
func (n *DockerClusterNode) cleanup() error {
if n.Container == nil || n.Container.ID == "" {
return nil
}
n.cleanupContainer()
return nil
}
func (n *DockerClusterNode) start(ctx context.Context, caDir string, opts *DockerClusterOptions) error {
vaultCfg := map[string]interface{}{}
vaultCfg["listener"] = map[string]interface{}{
"tcp": map[string]interface{}{
"address": fmt.Sprintf("%s:%d", "0.0.0.0", 8200),
"tls_cert_file": "/vault/config/cert.pem",
"tls_key_file": "/vault/config/key.pem",
"telemetry": map[string]interface{}{
"unauthenticated_metrics_access": true,
},
},
}
vaultCfg["telemetry"] = map[string]interface{}{
"disable_hostname": true,
}
raftOpts := map[string]interface{}{
// TODO add options from vnc
"path": "/vault/file",
"node_id": n.NodeID,
}
vaultCfg["storage"] = map[string]interface{}{
"raft": raftOpts,
}
if opts != nil && opts.VaultNodeConfig != nil && len(opts.VaultNodeConfig.StorageOptions) > 0 {
for k, v := range opts.VaultNodeConfig.StorageOptions {
if _, ok := raftOpts[k].(string); !ok {
raftOpts[k] = v
}
}
}
//// disable_mlock is required for working in the Docker environment with
//// custom plugins
vaultCfg["disable_mlock"] = true
vaultCfg["api_addr"] = `https://{{- GetAllInterfaces | exclude "flags" "loopback" | attr "address" -}}:8200`
vaultCfg["cluster_addr"] = `https://{{- GetAllInterfaces | exclude "flags" "loopback" | attr "address" -}}:8201`
systemJSON, err := json.Marshal(vaultCfg)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(n.WorkDir, "system.json"), systemJSON, 0o644)
if err != nil {
return err
}
if opts.VaultNodeConfig != nil {
localCfg := *opts.VaultNodeConfig
if opts.VaultNodeConfig.LicensePath != "" {
b, err := os.ReadFile(opts.VaultNodeConfig.LicensePath)
if err != nil || len(b) == 0 {
return fmt.Errorf("unable to read LicensePath at %q: %w", opts.VaultNodeConfig.LicensePath, err)
}
localCfg.LicensePath = "/vault/config/license"
dest := filepath.Join(n.WorkDir, "license")
err = os.WriteFile(dest, b, 0o644)
if err != nil {
return fmt.Errorf("error writing license to %q: %w", dest, err)
}
}
userJSON, err := json.Marshal(localCfg)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(n.WorkDir, "user.json"), userJSON, 0o644)
if err != nil {
return err
}
}
// Create a temporary cert so vault will start up
err = n.setupCert("127.0.0.1")
if err != nil {
return err
}
// setup plugin bin copy if needed
copyFromTo := map[string]string{
n.WorkDir: "/vault/config",
caDir: "/usr/local/share/ca-certificates/",
}
repo := opts.ImageRepo
if repo == "" {
repo = "vault"
}
tag := opts.ImageTag
if tag == "" {
tag = "latest"
}
var wg sync.WaitGroup
wg.Add(1)
var seenLogs uberAtomic.Bool
logConsumer := func(s string) {
if seenLogs.CAS(false, true) {
wg.Done()
}
n.Logger.Trace(s)
}
logStdout := &LogConsumerWriter{logConsumer}
logStderr := &LogConsumerWriter{func(s string) {
if seenLogs.CAS(false, true) {
wg.Done()
}
testcluster.JSONLogNoTimestamp(n.Logger, s)
}}
r, err := dockhelper.NewServiceRunner(dockhelper.RunOptions{
ImageRepo: repo,
ImageTag: tag,
// We don't need to run update-ca-certificates in the container, because
// we're providing the CA in the raft join call, and otherwise Vault
// servers don't talk to one another on the API port.
Cmd: []string{"server"},
Env: []string{
// For now we're using disable_mlock, because this is for testing
// anyway, and because it prevents us using external plugins.
"SKIP_SETCAP=true",
"VAULT_LOG_FORMAT=json",
},
Ports: []string{"8200/tcp", "8201/tcp"},
ContainerName: n.Name(),
NetworkName: opts.NetworkName,
CopyFromTo: copyFromTo,
LogConsumer: logConsumer,
LogStdout: logStdout,
LogStderr: logStderr,
PreDelete: true,
DoNotAutoRemove: true,
PostStart: func(containerID string, realIP string) error {
err := n.setupCert(realIP)
if err != nil {
return err
}
// If we signal Vault before it installs its sighup handler, it'll die.
wg.Wait()
n.Logger.Trace("running poststart", "containerID", containerID, "IP", realIP)
return n.runner.RefreshFiles(ctx, containerID)
},
Capabilities: []string{"NET_ADMIN"},
OmitLogTimestamps: true,
})
if err != nil {
return err
}
n.runner = r
svc, _, err := r.StartNewService(ctx, false, false, func(ctx context.Context, host string, port int) (dockhelper.ServiceConfig, error) {
config, err := n.apiConfig()
if err != nil {
return nil, err
}
config.Address = fmt.Sprintf("https://%s:%d", host, port)
client, err := api.NewClient(config)
if err != nil {
return nil, err
}
_, err = client.Sys().SealStatus()
if err != nil {
return nil, err
}
return dockhelper.NewServiceHostPort(host, port), nil
})
if err != nil {
return err
}
n.HostPort = svc.Config.Address()
n.Container = svc.Container
netName := opts.NetworkName
if netName == "" {
if len(svc.Container.NetworkSettings.Networks) > 1 {
return fmt.Errorf("Set d.RunOptions.NetworkName instead for container with multiple networks: %v", svc.Container.NetworkSettings.Networks)
}
for netName = range svc.Container.NetworkSettings.Networks {
// Networks above is a map; we just need to find the first and
// only key of this map (network name). The range handles this
// for us, but we need a loop construction in order to use range.
}
}
n.ContainerNetworkName = netName
n.ContainerIPAddress = svc.Container.NetworkSettings.Networks[netName].IPAddress
n.RealAPIAddr = "https://" + n.ContainerIPAddress + ":8200"
n.cleanupContainer = svc.Cleanup
return nil
}
type LogConsumerWriter struct {
consumer func(string)
}
func (l LogConsumerWriter) Write(p []byte) (n int, err error) {
// TODO this assumes that we're never passed partial log lines, which
// seems a safe assumption for now based on how docker looks to implement
// logging, but might change in the future.
scanner := bufio.NewScanner(bytes.NewReader(p))
scanner.Buffer(make([]byte, 64*1024), bufio.MaxScanTokenSize)
for scanner.Scan() {
l.consumer(scanner.Text())
}
return len(p), nil
}
// DockerClusterOptions has options for setting up the docker cluster
type DockerClusterOptions struct {
testcluster.ClusterOptions
CAKey *ecdsa.PrivateKey
NetworkName string
ImageRepo string
ImageTag string
CloneCA *DockerCluster
VaultBinary string
}
func ensureLeaderMatches(ctx context.Context, client *api.Client, ready func(response *api.LeaderResponse) error) error {
var leader *api.LeaderResponse
var err error
for ctx.Err() == nil {
leader, err = client.Sys().Leader()
switch {
case err != nil:
case leader == nil:
err = fmt.Errorf("nil response to leader check")
default:
err = ready(leader)
if err == nil {
return nil
}
}
time.Sleep(500 * time.Millisecond)
}
return fmt.Errorf("error checking leader: %v", err)
}
const DefaultNumCores = 3
// creates a managed docker container running Vault
func (dc *DockerCluster) setupDockerCluster(ctx context.Context, opts *DockerClusterOptions) error {
if opts.TmpDir != "" {
if _, err := os.Stat(opts.TmpDir); os.IsNotExist(err) {
if err := os.MkdirAll(opts.TmpDir, 0o700); err != nil {
return err
}
}
dc.tmpDir = opts.TmpDir
} else {
tempDir, err := ioutil.TempDir("", "vault-test-cluster-")
if err != nil {
return err
}
dc.tmpDir = tempDir
}
caDir := filepath.Join(dc.tmpDir, "ca")
if err := os.MkdirAll(caDir, 0o755); err != nil {
return err
}
var numCores int
if opts.NumCores == 0 {
numCores = DefaultNumCores
} else {
numCores = opts.NumCores
}
if opts.CloneCA != nil {
dc.CACert = opts.CloneCA.CACert
dc.CACertBytes = opts.CloneCA.CACertBytes
dc.CACertPEM = opts.CloneCA.CACertPEM
dc.CACertPEMFile = opts.CloneCA.CACertPEMFile
dc.CAKey = opts.CloneCA.CAKey
dc.CAKeyPEM = opts.CloneCA.CAKeyPEM
dc.RootCAs = opts.CloneCA.RootCAs
} else {
if err := dc.setupCA(opts); err != nil {
return err
}
}
for i := 0; i < numCores; i++ {
if err := dc.addNode(ctx, opts); err != nil {
return err
}
if opts.SkipInit {
continue
}
if i == 0 {
if err := dc.setupNode0(ctx); err != nil {
return nil
}
} else {
if err := dc.joinNode(ctx, i, 0); err != nil {
return err
}
}
}
return nil
}
func (dc *DockerCluster) AddNode(ctx context.Context, opts *DockerClusterOptions) error {
leaderIdx, err := testcluster.LeaderNode(ctx, dc)
if err != nil {
return err
}
if err := dc.addNode(ctx, opts); err != nil {
return err
}
return dc.joinNode(ctx, len(dc.ClusterNodes)-1, leaderIdx)
}
func (dc *DockerCluster) addNode(ctx context.Context, opts *DockerClusterOptions) error {
i := len(dc.ClusterNodes)
nodeID := fmt.Sprintf("core-%d", i)
node := &DockerClusterNode{
dockerAPI: dc.dockerAPI,
NodeID: nodeID,
Cluster: dc,
WorkDir: filepath.Join(dc.tmpDir, nodeID),
Logger: dc.Logger.Named(nodeID),
}
dc.ClusterNodes = append(dc.ClusterNodes, node)
if err := os.MkdirAll(node.WorkDir, 0o755); err != nil {
return err
}
if err := node.start(ctx, filepath.Join(dc.tmpDir, "ca"), opts); err != nil {
return err
}
client, err := node.newAPIClient()
if err != nil {
return err
}
client.SetToken(dc.rootToken)
node.client = client
return nil
}
func (dc *DockerCluster) joinNode(ctx context.Context, nodeIdx int, leaderIdx int) error {
leader := dc.ClusterNodes[leaderIdx]
if nodeIdx >= len(dc.ClusterNodes) {
return fmt.Errorf("invalid node %d", nodeIdx)
}
node := dc.ClusterNodes[nodeIdx]
client := node.APIClient()
var resp *api.RaftJoinResponse
resp, err := client.Sys().RaftJoinWithContext(ctx, &api.RaftJoinRequest{
// When running locally on a bridge network, the containers must use their
// actual (private) IP to talk to one another. Our code must instead use
// the portmapped address since we're not on their network in that case.
LeaderAPIAddr: leader.RealAPIAddr,
LeaderCACert: string(dc.CACertPEM),
LeaderClientCert: string(node.ServerCertPEM),
LeaderClientKey: string(node.ServerKeyPEM),
})
if resp == nil || !resp.Joined {
return fmt.Errorf("nil or negative response from raft join request: %v", resp)
}
if err != nil {
return fmt.Errorf("failed to join cluster: %w", err)
}
return testcluster.UnsealNode(ctx, dc, nodeIdx)
}
/* Notes on testing the non-bridge network case:
- you need the test itself to be running in a container so that it can use
the network; create the network using
docker network create testvault
- this means that you need to mount the docker socket in that test container,
but on macos there's stuff that prevents that from working; to hack that,
on the host run
sudo ln -s "$HOME/Library/Containers/com.docker.docker/Data/docker.raw.sock" /var/run/docker.sock.raw
- run the test container like
docker run --rm -it --network testvault \
-v /var/run/docker.sock.raw:/var/run/docker.sock \
-v $(pwd):/home/circleci/go/src/github.com/hashicorp/vault/ \
-w /home/circleci/go/src/github.com/hashicorp/vault/ \
"docker.mirror.hashicorp.services/cimg/go:1.19.2" /bin/bash
- in the container you may need to chown/chmod /var/run/docker.sock; use `docker ps`
to test if it's working
*/

View File

@ -12,12 +12,14 @@ import (
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/go-test/deep"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/api"
@ -31,6 +33,8 @@ import (
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/internalshared/configutil"
"github.com/hashicorp/vault/physical/raft"
"github.com/hashicorp/vault/sdk/helper/testcluster"
"github.com/hashicorp/vault/sdk/helper/testcluster/docker"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
vaultseal "github.com/hashicorp/vault/vault/seal"
@ -438,27 +442,52 @@ func TestRaft_Configuration(t *testing.T) {
t.Parallel()
cluster, _ := raftCluster(t, nil)
defer cluster.Cleanup()
testRaft_Configuration(t, cluster)
}
for i, c := range cluster.Cores {
if c.Core.Sealed() {
t.Fatalf("failed to unseal core %d", i)
}
// TestRaft_Configuration_Docker is a variant of TestRaft_Configuration that
// uses docker containers for the vault nodes.
func TestRaft_Configuration_Docker(t *testing.T) {
t.Parallel()
binary := os.Getenv("VAULT_BINARY")
if binary == "" {
t.Skip("only running docker test when $VAULT_BINARY present")
}
opts := &docker.DockerClusterOptions{
ImageRepo: "hashicorp/vault",
// We're replacing the binary anyway, so we're not too particular about
// the docker image version tag.
ImageTag: "latest",
VaultBinary: binary,
ClusterOptions: testcluster.ClusterOptions{
VaultNodeConfig: &testcluster.VaultNodeConfig{
LogLevel: "TRACE",
// If you want the test to run faster locally, you could
// uncomment this performance_multiplier change.
//StorageOptions: map[string]string{
// "performance_multiplier": "1",
//},
},
},
}
cluster := docker.NewTestDockerCluster(t, opts)
defer cluster.Cleanup()
testRaft_Configuration(t, cluster)
client := cluster.Cores[0].Client
if err := cluster.AddNode(context.TODO(), opts); err != nil {
t.Fatal(err)
}
testRaft_Configuration(t, cluster)
}
func testRaft_Configuration(t *testing.T, cluster testcluster.VaultCluster) {
client := cluster.Nodes()[0].APIClient()
secret, err := client.Logical().Read("sys/storage/raft/configuration")
if err != nil {
t.Fatal(err)
}
servers := secret.Data["config"].(map[string]interface{})["servers"].([]interface{})
expected := map[string]bool{
"core-0": true,
"core-1": true,
"core-2": true,
}
if len(servers) != 3 {
t.Fatalf("incorrect number of servers in the configuration")
}
found := make(map[string]struct{})
for _, s := range servers {
server := s.(map[string]interface{})
nodeID := server["node_id"].(string)
@ -474,10 +503,14 @@ func TestRaft_Configuration(t *testing.T) {
}
}
delete(expected, nodeID)
found[nodeID] = struct{}{}
}
if len(expected) != 0 {
t.Fatalf("failed to read configuration successfully")
expected := map[string]struct{}{}
for i := range cluster.Nodes() {
expected[fmt.Sprintf("core-%d", i)] = struct{}{}
}
if diff := deep.Equal(expected, found); len(diff) > 0 {
t.Fatalf("configuration mismatch, diff: %v", diff)
}
}