From 5b0ff4f058361656fae85b58ad55a43b93750187 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Tue, 11 Oct 2022 14:22:48 -0400 Subject: [PATCH] test(e2e): Port "credential stores deletion check" from bats test to e2e test suite The "can not read deleted $NEW_STORE static store" bats test was demonstrating flaky behavior where it would fail due to the credential store read still returning something valid. This check has been moved to an e2e test in order to add some retry logic to the check. --- .../tests/cli/boundary/credential_stores.bats | 20 ++--- testing/internal/e2e/helpers.go | 5 ++ .../static/static_credential_store_test.go | 88 +++++++++++++++++++ 3 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 testing/internal/e2e/host/static/static_credential_store_test.go diff --git a/internal/tests/cli/boundary/credential_stores.bats b/internal/tests/cli/boundary/credential_stores.bats index a2dbde63a..a58388ade 100644 --- a/internal/tests/cli/boundary/credential_stores.bats +++ b/internal/tests/cli/boundary/credential_stores.bats @@ -13,35 +13,29 @@ export NEW_STORE='test' } @test "boundary/credential-stores: can create $NEW_STORE static store in default project" { - run create_static_credential_store $NEW_STORE $DEFAULT_P_ID + run create_static_credential_store $NEW_STORE $DEFAULT_P_ID echo "$output" - [ "$status" -eq 0 ] + [ "$status" -eq 0 ] } @test "boundary/credential-stores: can not create already created $NEW_STORE static store" { - run create_static_credential_store $NEW_STORE $DEFAULT_P_ID + run create_static_credential_store $NEW_STORE $DEFAULT_P_ID echo "$output" - [ "$status" -eq 1 ] + [ "$status" -eq 1 ] } @test "boundary/credential-stores: can read $NEW_STORE static store" { local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) - run read_credential_store $csid + run read_credential_store $csid echo "$output" - [ "$status" -eq 0 ] + [ "$status" -eq 0 ] } @test "boundary/credential-stores: can delete $NEW_STORE static store" { local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) run delete_credential_store $csid echo "$output" + [ "$status" -eq 0 ] run has_status_code "$output" "204" [ "$status" -eq 0 ] } - -@test "boundary/credential-stores: can not read deleted $NEW_STORE static store" { - local csid=$(credential_store_id $NEW_STORE $DEFAULT_P_ID) - run read_credential_store $csid - echo "$output" - [ "$status" -eq 1 ] -} diff --git a/testing/internal/e2e/helpers.go b/testing/internal/e2e/helpers.go index acc15ced7..a4f885401 100644 --- a/testing/internal/e2e/helpers.go +++ b/testing/internal/e2e/helpers.go @@ -18,6 +18,11 @@ type CommandResult struct { Err error } +// CliError parses the Stderr from running a boundary command +type CliError struct { + Status int `json:"status"` +} + const EnvToCheckSkip = "E2E_PASSWORD_AUTH_METHOD_ID" // RunCommand executes external commands on the system. Returns the results diff --git a/testing/internal/e2e/host/static/static_credential_store_test.go b/testing/internal/e2e/host/static/static_credential_store_test.go new file mode 100644 index 000000000..c00e9d3fe --- /dev/null +++ b/testing/internal/e2e/host/static/static_credential_store_test.go @@ -0,0 +1,88 @@ +package static_test + +import ( + "context" + "encoding/json" + "fmt" + "os" + "testing" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/hashicorp/boundary/api/credentials" + "github.com/hashicorp/boundary/testing/internal/e2e" + "github.com/hashicorp/boundary/testing/internal/e2e/boundary" + "github.com/stretchr/testify/require" +) + +// TestStaticCredentialStoreCli validates various credential-store operations using the cli +func TestStaticCredentialStoreCli(t *testing.T) { + e2e.MaybeSkipTest(t) + c, err := loadConfig() + require.NoError(t, err) + + boundary.AuthenticateCli(t) + newOrgId := boundary.CreateNewOrgCli(t) + newProjectId := boundary.CreateNewProjectCli(t, newOrgId) + newCredentialStoreId := boundary.CreateNewCredentialStoreStaticCli(t, newProjectId) + + // Create ssh key credentials + ctx := context.Background() + output := e2e.RunCommand(ctx, "boundary", "credentials", "create", "ssh-private-key", + "-credential-store-id", newCredentialStoreId, + "-username", c.TargetSshUser, + "-private-key", "file://"+c.TargetSshKeyPath, + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var keyCredentialsResult credentials.CredentialCreateResult + err = json.Unmarshal(output.Stdout, &keyCredentialsResult) + require.NoError(t, err) + keyCredentialsId := keyCredentialsResult.Item.Id + t.Logf("Created SSH Private Key Credentials: %s", keyCredentialsId) + + // Create username/password credentials + os.Setenv("E2E_CREDENTIALS_PASSWORD", "password") + output = e2e.RunCommand(ctx, "boundary", "credentials", "create", "username-password", + "-credential-store-id", newCredentialStoreId, + "-username", c.TargetSshUser, + "-password", "env://E2E_CREDENTIALS_PASSWORD", + "-format", "json", + ) + require.NoError(t, output.Err, string(output.Stderr)) + var pwCredentialsResult credentials.CredentialCreateResult + err = json.Unmarshal(output.Stdout, &pwCredentialsResult) + require.NoError(t, err) + pwCredentialsId := pwCredentialsResult.Item.Id + t.Logf("Created Username/Password Credentials: %s", pwCredentialsId) + + // Delete credential store + output = e2e.RunCommand(ctx, "boundary", "credential-stores", "delete", "-id", newCredentialStoreId) + require.NoError(t, output.Err, string(output.Stderr)) + err = backoff.RetryNotify( + func() error { + output = e2e.RunCommand(ctx, "boundary", "credential-stores", "read", "-id", newCredentialStoreId, "-format", "json") + if output.Err == nil { + return fmt.Errorf("Deleted credential can still be read: '%s'", output.Stdout) + } + + var response e2e.CliError + err = json.Unmarshal(output.Stderr, &response) + require.NoError(t, err) + statusCode := response.Status + if statusCode != 404 { + return backoff.Permanent( + fmt.Errorf("Command did not return expected status code. Expected: 404, Actual: %d", statusCode), + ) + } + + return nil + }, + backoff.WithMaxRetries(backoff.NewConstantBackOff(3*time.Second), 5), + func(err error, td time.Duration) { + t.Logf("%s. Retrying...", err.Error()) + }, + ) + require.NoError(t, err) + t.Logf("Successfully deleted credential store") +}