diff --git a/.changelog/147.txt b/.changelog/147.txt new file mode 100644 index 00000000..879b0f4a --- /dev/null +++ b/.changelog/147.txt @@ -0,0 +1,3 @@ +```release-note:bug +include all secrets from paginated respoonses when invoking `hcp vs run` command +``` diff --git a/internal/commands/vaultsecrets/run/run.go b/internal/commands/vaultsecrets/run/run.go index 14b5836a..9a7847c0 100644 --- a/internal/commands/vaultsecrets/run/run.go +++ b/internal/commands/vaultsecrets/run/run.go @@ -139,12 +139,7 @@ func runRun(opts *RunOpts) (err error) { } func getAllSecretsForEnv(opts *RunOpts) ([]string, error) { - params := preview_secret_service.NewOpenAppSecretsParamsWithContext(opts.Ctx) - params.OrganizationID = opts.Profile.OrganizationID - params.ProjectID = opts.Profile.ProjectID - params.AppName = opts.AppName - - res, err := opts.PreviewClient.OpenAppSecrets(params, nil) + secs, err := fetchPaginatedSecrets(opts) if err != nil { return nil, err } @@ -152,7 +147,7 @@ func getAllSecretsForEnv(opts *RunOpts) ([]string, error) { result := os.Environ() collisions := make(map[string][]*models.Secrets20231128OpenSecret, 0) - for _, secret := range res.Payload.Secrets { + for _, secret := range secs { // we need to append results in case of duplicates we want secrets to override switch { case secret.RotatingVersion != nil: @@ -224,3 +219,28 @@ func setupChildProcess(ctx context.Context, command []string, envVars []string) return cmdCtx } + +func fetchPaginatedSecrets(opts *RunOpts) ([]*models.Secrets20231128OpenSecret, error) { + params := preview_secret_service.NewOpenAppSecretsParamsWithContext(opts.Ctx) + params.OrganizationID = opts.Profile.OrganizationID + params.ProjectID = opts.Profile.ProjectID + params.AppName = opts.AppName + + var secrets []*models.Secrets20231128OpenSecret + for { + resp, err := opts.PreviewClient.OpenAppSecrets(params, nil) + if err != nil { + return nil, fmt.Errorf("failed to open app secrets: %w", err) + } + + secrets = append(secrets, resp.Payload.Secrets...) + if resp.Payload.Pagination == nil || resp.Payload.Pagination.NextPageToken == "" { + break + } + + next := resp.Payload.Pagination.NextPageToken + params.PaginationNextPageToken = &next + } + + return secrets, nil +} diff --git a/internal/commands/vaultsecrets/run/run_test.go b/internal/commands/vaultsecrets/run/run_test.go index 196909b8..068a1abc 100644 --- a/internal/commands/vaultsecrets/run/run_test.go +++ b/internal/commands/vaultsecrets/run/run_test.go @@ -104,19 +104,17 @@ func TestRunRun(t *testing.T) { RespErr bool ErrMsg string IOErrorContains string - MockCalled bool + PaginatedResp bool }{ { - Name: "Failed: Secret not found", - RespErr: true, - Secrets: nil, - ErrMsg: "[GET /secrets/2023-11-28/organizations/{organization_id}/projects/{project_id}/apps/{app_name}/secrets:open][403]", - MockCalled: true, + Name: "Failed: Secret not found", + RespErr: true, + Secrets: nil, + ErrMsg: "[GET /secrets/2023-11-28/organizations/{organization_id}/projects/{project_id}/apps/{app_name}/secrets:open][403]", }, { - Name: "Success", - RespErr: false, - MockCalled: true, + Name: "Success", + RespErr: false, Secrets: []*preview_models.Secrets20231128OpenSecret{ { Name: "static", @@ -139,7 +137,6 @@ func TestRunRun(t *testing.T) { { Name: "Collide", RespErr: false, - MockCalled: true, ErrMsg: "multiple secrets map to the same environment variable", IOErrorContains: "ERROR: \"static_collision\" [static], \"static\" [rotating] map to the same environment variable \"STATIC_COLLISION\"", Secrets: []*preview_models.Secrets20231128OpenSecret{ @@ -161,6 +158,29 @@ func TestRunRun(t *testing.T) { }, }, }, + { + Name: "Paginated", + PaginatedResp: true, + RespErr: false, + Secrets: []*preview_models.Secrets20231128OpenSecret{ + { + Name: "static_1", + StaticVersion: &preview_models.Secrets20231128OpenSecretStaticVersion{}, + }, + { + Name: "static_2", + StaticVersion: &preview_models.Secrets20231128OpenSecretStaticVersion{}, + }, + { + Name: "static_3", + StaticVersion: &preview_models.Secrets20231128OpenSecretStaticVersion{}, + }, + { + Name: "static_4", + StaticVersion: &preview_models.Secrets20231128OpenSecretStaticVersion{}, + }, + }, + }, } for _, c := range cases { @@ -182,21 +202,51 @@ func TestRunRun(t *testing.T) { Command: []string{"echo \"Testing\""}, } - if c.MockCalled { - if c.RespErr { - vs.EXPECT().OpenAppSecrets(mock.Anything, mock.Anything).Return(nil, errors.New(c.ErrMsg)).Once() - } else { - vs.EXPECT().OpenAppSecrets(&preview_secret_service.OpenAppSecretsParams{ - OrganizationID: testProfile(t).OrganizationID, - ProjectID: testProfile(t).ProjectID, - AppName: testProfile(t).VaultSecrets.AppName, - Context: opts.Ctx, - }, nil).Return(&preview_secret_service.OpenAppSecretsOK{ - Payload: &preview_models.Secrets20231128OpenAppSecretsResponse{ - Secrets: c.Secrets, + if c.RespErr { + vs.EXPECT().OpenAppSecrets(mock.Anything, mock.Anything).Return(nil, errors.New(c.ErrMsg)).Once() + } else if c.PaginatedResp { + paginationNextPageToken := "next_page_token" + + // expect first request to be missing the page token + // provide half the secrets and a NextPageToken + vs.EXPECT().OpenAppSecrets(&preview_secret_service.OpenAppSecretsParams{ + OrganizationID: testProfile(t).OrganizationID, + ProjectID: testProfile(t).ProjectID, + AppName: testProfile(t).VaultSecrets.AppName, + Context: opts.Ctx, + }, mock.Anything).Return(&preview_secret_service.OpenAppSecretsOK{ + Payload: &preview_models.Secrets20231128OpenAppSecretsResponse{ + Secrets: c.Secrets[:len(c.Secrets)/2], + Pagination: &preview_models.CommonPaginationResponse{ + NextPageToken: paginationNextPageToken, }, - }, nil).Once() - } + }, + }, nil).Once() + + // expect second request to have a page token + // provide later half of the secrets + vs.EXPECT().OpenAppSecrets(&preview_secret_service.OpenAppSecretsParams{ + OrganizationID: testProfile(t).OrganizationID, + ProjectID: testProfile(t).ProjectID, + AppName: testProfile(t).VaultSecrets.AppName, + Context: opts.Ctx, + PaginationNextPageToken: &paginationNextPageToken, + }, mock.Anything).Return(&preview_secret_service.OpenAppSecretsOK{ + Payload: &preview_models.Secrets20231128OpenAppSecretsResponse{ + Secrets: c.Secrets[len(c.Secrets)/2:], + }, + }, nil).Once() + } else { + vs.EXPECT().OpenAppSecrets(&preview_secret_service.OpenAppSecretsParams{ + OrganizationID: testProfile(t).OrganizationID, + ProjectID: testProfile(t).ProjectID, + AppName: testProfile(t).VaultSecrets.AppName, + Context: opts.Ctx, + }, nil).Return(&preview_secret_service.OpenAppSecretsOK{ + Payload: &preview_models.Secrets20231128OpenAppSecretsResponse{ + Secrets: c.Secrets, + }, + }, nil).Once() } // Run the command