From 7012ce26371f1b756fbf9113838563c2a4431d51 Mon Sep 17 00:00:00 2001 From: Nick Cabatoff Date: Thu, 27 Apr 2023 09:41:49 -0400 Subject: [PATCH] Use a dedicated runner for the binary-based tests. (#20377) --- .../scripts/generate-test-package-lists.sh | 8 + .github/workflows/ci.yml | 5 +- .github/workflows/test-go.yml | 103 +--------- .../pkiext/{ => pkiext_binary}/acme_test.go | 7 +- builtin/logical/pkiext/test_helpers.go | 4 +- vault/external_tests/pprof/pprof.go | 140 ++++++++++++++ .../pprof/pprof_binary/pprof_test.go | 58 ++++++ vault/external_tests/pprof/pprof_test.go | 177 +----------------- vault/external_tests/raft/raft.go | 46 +++++ .../raft/raft_binary/raft_test.go | 46 +++++ vault/external_tests/raft/raft_test.go | 75 +------- 11 files changed, 321 insertions(+), 348 deletions(-) rename builtin/logical/pkiext/{ => pkiext_binary}/acme_test.go (98%) create mode 100644 vault/external_tests/pprof/pprof.go create mode 100644 vault/external_tests/pprof/pprof_binary/pprof_test.go create mode 100644 vault/external_tests/raft/raft.go create mode 100644 vault/external_tests/raft/raft_binary/raft_test.go diff --git a/.github/scripts/generate-test-package-lists.sh b/.github/scripts/generate-test-package-lists.sh index f2f5e3e695bd..0a92518d11aa 100755 --- a/.github/scripts/generate-test-package-lists.sh +++ b/.github/scripts/generate-test-package-lists.sh @@ -8,6 +8,8 @@ # solution. It distributes the entire set of test packages into 16 sublists, # which should roughly take an equal amount of time to complete. +set -e + test_packages=() base="github.com/hashicorp/vault" @@ -282,3 +284,9 @@ if [ "${ENTERPRISE:+x}" == "x" ] ; then test_packages[16]+=" $base/vault/external_tests/replicationext" test_packages[16]+=" $base/vault/external_tests/sealext" fi + +for i in $(cd $(git rev-parse --show-toplevel) && go list -test -json ./... | + jq -r '.ForTest | select(.!=null) | select(.|test("_binary$"))'); +do + test_packages[17]+=" $i" +done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce85fddda1d4..4e8c09f58316 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,7 +124,10 @@ jobs: !startsWith(github.head_ref, 'backport/docs/') uses: ./.github/workflows/test-go.yml with: - total-runners: 16 + # The regular Go tests use an extra runner to execute the + # binary-dependent tests. We isolate them there so that the + # other tests aren't slowed down waiting for a binary build. + total-runners: 17 go-arch: amd64 go-build-tags: '${{ needs.setup.outputs.go-build-tags }},deadlock' runs-on: ${{ needs.setup.outputs.compute-larger }} diff --git a/.github/workflows/test-go.yml b/.github/workflows/test-go.yml index 8328da3cfa43..e98bd1d35be3 100644 --- a/.github/workflows/test-go.yml +++ b/.github/workflows/test-go.yml @@ -60,83 +60,6 @@ jobs: INDEX_LIST="$(seq 1 ${{ inputs.total-runners }})" INDEX_JSON="$(jq --null-input --compact-output '. |= [inputs]' <<< "${INDEX_LIST}")" echo "indexes=${INDEX_JSON}" >> "${GITHUB_OUTPUT}" - build-vault: - permissions: - id-token: write # Note: this permission is explicitly required for Vault auth - contents: read - runs-on: ${{ fromJSON(inputs.runs-on) }} - name: Build Vault dev binary - steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - if: | - ! contains(inputs.extra-flags, '-race') && - ! contains(inputs.go-build-tags, 'fips') && - github.repository != 'hashicorp/vault-enterprise' - - uses: actions/setup-go@d0a58c1c4d2b25278816e339b944508c875f3613 - if: | - ! contains(inputs.extra-flags, '-race') && - ! contains(inputs.go-build-tags, 'fips') && - github.repository != 'hashicorp/vault-enterprise' - with: - go-version-file: ./.go-version - cache: true - - name: Authenticate to Vault - id: vault-auth - if: github.repository == 'hashicorp/vault-enterprise' - run: vault-auth - - name: Fetch Secrets - id: secrets - if: github.repository == 'hashicorp/vault-enterprise' - uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e - with: - url: ${{ steps.vault-auth.outputs.addr }} - caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }} - token: ${{ steps.vault-auth.outputs.token }} - secrets: | - kv/data/github/${{ github.repository }}/datadog-ci DATADOG_API_KEY; - kv/data/github/${{ github.repository }}/github-token username-and-token | github-token; - kv/data/github/${{ github.repository }}/license license_1 | VAULT_LICENSE_CI; - kv/data/github/${{ github.repository }}/license license_2 | VAULT_LICENSE_2; - kv/data/github/${{ github.repository }}/hcp-link HCP_API_ADDRESS; - kv/data/github/${{ github.repository }}/hcp-link HCP_AUTH_URL; - kv/data/github/${{ github.repository }}/hcp-link HCP_CLIENT_ID; - kv/data/github/${{ github.repository }}/hcp-link HCP_CLIENT_SECRET; - kv/data/github/${{ github.repository }}/hcp-link HCP_RESOURCE_ID; - - id: setup-git-private - name: Setup Git configuration (private) - if: github.repository == 'hashicorp/vault-enterprise' - run: | - git config --global url."https://${{ steps.secrets.outputs.github-token }}@github.com".insteadOf https://github.com - - id: setup-git-public - name: Setup Git configuration (public) - if: github.repository != 'hashicorp/vault-enterprise' - run: | - git config --global url."https://${{ secrets.ELEVATED_GITHUB_TOKEN}}@github.com".insteadOf https://github.com - - id: go-mod-download - if: | - ! contains(inputs.extra-flags, '-race') && - ! contains(inputs.go-build-tags, 'fips') && - github.repository != 'hashicorp/vault-enterprise' - env: - GOPRIVATE: github.com/hashicorp/* - run: time go mod download -x - - id: build - if: | - ! contains(inputs.extra-flags, '-race') && - ! contains(inputs.go-build-tags, 'fips') && - github.repository != 'hashicorp/vault-enterprise' - env: - GOPRIVATE: github.com/hashicorp/* - run: time make ci-bootstrap dev - - name: Save dev binary - if: | - ! contains(inputs.extra-flags, '-race') && - ! contains(inputs.go-build-tags, 'fips') && - github.repository != 'hashicorp/vault-enterprise' - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce - with: - name: vault-dev - path: bin/vault test-go: permissions: id-token: write # Note: this permission is explicitly required for Vault auth @@ -144,7 +67,6 @@ jobs: name: "${{ matrix.runner-index }}" needs: - runner-indexes - - build-vault runs-on: ${{ fromJSON(inputs.runs-on) }} strategy: fail-fast: false @@ -195,21 +117,16 @@ jobs: if: github.repository != 'hashicorp/vault-enterprise' run: | git config --global url."https://${{ secrets.ELEVATED_GITHUB_TOKEN}}@github.com".insteadOf https://github.com - - name: Retrieve vault dev binary - if: | - ! contains(inputs.extra-flags, '-race') && - ! contains(inputs.go-build-tags, 'fips') && - github.repository != 'hashicorp/vault-enterprise' - uses: actions/download-artifact@e9ef242655d12993efdcda9058dee2db83a2cb9b - with: - name: vault-dev - path: bin - - name: Make vault dev binary executable - if: | - ! contains(inputs.extra-flags, '-race') && - ! contains(inputs.go-build-tags, 'fips') && - github.repository != 'hashicorp/vault-enterprise' - run: chmod a+x bin/vault + - id: go-mod-download + if: matrix.runner-index > 16 + env: + GOPRIVATE: github.com/hashicorp/* + run: time go mod download -x + - id: build + if: matrix.runner-index > 16 + env: + GOPRIVATE: github.com/hashicorp/* + run: time make ci-bootstrap dev - id: run-go-tests name: Run Go tests timeout-minutes: ${{ fromJSON(env.TIMEOUT_IN_MINUTES) }} diff --git a/builtin/logical/pkiext/acme_test.go b/builtin/logical/pkiext/pkiext_binary/acme_test.go similarity index 98% rename from builtin/logical/pkiext/acme_test.go rename to builtin/logical/pkiext/pkiext_binary/acme_test.go index e31331fe54f5..31786250ca5e 100644 --- a/builtin/logical/pkiext/acme_test.go +++ b/builtin/logical/pkiext/pkiext_binary/acme_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package pkiext +package pkiext_binary import ( "context" @@ -10,6 +10,7 @@ import ( "testing" "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/builtin/logical/pkiext" hDocker "github.com/hashicorp/vault/sdk/helper/docker" "github.com/hashicorp/vault/sdk/helper/testcluster" tcDocker "github.com/hashicorp/vault/sdk/helper/testcluster/docker" @@ -21,8 +22,8 @@ func CheckCertBot(t *testing.T, vaultNetwork string, vaultNodeID string, directo t.Logf(s) } - logStdout := &LogConsumerWriter{logConsumer} - logStderr := &LogConsumerWriter{logConsumer} + logStdout := &pkiext.LogConsumerWriter{logConsumer} + logStderr := &pkiext.LogConsumerWriter{logConsumer} t.Logf("creating on network: %v", vaultNetwork) runner, err := hDocker.NewServiceRunner(hDocker.RunOptions{ diff --git a/builtin/logical/pkiext/test_helpers.go b/builtin/logical/pkiext/test_helpers.go index a9d59bd93310..38bbdfe112b5 100644 --- a/builtin/logical/pkiext/test_helpers.go +++ b/builtin/logical/pkiext/test_helpers.go @@ -70,7 +70,7 @@ func parseKey(t *testing.T, pemKey string) crypto.Signer { } type LogConsumerWriter struct { - consumer func(string) + Consumer func(string) } func (l LogConsumerWriter) Write(p []byte) (n int, err error) { @@ -80,7 +80,7 @@ func (l LogConsumerWriter) Write(p []byte) (n int, err error) { scanner := bufio.NewScanner(bytes.NewReader(p)) scanner.Buffer(make([]byte, 64*1024), bufio.MaxScanTokenSize) for scanner.Scan() { - l.consumer(scanner.Text()) + l.Consumer(scanner.Text()) } return len(p), nil } diff --git a/vault/external_tests/pprof/pprof.go b/vault/external_tests/pprof/pprof.go new file mode 100644 index 000000000000..9f13fbcfb95a --- /dev/null +++ b/vault/external_tests/pprof/pprof.go @@ -0,0 +1,140 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package pprof + +import ( + "context" + "encoding/json" + "io/ioutil" + "net/http" + "testing" + + "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/sdk/helper/testcluster" + "github.com/stretchr/testify/require" + "golang.org/x/net/http2" +) + +func SysPprof_Test(t *testing.T, cluster testcluster.VaultCluster) { + nodes := cluster.Nodes() + client := nodes[0].APIClient() + + transport := cleanhttp.DefaultPooledTransport() + transport.TLSClientConfig = nodes[0].TLSConfig() + if err := http2.ConfigureTransport(transport); err != nil { + t.Fatal(err) + } + httpClient := &http.Client{ + Transport: transport, + } + + cases := []struct { + name string + path string + seconds string + }{ + { + "index", + "/v1/sys/pprof/", + "", + }, + { + "cmdline", + "/v1/sys/pprof/cmdline", + "", + }, + { + "goroutine", + "/v1/sys/pprof/goroutine", + "", + }, + { + "heap", + "/v1/sys/pprof/heap", + "", + }, + { + "profile", + "/v1/sys/pprof/profile", + "1", + }, + { + "symbol", + "/v1/sys/pprof/symbol", + "", + }, + { + "trace", + "/v1/sys/pprof/trace", + "1", + }, + } + + pprofRequest := func(t *testing.T, path string, seconds string) { + req := client.NewRequest("GET", path) + if seconds != "" { + req.Params.Set("seconds", seconds) + } + httpReq, err := req.ToHTTP() + if err != nil { + t.Fatal(err) + } + resp, err := httpClient.Do(httpReq) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + httpRespBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + + httpResp := make(map[string]interface{}) + + // Skip this error check since some endpoints return binary blobs, we + // only care about the ok check right after as an existence check. + _ = json.Unmarshal(httpRespBody, &httpResp) + + // Make sure that we don't get a error response + if _, ok := httpResp["errors"]; ok { + t.Fatalf("unexpected error response: %v", httpResp["errors"]) + } + + if len(httpRespBody) == 0 { + t.Fatal("no pprof index returned") + } + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + pprofRequest(t, tc.path, tc.seconds) + }) + } +} + +func SysPprof_Standby_Test(t *testing.T, cluster testcluster.VaultCluster) { + pprof := func(client *api.Client) (string, error) { + req := client.NewRequest("GET", "/v1/sys/pprof/cmdline") + resp, err := client.RawRequestWithContext(context.Background(), req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + data, err := ioutil.ReadAll(resp.Body) + return string(data), err + } + + cmdline, err := pprof(cluster.Nodes()[0].APIClient()) + require.Nil(t, err) + require.NotEmpty(t, cmdline) + t.Log(cmdline) + + cmdline, err = pprof(cluster.Nodes()[1].APIClient()) + require.Nil(t, err) + require.NotEmpty(t, cmdline) + t.Log(cmdline) +} diff --git a/vault/external_tests/pprof/pprof_binary/pprof_test.go b/vault/external_tests/pprof/pprof_binary/pprof_test.go new file mode 100644 index 000000000000..a85f0367a0d3 --- /dev/null +++ b/vault/external_tests/pprof/pprof_binary/pprof_test.go @@ -0,0 +1,58 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package pprof_binary + +import ( + "os" + "testing" + + "github.com/hashicorp/vault/sdk/helper/testcluster" + "github.com/hashicorp/vault/vault/external_tests/pprof" +) + +// TestSysPprof_Exec is the same as TestSysPprof, but using a Vault binary +// running as -dev instead of a fake single node TestCluster. There's no +// particular reason why TestSysPprof was chosen to validate that mechanism, +// other than that it was fast and simple. +func TestSysPprof_Exec(t *testing.T) { + t.Parallel() + binary := os.Getenv("VAULT_BINARY") + if binary == "" { + t.Skip("only running exec test when $VAULT_BINARY present") + } + cluster := testcluster.NewTestExecDevCluster(t, &testcluster.ExecDevClusterOptions{ + ClusterOptions: testcluster.ClusterOptions{ + NumCores: 1, + }, + BinaryPath: binary, + BaseListenAddress: "127.0.0.1:8208", + }) + defer cluster.Cleanup() + + pprof.SysPprof_Test(t, cluster) +} + +// TestSysPprof_Standby_Exec is the same as TestSysPprof_Standby, but using a Vault binary +// running as -dev-three-node instead of a fake single node TestCluster. There's +// no particular reason why TestSysPprof was chosen to validate that mechanism, +// other than that it was fast and simple. +func TestSysPprof_Standby_Exec(t *testing.T) { + t.Parallel() + binary := os.Getenv("VAULT_BINARY") + if binary == "" { + t.Skip("only running exec test when $VAULT_BINARY present") + } + cluster := testcluster.NewTestExecDevCluster(t, &testcluster.ExecDevClusterOptions{ + ClusterOptions: testcluster.ClusterOptions{ + VaultNodeConfig: &testcluster.VaultNodeConfig{ + DisablePerformanceStandby: true, + }, + }, + BinaryPath: binary, + BaseListenAddress: "127.0.0.1:8210", + }) + defer cluster.Cleanup() + + pprof.SysPprof_Standby_Test(t, cluster) +} diff --git a/vault/external_tests/pprof/pprof_test.go b/vault/external_tests/pprof/pprof_test.go index 2352d2d7d4f0..a66766e1bb3b 100644 --- a/vault/external_tests/pprof/pprof_test.go +++ b/vault/external_tests/pprof/pprof_test.go @@ -4,23 +4,18 @@ package pprof import ( - "context" "encoding/json" "io/ioutil" "net/http" - "os" "strconv" "strings" "testing" "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/vault/api" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/internalshared/configutil" - "github.com/hashicorp/vault/sdk/helper/testcluster" "github.com/hashicorp/vault/sdk/helper/testhelpers/schema" "github.com/hashicorp/vault/vault" - "github.com/stretchr/testify/require" "golang.org/x/net/http2" ) @@ -35,127 +30,7 @@ func TestSysPprof(t *testing.T) { core := cluster.Cores[0].Core vault.TestWaitActive(t, core) - testSysPprof(t, cluster) -} - -// TestSysPprof_Exec is the same as TestSysPprof, but using a Vault binary -// running as -dev instead of a fake single node TestCluster. There's no -// particular reason why TestSysPprof was chosen to validate that mechanism, -// other than that it was fast and simple. -func TestSysPprof_Exec(t *testing.T) { - t.Parallel() - binary := os.Getenv("VAULT_BINARY") - if binary == "" { - t.Skip("only running exec test when $VAULT_BINARY present") - } - cluster := testcluster.NewTestExecDevCluster(t, &testcluster.ExecDevClusterOptions{ - ClusterOptions: testcluster.ClusterOptions{ - NumCores: 1, - }, - BinaryPath: binary, - BaseListenAddress: "127.0.0.1:8208", - }) - defer cluster.Cleanup() - - testSysPprof(t, cluster) -} - -func testSysPprof(t *testing.T, cluster testcluster.VaultCluster) { - nodes := cluster.Nodes() - client := nodes[0].APIClient() - - transport := cleanhttp.DefaultPooledTransport() - transport.TLSClientConfig = nodes[0].TLSConfig() - if err := http2.ConfigureTransport(transport); err != nil { - t.Fatal(err) - } - httpClient := &http.Client{ - Transport: transport, - } - - cases := []struct { - name string - path string - seconds string - }{ - { - "index", - "/v1/sys/pprof/", - "", - }, - { - "cmdline", - "/v1/sys/pprof/cmdline", - "", - }, - { - "goroutine", - "/v1/sys/pprof/goroutine", - "", - }, - { - "heap", - "/v1/sys/pprof/heap", - "", - }, - { - "profile", - "/v1/sys/pprof/profile", - "1", - }, - { - "symbol", - "/v1/sys/pprof/symbol", - "", - }, - { - "trace", - "/v1/sys/pprof/trace", - "1", - }, - } - - pprofRequest := func(t *testing.T, path string, seconds string) { - req := client.NewRequest("GET", path) - if seconds != "" { - req.Params.Set("seconds", seconds) - } - httpReq, err := req.ToHTTP() - if err != nil { - t.Fatal(err) - } - resp, err := httpClient.Do(httpReq) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - - httpRespBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - - httpResp := make(map[string]interface{}) - - // Skip this error check since some endpoints return binary blobs, we - // only care about the ok check right after as an existence check. - _ = json.Unmarshal(httpRespBody, &httpResp) - - // Make sure that we don't get a error response - if _, ok := httpResp["errors"]; ok { - t.Fatalf("unexpected error response: %v", httpResp["errors"]) - } - - if len(httpRespBody) == 0 { - t.Fatal("no pprof index returned") - } - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - pprofRequest(t, tc.path, tc.seconds) - }) - } + SysPprof_Test(t, cluster) } func TestSysPprof_MaxRequestDuration(t *testing.T) { @@ -228,53 +103,5 @@ func TestSysPprof_Standby(t *testing.T) { }) defer cluster.Cleanup() - testSysPprof_Standby(t, cluster) -} - -// TestSysPprof_Standby_Exec is the same as TestSysPprof_Standby, but using a Vault binary -// running as -dev-three-node instead of a fake single node TestCluster. There's -// no particular reason why TestSysPprof was chosen to validate that mechanism, -// other than that it was fast and simple. -func TestSysPprof_Standby_Exec(t *testing.T) { - t.Parallel() - binary := os.Getenv("VAULT_BINARY") - if binary == "" { - t.Skip("only running exec test when $VAULT_BINARY present") - } - cluster := testcluster.NewTestExecDevCluster(t, &testcluster.ExecDevClusterOptions{ - ClusterOptions: testcluster.ClusterOptions{ - VaultNodeConfig: &testcluster.VaultNodeConfig{ - DisablePerformanceStandby: true, - }, - }, - BinaryPath: binary, - BaseListenAddress: "127.0.0.1:8210", - }) - defer cluster.Cleanup() - - testSysPprof_Standby(t, cluster) -} - -func testSysPprof_Standby(t *testing.T, cluster testcluster.VaultCluster) { - pprof := func(client *api.Client) (string, error) { - req := client.NewRequest("GET", "/v1/sys/pprof/cmdline") - resp, err := client.RawRequestWithContext(context.Background(), req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - data, err := ioutil.ReadAll(resp.Body) - return string(data), err - } - - cmdline, err := pprof(cluster.Nodes()[0].APIClient()) - require.Nil(t, err) - require.NotEmpty(t, cmdline) - t.Log(cmdline) - - cmdline, err = pprof(cluster.Nodes()[1].APIClient()) - require.Nil(t, err) - require.NotEmpty(t, cmdline) - t.Log(cmdline) + SysPprof_Standby_Test(t, cluster) } diff --git a/vault/external_tests/raft/raft.go b/vault/external_tests/raft/raft.go new file mode 100644 index 000000000000..9b58b07dcfaf --- /dev/null +++ b/vault/external_tests/raft/raft.go @@ -0,0 +1,46 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package rafttests + +import ( + "fmt" + "testing" + + "github.com/go-test/deep" + "github.com/hashicorp/vault/sdk/helper/testcluster" +) + +func Raft_Configuration_Test(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{}) + found := make(map[string]struct{}) + for _, s := range servers { + server := s.(map[string]interface{}) + nodeID := server["node_id"].(string) + leader := server["leader"].(bool) + switch nodeID { + case "core-0": + if !leader { + t.Fatalf("expected server to be leader: %#v", server) + } + default: + if leader { + t.Fatalf("expected server to not be leader: %#v", server) + } + } + + found[nodeID] = struct{}{} + } + 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) + } +} diff --git a/vault/external_tests/raft/raft_binary/raft_test.go b/vault/external_tests/raft/raft_binary/raft_test.go new file mode 100644 index 000000000000..53f2313ed01f --- /dev/null +++ b/vault/external_tests/raft/raft_binary/raft_test.go @@ -0,0 +1,46 @@ +package raft_binary + +import ( + "context" + "os" + "testing" + + "github.com/hashicorp/vault/sdk/helper/testcluster" + "github.com/hashicorp/vault/sdk/helper/testcluster/docker" + rafttest "github.com/hashicorp/vault/vault/external_tests/raft" +) + +// 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() + rafttest.Raft_Configuration_Test(t, cluster) + + if err := cluster.AddNode(context.TODO(), opts); err != nil { + t.Fatal(err) + } + rafttest.Raft_Configuration_Test(t, cluster) +} diff --git a/vault/external_tests/raft/raft_test.go b/vault/external_tests/raft/raft_test.go index 2fead68d5d76..c46d1160db89 100644 --- a/vault/external_tests/raft/raft_test.go +++ b/vault/external_tests/raft/raft_test.go @@ -12,14 +12,12 @@ 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" @@ -33,8 +31,6 @@ 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" @@ -442,76 +438,7 @@ func TestRaft_Configuration(t *testing.T) { t.Parallel() cluster, _ := raftCluster(t, nil) defer cluster.Cleanup() - testRaft_Configuration(t, cluster) -} - -// 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) - - 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{}) - found := make(map[string]struct{}) - for _, s := range servers { - server := s.(map[string]interface{}) - nodeID := server["node_id"].(string) - leader := server["leader"].(bool) - switch nodeID { - case "core-0": - if !leader { - t.Fatalf("expected server to be leader: %#v", server) - } - default: - if leader { - t.Fatalf("expected server to not be leader: %#v", server) - } - } - - found[nodeID] = struct{}{} - } - 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) - } + Raft_Configuration_Test(t, cluster) } func TestRaft_ShamirUnseal(t *testing.T) {