Add support for docker testclusters (#20247)
This commit is contained in:
parent
a889ba1205
commit
22b00eba12
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
sdk: Add new docker-based cluster testing framework to the sdk.
|
||||
```
|
|
@ -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
118
go.mod
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
||||
"github.com/hashicorp/vault/sdk/helper/docker"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
|
|
44
sdk/go.mod
44
sdk/go.mod
|
@ -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
|
||||
)
|
||||
|
|
105
sdk/go.sum
105
sdk/go.sum
|
@ -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=
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
*/
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue