* feat: update retry logic for validation call - update validation for service account validation call to use `select` instead of `sleep` - add `validateServiceAccountKeyTimeout` to config. This makes it easier to control the timeout from tests so tests do not have to run for long with the default timeout
281 lines
8.3 KiB
Go
281 lines
8.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package credential
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
)
|
|
|
|
func TestGetCredentialAttributes(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in map[string]any
|
|
expected *CredentialAttributes
|
|
expectedErrContains string
|
|
}{
|
|
{
|
|
name: "missing project",
|
|
in: map[string]any{},
|
|
expectedErrContains: "missing required value \"project_id\"",
|
|
},
|
|
{
|
|
name: "missing zone",
|
|
in: map[string]any{},
|
|
expectedErrContains: "missing required value \"zone\"",
|
|
},
|
|
{
|
|
name: "valid project and zone",
|
|
in: map[string]any{
|
|
ConstProjectId: "test-project",
|
|
ConstZone: "us-central-1",
|
|
},
|
|
expected: &CredentialAttributes{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1",
|
|
DisableCredentialRotation: false,
|
|
},
|
|
},
|
|
{
|
|
name: "with disable_credential_rotation",
|
|
in: map[string]any{
|
|
ConstDisableCredentialRotation: true,
|
|
ConstProjectId: "test-project",
|
|
ConstZone: "us-central-1",
|
|
},
|
|
expected: &CredentialAttributes{
|
|
DisableCredentialRotation: true,
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1",
|
|
},
|
|
},
|
|
{
|
|
name: "with client_email",
|
|
in: map[string]any{
|
|
ConstClientEmail: "test@test.com",
|
|
ConstProjectId: "test-project",
|
|
ConstZone: "us-central-1",
|
|
},
|
|
expected: &CredentialAttributes{
|
|
ClientEmail: "test@test.com",
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1",
|
|
},
|
|
},
|
|
{
|
|
name: "with target_service_account_id",
|
|
in: map[string]any{
|
|
ConstTargetServiceAccountID: "test-target-service-account",
|
|
ConstProjectId: "test-project",
|
|
ConstZone: "us-central-1",
|
|
},
|
|
expected: &CredentialAttributes{
|
|
TargetServiceAccountId: "test-target-service-account",
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
tc := tc
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
input, err := structpb.NewStruct(tc.in)
|
|
require.NoError(err)
|
|
|
|
actual, err := GetCredentialAttributes(input)
|
|
if tc.expectedErrContains != "" {
|
|
require.Error(err)
|
|
require.Contains(err.Error(), tc.expectedErrContains)
|
|
require.Equal(status.Code(err), codes.InvalidArgument)
|
|
return
|
|
}
|
|
|
|
require.NoError(err)
|
|
require.EqualValues(tc.expected.ProjectId, actual.ProjectId)
|
|
require.EqualValues(tc.expected.Zone, actual.Zone)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetCredentialsConfig(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
secrets map[string]any
|
|
attrs *CredentialAttributes
|
|
expected *Config
|
|
expectedErrContains string
|
|
}{
|
|
{
|
|
name: "nil secrets",
|
|
attrs: &CredentialAttributes{
|
|
Zone: "us-central-1a",
|
|
},
|
|
expected: &Config{
|
|
Zone: "us-central-1a",
|
|
Scopes: []string{defaultGCPScope},
|
|
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
|
|
},
|
|
},
|
|
{
|
|
name: "nil attributes",
|
|
attrs: nil,
|
|
expected: &Config{
|
|
Scopes: []string{defaultGCPScope},
|
|
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
|
|
},
|
|
},
|
|
{
|
|
name: "unknown fields in secrets",
|
|
secrets: map[string]any{
|
|
"unknown_field": "value",
|
|
},
|
|
attrs: &CredentialAttributes{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
},
|
|
expectedErrContains: "secrets.unknown_field: unrecognized field",
|
|
},
|
|
{
|
|
name: "valid ignore creds_last_rotated_time",
|
|
secrets: map[string]any{
|
|
ConstPrivateKeyId: "test-private-key-id",
|
|
ConstPrivateKey: "test-private-key",
|
|
ConstCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
|
|
},
|
|
attrs: &CredentialAttributes{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
ClientEmail: "test@test.com",
|
|
},
|
|
expected: &Config{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
ClientEmail: "test@test.com",
|
|
PrivateKey: "test-private-key",
|
|
PrivateKeyId: "test-private-key-id",
|
|
Scopes: []string{defaultGCPScope},
|
|
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
|
|
},
|
|
},
|
|
{
|
|
name: "missing private key with target service account id",
|
|
secrets: map[string]any{
|
|
ConstClientEmail: "test@test.com",
|
|
},
|
|
attrs: &CredentialAttributes{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
TargetServiceAccountId: "test-target-service-account",
|
|
},
|
|
expectedErrContains: "must not be empty when target service account id is set",
|
|
},
|
|
{
|
|
name: "missing client email with target service account id",
|
|
secrets: map[string]any{
|
|
ConstPrivateKey: "private-key",
|
|
},
|
|
attrs: &CredentialAttributes{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
TargetServiceAccountId: "test-target-service-account",
|
|
},
|
|
expectedErrContains: "must not be empty when target service account id is set",
|
|
},
|
|
{
|
|
name: "missing client email with private key",
|
|
secrets: map[string]any{
|
|
ConstPrivateKeyId: "test-private-key-id",
|
|
ConstPrivateKey: "test-private-key",
|
|
},
|
|
attrs: &CredentialAttributes{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
},
|
|
expectedErrContains: "must not be empty when private key is set",
|
|
},
|
|
{
|
|
name: "missing private key with client email",
|
|
secrets: map[string]any{},
|
|
attrs: &CredentialAttributes{
|
|
ClientEmail: "test@test.com",
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
},
|
|
expectedErrContains: "must not be empty when client email is set",
|
|
},
|
|
{
|
|
name: "valid static credentials",
|
|
secrets: map[string]any{
|
|
ConstPrivateKeyId: "test-private-key-id",
|
|
ConstPrivateKey: "test-private-key",
|
|
},
|
|
attrs: &CredentialAttributes{
|
|
ClientEmail: "test@test.com",
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
},
|
|
expected: &Config{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
PrivateKey: "test-private-key",
|
|
PrivateKeyId: "test-private-key-id",
|
|
ClientEmail: "test@test.com",
|
|
Scopes: []string{defaultGCPScope},
|
|
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
|
|
},
|
|
},
|
|
{
|
|
name: "valid dynamic credentials",
|
|
secrets: map[string]any{
|
|
ConstPrivateKeyId: "test-private-key-id",
|
|
ConstPrivateKey: "test-private-key",
|
|
},
|
|
attrs: &CredentialAttributes{
|
|
ClientEmail: "test@test.com",
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
TargetServiceAccountId: "test-target-service-account",
|
|
},
|
|
expected: &Config{
|
|
ProjectId: "test-project",
|
|
Zone: "us-central-1a",
|
|
PrivateKey: "test-private-key",
|
|
PrivateKeyId: "test-private-key-id",
|
|
ClientEmail: "test@test.com",
|
|
TargetServiceAccountId: "test-target-service-account",
|
|
Scopes: []string{defaultGCPScope},
|
|
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
tc := tc
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
secrets, err := structpb.NewStruct(tc.secrets)
|
|
require.NoError(err)
|
|
|
|
actual, err := GetCredentialsConfig(secrets, tc.attrs)
|
|
if tc.expectedErrContains != "" {
|
|
require.Error(err)
|
|
require.Contains(err.Error(), tc.expectedErrContains)
|
|
require.Equal(status.Code(err), codes.InvalidArgument)
|
|
return
|
|
}
|
|
|
|
require.NoError(err)
|
|
require.EqualValues(tc.expected, actual)
|
|
})
|
|
}
|
|
}
|