From 9b9cf3bb1e3a1587fc87e440719253de30958e3d Mon Sep 17 00:00:00 2001 From: Nithya Subramanian <98416062+nithyatsu@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:00:47 -0700 Subject: [PATCH] implement controllers for AWS IRSA (#7739) # Description Add convertor and controllers for the new IRSA credential type ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Partially Fixes: https://github.com/radius-project/radius/issues/7618 --------- Signed-off-by: nithyatsu Signed-off-by: Nithya Subramanian <98416062+nithyatsu@users.noreply.github.com> --- pkg/recipes/terraform/config/providers/aws.go | 20 ++++-- .../terraform/config/providers/aws_test.go | 49 ++++++++++----- .../aws_credential_conversion.go | 52 +++++++++++++-- .../aws_credential_conversion_test.go | 63 +++++++++++++++++-- ... => credentialresource-aws-accesskey.json} | 0 .../testdata/credentialresource-aws-irsa.json | 16 +++++ ...entialresourcedatamodel-aws-accesskey.json | 34 ++++++++++ ...credentialresourcedatamodel-aws-irsa.json} | 8 ++- pkg/ucp/aws/ucpcredentialprovider.go | 8 +-- pkg/ucp/aws/ucpcredentialprovider_test.go | 10 ++- pkg/ucp/credentials/types.go | 4 ++ pkg/ucp/datamodel/credential.go | 26 ++++++-- .../aws/createorupdateawscredential.go | 6 +- .../aws/deleteawscredential_test.go | 2 +- .../azure/deleteazurecredential_test.go | 2 +- .../AWSCredential_CreateOrUpdate.json | 53 ---------------- .../examples/AWSCredential_Delete.json | 14 ----- .../examples/AWSCredential_Get.json | 27 -------- .../AwsCredentials_CreateOrUpdate.json | 53 ---------------- .../examples/AwsCredentials_Delete.json | 14 ----- .../examples/AwsCredentials_Get.json | 27 -------- .../examples/AwsCredentials_List.json | 30 --------- 22 files changed, 250 insertions(+), 268 deletions(-) rename pkg/ucp/api/v20231001preview/testdata/{credentialresource-aws.json => credentialresource-aws-accesskey.json} (100%) create mode 100644 pkg/ucp/api/v20231001preview/testdata/credentialresource-aws-irsa.json create mode 100644 pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws-accesskey.json rename pkg/ucp/api/v20231001preview/testdata/{credentialresourcedatamodel-aws.json => credentialresourcedatamodel-aws-irsa.json} (82%) delete mode 100644 swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_CreateOrUpdate.json delete mode 100644 swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Delete.json delete mode 100644 swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Get.json delete mode 100644 swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_CreateOrUpdate.json delete mode 100644 swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Delete.json delete mode 100644 swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Get.json delete mode 100644 swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_List.json diff --git a/pkg/recipes/terraform/config/providers/aws.go b/pkg/recipes/terraform/config/providers/aws.go index e00418e733..25ca1fc771 100644 --- a/pkg/recipes/terraform/config/providers/aws.go +++ b/pkg/recipes/terraform/config/providers/aws.go @@ -21,6 +21,8 @@ import ( "errors" "fmt" + ucp_datamodel "github.com/radius-project/radius/pkg/ucp/datamodel" + "github.com/radius-project/radius/pkg/azure/tokencredentials" "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" @@ -118,9 +120,14 @@ func fetchAWSCredentials(ctx context.Context, awsCredentialsProvider credentials return nil, err } - if credentials == nil || credentials.AccessKeyID == "" || credentials.SecretAccessKey == "" { - logger.Info("AWS credentials are not registered, skipping credentials configuration.") - return nil, nil + switch credentials.Kind { + case ucp_datamodel.AWSAccessKeyCredentialKind: + if credentials.AccessKeyCredential == nil || credentials.AccessKeyCredential.AccessKeyID == "" || credentials.AccessKeyCredential.SecretAccessKey == "" { + logger.Info("AWS AccessKey credentials are not registered, skipping credentials configuration.") + return nil, nil + } + case ucp_datamodel.AWSIRSACredentialKind: + return nil, errors.New("AWS IRSA Credential is not supported yet.") } return credentials, nil @@ -132,9 +139,10 @@ func (p *awsProvider) generateProviderConfigMap(credentials *credentials.AWSCred config[awsRegionParam] = region } - if credentials != nil && credentials.AccessKeyID != "" && credentials.SecretAccessKey != "" { - config[awsAccessKeyParam] = credentials.AccessKeyID - config[awsSecretKeyParam] = credentials.SecretAccessKey + if credentials != nil && credentials.AccessKeyCredential != nil && + credentials.AccessKeyCredential.AccessKeyID != "" && credentials.AccessKeyCredential.SecretAccessKey != "" { + config[awsAccessKeyParam] = credentials.AccessKeyCredential.AccessKeyID + config[awsSecretKeyParam] = credentials.AccessKeyCredential.SecretAccessKey } return config diff --git a/pkg/recipes/terraform/config/providers/aws_test.go b/pkg/recipes/terraform/config/providers/aws_test.go index 965f3d7113..63dcbb8911 100644 --- a/pkg/recipes/terraform/config/providers/aws_test.go +++ b/pkg/recipes/terraform/config/providers/aws_test.go @@ -24,7 +24,9 @@ import ( "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/sdk" + ucp_credentials "github.com/radius-project/radius/pkg/ucp/credentials" + ucp_datamodel "github.com/radius-project/radius/pkg/ucp/datamodel" "github.com/radius-project/radius/pkg/ucp/secret" "github.com/radius-project/radius/test/testcontext" "github.com/stretchr/testify/require" @@ -33,8 +35,11 @@ import ( var ( testRegion = "test-region" testAWSCredentials = ucp_credentials.AWSCredential{ - AccessKeyID: "testAccessKey", - SecretAccessKey: "testSecretKey", + Kind: ucp_datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &ucp_datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: "testAccessKey", + SecretAccessKey: "testSecretKey", + }, } ) @@ -45,8 +50,11 @@ type mockAWSCredentialsProvider struct { func newMockAWSCredentialsProvider() *mockAWSCredentialsProvider { return &mockAWSCredentialsProvider{ testCredential: &ucp_credentials.AWSCredential{ - AccessKeyID: testAWSCredentials.AccessKeyID, - SecretAccessKey: testAWSCredentials.SecretAccessKey, + Kind: ucp_datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &ucp_datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: testAWSCredentials.AccessKeyCredential.AccessKeyID, + SecretAccessKey: testAWSCredentials.AccessKeyCredential.SecretAccessKey, + }, }, } } @@ -58,11 +66,11 @@ func (p *mockAWSCredentialsProvider) Fetch(ctx context.Context, planeName, name return nil, &secret.ErrNotFound{} } - if p.testCredential.AccessKeyID == "" && p.testCredential.SecretAccessKey == "" { + if p.testCredential.AccessKeyCredential.AccessKeyID == "" && p.testCredential.AccessKeyCredential.SecretAccessKey == "" { return p.testCredential, nil } - if p.testCredential.AccessKeyID == "" { + if p.testCredential.AccessKeyCredential.AccessKeyID == "" { return nil, errors.New("failed to fetch credential") } @@ -202,8 +210,11 @@ func TestAWSProvider_FetchCredentials(t *testing.T) { desc: "empty values - no error", credentialsProvider: &mockAWSCredentialsProvider{ &ucp_credentials.AWSCredential{ - AccessKeyID: "", - SecretAccessKey: "", + Kind: ucp_datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &ucp_datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: testAWSCredentials.AccessKeyCredential.AccessKeyID, + SecretAccessKey: testAWSCredentials.AccessKeyCredential.SecretAccessKey, + }, }, }, expectedCreds: nil, @@ -213,8 +224,11 @@ func TestAWSProvider_FetchCredentials(t *testing.T) { desc: "fetch credential error", credentialsProvider: &mockAWSCredentialsProvider{ &ucp_credentials.AWSCredential{ - AccessKeyID: "", - SecretAccessKey: testAWSCredentials.SecretAccessKey, + Kind: ucp_datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &ucp_datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: "", + SecretAccessKey: testAWSCredentials.AccessKeyCredential.SecretAccessKey, + }, }, }, expectedCreds: nil, @@ -251,16 +265,16 @@ func TestAWSProvider_generateProviderConfigMap(t *testing.T) { credentials: testAWSCredentials, expectedConfig: map[string]any{ awsRegionParam: testRegion, - awsAccessKeyParam: testAWSCredentials.AccessKeyID, - awsSecretKeyParam: testAWSCredentials.SecretAccessKey, + awsAccessKeyParam: testAWSCredentials.AccessKeyCredential.AccessKeyID, + awsSecretKeyParam: testAWSCredentials.AccessKeyCredential.SecretAccessKey, }, }, { desc: "missing region", credentials: testAWSCredentials, expectedConfig: map[string]any{ - awsAccessKeyParam: testAWSCredentials.AccessKeyID, - awsSecretKeyParam: testAWSCredentials.SecretAccessKey, + awsAccessKeyParam: testAWSCredentials.AccessKeyCredential.AccessKeyID, + awsSecretKeyParam: testAWSCredentials.AccessKeyCredential.SecretAccessKey, }, }, { @@ -273,8 +287,11 @@ func TestAWSProvider_generateProviderConfigMap(t *testing.T) { { desc: "invalid credentials", credentials: ucp_credentials.AWSCredential{ - AccessKeyID: "", - SecretAccessKey: testAWSCredentials.SecretAccessKey, + Kind: ucp_datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &ucp_datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: "", + SecretAccessKey: testAWSCredentials.AccessKeyCredential.SecretAccessKey, + }, }, expectedConfig: map[string]any{}, }, diff --git a/pkg/ucp/api/v20231001preview/aws_credential_conversion.go b/pkg/ucp/api/v20231001preview/aws_credential_conversion.go index 167e0b3129..e4d37656e5 100644 --- a/pkg/ucp/api/v20231001preview/aws_credential_conversion.go +++ b/pkg/ucp/api/v20231001preview/aws_credential_conversion.go @@ -67,7 +67,7 @@ func (cr *AwsCredentialResource) getDataModelCredentialProperties() (*datamodel. switch c := p.Storage.(type) { case *InternalCredentialStorageProperties: if c.Kind == nil { - return nil, &v1.ErrModelConversion{PropertyName: "$.properties", ValidValue: "not nil"} + return nil, &v1.ErrModelConversion{PropertyName: "$.properties.storage.kind", ValidValue: fmt.Sprintf("one of %q", PossibleCredentialStorageKindValues())} } storage = &datamodel.CredentialStorageProperties{ Kind: datamodel.InternalStorageKind, @@ -82,13 +82,47 @@ func (cr *AwsCredentialResource) getDataModelCredentialProperties() (*datamodel. } return &datamodel.AWSCredentialResourceProperties{ - Kind: datamodel.AWSCredentialKind, + Kind: datamodel.AWSAccessKeyCredentialKind, AWSCredential: &datamodel.AWSCredentialProperties{ - AccessKeyID: to.String(p.AccessKeyID), - SecretAccessKey: to.String(p.SecretAccessKey), + Kind: datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: to.String(p.AccessKeyID), + SecretAccessKey: to.String(p.SecretAccessKey), + }, + }, + Storage: storage, + }, nil + case *AwsIRSACredentialProperties: + var storage *datamodel.CredentialStorageProperties + + switch c := p.Storage.(type) { + case *InternalCredentialStorageProperties: + if c.Kind == nil { + return nil, &v1.ErrModelConversion{PropertyName: "$.properties.storage.kind", ValidValue: fmt.Sprintf("one of %q", PossibleCredentialStorageKindValues())} + } + storage = &datamodel.CredentialStorageProperties{ + Kind: datamodel.InternalStorageKind, + InternalCredential: &datamodel.InternalCredentialStorageProperties{ + SecretName: to.String(c.SecretName), + }, + } + case nil: + return nil, &v1.ErrModelConversion{PropertyName: "$.properties.storage", ValidValue: "not nil"} + default: + return nil, &v1.ErrModelConversion{PropertyName: "$.properties.storage.kind", ValidValue: fmt.Sprintf("one of %q", PossibleCredentialStorageKindValues())} + } + + return &datamodel.AWSCredentialResourceProperties{ + Kind: datamodel.AWSIRSACredentialKind, + AWSCredential: &datamodel.AWSCredentialProperties{ + Kind: datamodel.AWSIRSACredentialKind, + IRSACredential: &datamodel.AWSIRSACredentialProperties{ + RoleARN: to.String(p.RoleARN), + }, }, Storage: storage, }, nil + default: return nil, v1.ErrInvalidModelConversion } @@ -120,12 +154,18 @@ func (dst *AwsCredentialResource) ConvertFrom(src v1.DataModelInterface) error { // DO NOT convert any secret values to versioned model. switch dm.Properties.Kind { - case datamodel.AWSCredentialKind: + case datamodel.AWSAccessKeyCredentialKind: dst.Properties = &AwsAccessKeyCredentialProperties{ Kind: to.Ptr(AWSCredentialKind(dm.Properties.Kind)), - AccessKeyID: to.Ptr(dm.Properties.AWSCredential.AccessKeyID), + AccessKeyID: to.Ptr(dm.Properties.AWSCredential.AccessKeyCredential.AccessKeyID), Storage: storage, } + case datamodel.AWSIRSACredentialKind: + dst.Properties = &AwsIRSACredentialProperties{ + Kind: to.Ptr(AWSCredentialKind(dm.Properties.Kind)), + RoleARN: to.Ptr(dm.Properties.AWSCredential.IRSACredential.RoleARN), + Storage: storage, + } default: return v1.ErrInvalidModelConversion } diff --git a/pkg/ucp/api/v20231001preview/aws_credential_conversion_test.go b/pkg/ucp/api/v20231001preview/aws_credential_conversion_test.go index 0f8a4cd65b..1f8af38420 100644 --- a/pkg/ucp/api/v20231001preview/aws_credential_conversion_test.go +++ b/pkg/ucp/api/v20231001preview/aws_credential_conversion_test.go @@ -36,7 +36,7 @@ func TestAWSCredentialConvertVersionedToDataModel(t *testing.T) { err error }{ { - filename: "credentialresource-aws.json", + filename: "credentialresource-aws-accesskey.json", expected: &datamodel.AWSCredential{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -55,8 +55,43 @@ func TestAWSCredentialConvertVersionedToDataModel(t *testing.T) { Properties: &datamodel.AWSCredentialResourceProperties{ Kind: "AccessKey", AWSCredential: &datamodel.AWSCredentialProperties{ - AccessKeyID: "00000000-0000-0000-0000-000000000000", - SecretAccessKey: "00000000-0000-0000-0000-000000000000", + Kind: datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: "00000000-0000-0000-0000-000000000000", + SecretAccessKey: "00000000-0000-0000-0000-000000000000", + }, + }, + Storage: &datamodel.CredentialStorageProperties{ + Kind: datamodel.InternalStorageKind, + InternalCredential: &datamodel.InternalCredentialStorageProperties{}, + }, + }, + }, + }, + { + filename: "credentialresource-aws-irsa.json", + expected: &datamodel.AWSCredential{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + ID: "/planes/aws/aws/providers/System.AWS/credentials/default", + Name: "default", + Type: "System.AWS/credentials", + Location: "west-us-2", + Tags: map[string]string{ + "env": "dev", + }, + }, + InternalMetadata: v1.InternalMetadata{ + UpdatedAPIVersion: Version, + }, + }, + Properties: &datamodel.AWSCredentialResourceProperties{ + Kind: "IRSA", + AWSCredential: &datamodel.AWSCredentialProperties{ + Kind: datamodel.AWSIRSACredentialKind, + IRSACredential: &datamodel.AWSIRSACredentialProperties{ + RoleARN: "arn:aws:iam::000000000000:role/role-name", + }, }, Storage: &datamodel.CredentialStorageProperties{ Kind: datamodel.InternalStorageKind, @@ -113,7 +148,7 @@ func TestAWSCredentialConvertDataModelToVersioned(t *testing.T) { err error }{ { - filename: "credentialresourcedatamodel-aws.json", + filename: "credentialresourcedatamodel-aws-accesskey.json", expected: &AwsCredentialResource{ ID: to.Ptr("/planes/aws/aws/providers/System.AWS/credentials/default"), Name: to.Ptr("default"), @@ -132,6 +167,26 @@ func TestAWSCredentialConvertDataModelToVersioned(t *testing.T) { }, }, }, + { + filename: "credentialresourcedatamodel-aws-irsa.json", + expected: &AwsCredentialResource{ + ID: to.Ptr("/planes/aws/aws/providers/System.AWS/credentials/default"), + Name: to.Ptr("default"), + Type: to.Ptr("System.AWS/credentials"), + Location: to.Ptr("west-us-2"), + Tags: map[string]*string{ + "env": to.Ptr("dev"), + }, + Properties: &AwsIRSACredentialProperties{ + Kind: to.Ptr(AWSCredentialKindIRSA), + RoleARN: to.Ptr("arn:aws:iam::000000000000:role/role-name"), + Storage: &InternalCredentialStorageProperties{ + Kind: to.Ptr(CredentialStorageKindInternal), + SecretName: to.Ptr("aws-awscloud-default"), + }, + }, + }, + }, { filename: "credentialresourcedatamodel-default.json", err: v1.ErrInvalidModelConversion, diff --git a/pkg/ucp/api/v20231001preview/testdata/credentialresource-aws.json b/pkg/ucp/api/v20231001preview/testdata/credentialresource-aws-accesskey.json similarity index 100% rename from pkg/ucp/api/v20231001preview/testdata/credentialresource-aws.json rename to pkg/ucp/api/v20231001preview/testdata/credentialresource-aws-accesskey.json diff --git a/pkg/ucp/api/v20231001preview/testdata/credentialresource-aws-irsa.json b/pkg/ucp/api/v20231001preview/testdata/credentialresource-aws-irsa.json new file mode 100644 index 0000000000..421a43d58b --- /dev/null +++ b/pkg/ucp/api/v20231001preview/testdata/credentialresource-aws-irsa.json @@ -0,0 +1,16 @@ +{ + "id": "/planes/aws/aws/providers/System.AWS/credentials/default", + "name": "default", + "type": "System.AWS/credentials", + "location": "west-us-2", + "tags": { + "env": "dev" + }, + "properties": { + "roleARN": "arn:aws:iam::000000000000:role/role-name", + "kind": "IRSA", + "storage": { + "kind": "Internal" + } + } +} \ No newline at end of file diff --git a/pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws-accesskey.json b/pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws-accesskey.json new file mode 100644 index 0000000000..7239e89467 --- /dev/null +++ b/pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws-accesskey.json @@ -0,0 +1,34 @@ +{ + "id": "/planes/aws/aws/providers/System.AWS/credentials/default", + "name": "default", + "type": "System.AWS/credentials", + "location": "west-us-2", + "systemData": { + "createdBy": "fakeid@live.com", + "createdByType": "User", + "createdAt": "2021-09-24T19:09:54.2403864Z", + "lastModifiedBy": "fakeid@live.com", + "lastModifiedByType": "User", + "lastModifiedAt": "2021-09-24T20:09:54.2403864Z" + }, + "tags": { + "env": "dev" + }, + "properties": { + "namespace": "radius-system", + "kind": "AccessKey", + "awsCredential": { + "kind": "AccessKey", + "accesskey": { + "accessKeyId": "00000000-0000-0000-0000-000000000000", + "secretAccessKey": "00000000-0000-0000-0000-000000000000" + } + }, + "storage": { + "kind": "Internal", + "internalCredential": { + "secretName": "aws-awscloud-default" + } + } + } +} \ No newline at end of file diff --git a/pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws.json b/pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws-irsa.json similarity index 82% rename from pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws.json rename to pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws-irsa.json index 4e30d5777c..efb56f8e8d 100644 --- a/pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws.json +++ b/pkg/ucp/api/v20231001preview/testdata/credentialresourcedatamodel-aws-irsa.json @@ -16,10 +16,12 @@ }, "properties": { "namespace": "radius-system", - "kind": "AccessKey", + "kind": "IRSA", "awsCredential": { - "accessKeyId": "00000000-0000-0000-0000-000000000000", - "secretAccessKey": "00000000-0000-0000-0000-000000000000" + "kind": "IRSA", + "irsa": { + "roleARN": "arn:aws:iam::000000000000:role/role-name" + } }, "storage": { "kind": "Internal", diff --git a/pkg/ucp/aws/ucpcredentialprovider.go b/pkg/ucp/aws/ucpcredentialprovider.go index f72b1de6f7..447e4e57ae 100644 --- a/pkg/ucp/aws/ucpcredentialprovider.go +++ b/pkg/ucp/aws/ucpcredentialprovider.go @@ -73,15 +73,15 @@ func (c *UCPCredentialProvider) Retrieve(ctx context.Context) (aws.Credentials, return aws.Credentials{}, err } - if s.AccessKeyID == "" || s.SecretAccessKey == "" { + if s.AccessKeyCredential == nil || s.AccessKeyCredential.AccessKeyID == "" || s.AccessKeyCredential.SecretAccessKey == "" { return aws.Credentials{}, errors.New("invalid access key info") } - logger.Info(fmt.Sprintf("Retrieved AWS Credential - AccessKeyID: %s", s.AccessKeyID)) + logger.Info(fmt.Sprintf("Retrieved AWS Credential - AccessKeyID: %s", s.AccessKeyCredential.AccessKeyID)) value := aws.Credentials{ - AccessKeyID: s.AccessKeyID, - SecretAccessKey: s.SecretAccessKey, + AccessKeyID: s.AccessKeyCredential.AccessKeyID, + SecretAccessKey: s.AccessKeyCredential.SecretAccessKey, Source: "radiusucp", CanExpire: true, // Enables AWS SDK to fetch (rotate) access keys by calling Retrieve() after Expires. diff --git a/pkg/ucp/aws/ucpcredentialprovider_test.go b/pkg/ucp/aws/ucpcredentialprovider_test.go index bb5e9fb8d4..ff1a4ad7b2 100644 --- a/pkg/ucp/aws/ucpcredentialprovider_test.go +++ b/pkg/ucp/aws/ucpcredentialprovider_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" sdk_cred "github.com/radius-project/radius/pkg/ucp/credentials" + ucp_datamodel "github.com/radius-project/radius/pkg/ucp/datamodel" ) type mockProvider struct { @@ -43,8 +44,11 @@ func (p *mockProvider) Fetch(ctx context.Context, planeName, name string) (*sdk_ func newMockProvider() *mockProvider { return &mockProvider{ fakeCredential: &sdk_cred.AWSCredential{ - AccessKeyID: "fakeid", - SecretAccessKey: "fakesecretkey", + Kind: ucp_datamodel.AWSAccessKeyCredentialKind, + AccessKeyCredential: &ucp_datamodel.AWSAccessKeyCredentialProperties{ + AccessKeyID: "fakeid", + SecretAccessKey: "fakesecretkey", + }, }, } } @@ -58,7 +62,7 @@ func TestRetrieve(t *testing.T) { t.Run("invalid credential", func(t *testing.T) { p := newMockProvider() cp := NewUCPCredentialProvider(p, DefaultExpireDuration) - p.fakeCredential.AccessKeyID = "" + p.fakeCredential.AccessKeyCredential.AccessKeyID = "" _, err := cp.Retrieve(context.TODO()) require.Error(t, err) diff --git a/pkg/ucp/credentials/types.go b/pkg/ucp/credentials/types.go index 8f2fdcb417..d6ccb9f952 100644 --- a/pkg/ucp/credentials/types.go +++ b/pkg/ucp/credentials/types.go @@ -45,6 +45,10 @@ type ( AzureWorkloadIdentityCredential = ucp_dm.AzureWorkloadIdentityCredentialProperties // AWSCredential represents a credential for AWS IAM. AWSCredential = ucp_dm.AWSCredentialProperties + // AWSAccessKeyCredential represents a credential for AWS access key. + AWSAccessKeyCredential = ucp_dm.AWSAccessKeyCredentialProperties + // AWSIRSACredential represents a RoleARN for AWS IRSA. + AWSIRSACredential = ucp_dm.AWSIRSACredentialProperties ) // CredentialProvider is an UCP credential provider interface. diff --git a/pkg/ucp/datamodel/credential.go b/pkg/ucp/datamodel/credential.go index 9e0b426832..630c897f0d 100644 --- a/pkg/ucp/datamodel/credential.go +++ b/pkg/ucp/datamodel/credential.go @@ -25,8 +25,10 @@ const ( AzureServicePrincipalCredentialKind = "ServicePrincipal" // AzureWorkloadIdentityCredentialKind represents ucp credential kind for Azure workload identity credentials. AzureWorkloadIdentityCredentialKind = "WorkloadIdentity" - // AWSCredentialKind represents ucp credential kind for aws credentials. - AWSCredentialKind = "AccessKey" + // AWSAccessKeyCredentialKind represents ucp credential kind for aws access key credentials. + AWSAccessKeyCredentialKind = "AccessKey" + // AWSIRSACredentialKind represents ucp credential kind for aws irsa credentials. + AWSIRSACredentialKind = "IRSA" ) // Credential represents UCP Credential. @@ -100,14 +102,30 @@ type AzureCredentialProperties struct { WorkloadIdentity *AzureWorkloadIdentityCredentialProperties `json:"workloadIdentity,omitempty"` } -// AWSCredentialProperties contains ucp AWS credential properties. -type AWSCredentialProperties struct { +// AWSAccessKeyCredentialProperties contains ucp AWS access key credential properties. +type AWSAccessKeyCredentialProperties struct { // AccessKeyID contains aws access key for iam. AccessKeyID string `json:"accessKeyId"` // SecretAccessKey contains secret access key for iam. SecretAccessKey string `json:"secretAccessKey,omitempty"` } +// AWSIRSACredentialProperties contains ucp AWS IRSA credential properties. +type AWSIRSACredentialProperties struct { + // RoleARN contains aws role arn for irsa. + RoleARN string `json:"roleARN"` +} + +// AWSCredentialProperties contains ucp AWS credential properties. +type AWSCredentialProperties struct { + // Kind is the kind of AWS credential. + Kind string `json:"kind,omitempty"` + // AccessKeyCredential represents the access key credential properties. + AccessKeyCredential *AWSAccessKeyCredentialProperties `json:"accesskey,omitempty"` + // IRSACredential represents the irsa credential properties. + IRSACredential *AWSIRSACredentialProperties `json:"irsa,omitempty"` +} + // CredentialStorageProperties contains ucp credential storage properties. type CredentialStorageProperties struct { // Kind represents ucp credential storage kind. diff --git a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go index edcab9e37f..9fd2ea5433 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go +++ b/pkg/ucp/frontend/controller/credentials/aws/createorupdateawscredential.go @@ -59,7 +59,7 @@ func (c *CreateOrUpdateAWSCredential) Run(ctx context.Context, w http.ResponseWr return nil, err } - if newResource.Properties.Kind != datamodel.AWSCredentialKind { + if newResource.Properties.Kind != datamodel.AWSAccessKeyCredentialKind && newResource.Properties.Kind != datamodel.AWSIRSACredentialKind { return armrpc_rest.NewBadRequestResponse("Invalid Credential Kind"), nil } @@ -84,7 +84,9 @@ func (c *CreateOrUpdateAWSCredential) Run(ctx context.Context, w http.ResponseWr } // Do not save the secret in metadata store. - newResource.Properties.AWSCredential.SecretAccessKey = "" + if newResource.Properties.AWSCredential.Kind == datamodel.AWSAccessKeyCredentialKind { + newResource.Properties.AWSCredential.AccessKeyCredential.SecretAccessKey = "" + } newResource.SetProvisioningState(v1.ProvisioningStateSucceeded) newEtag, err := c.SaveResource(ctx, serviceCtx.ResourceID.String(), newResource, etag) diff --git a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go index d674970c1d..d3b562264e 100644 --- a/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/aws/deleteawscredential_test.go @@ -129,7 +129,7 @@ func setupCredentialMocks(mockStorageClient store.MockStorageClient) { datamodelCredential := datamodel.AWSCredential{ BaseResource: v1.BaseResource{}, Properties: &datamodel.AWSCredentialResourceProperties{ - Kind: datamodel.AWSCredentialKind, + Kind: datamodel.AWSAccessKeyCredentialKind, }, } diff --git a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go index 89ee2fc85b..ff66ff221a 100644 --- a/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go +++ b/pkg/ucp/frontend/controller/credentials/azure/deleteazurecredential_test.go @@ -132,7 +132,7 @@ func setupCredentialMocks(mockStorageClient store.MockStorageClient) { datamodelCredential := datamodel.AzureCredential{ BaseResource: v1.BaseResource{}, Properties: &datamodel.AzureCredentialResourceProperties{ - Kind: datamodel.AWSCredentialKind, + Kind: datamodel.AzureWorkloadIdentityCredentialKind, }, } diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_CreateOrUpdate.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_CreateOrUpdate.json deleted file mode 100644 index cfa9d28cfa..0000000000 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_CreateOrUpdate.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "operationId": "AwsCredentials_CreateOrUpdate", - "title": "Create or update an AWS credential", - "parameters": { - "api-version": "2023-10-01-preview", - "planeType": "aws", - "planeName": "aws", - "credentialName": "default", - "Credential": { - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "accessKeyId": "enterAccessKeyIdHere", - "secretAccessKey": "enterSecretAccessKey", - "storage": { - "kind": "Internal" - } - } - } - }, - "responses": { - "200": { - "body": { - "id": "/planes/AWS/aws/providers/System.AWS/credentials/default", - "name": "default", - "type": "System.AWS/credentials", - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "storage": { - "kind": "Internal", - "secretName": "aws-awscloud-default" - } - } - } - }, - "201": { - "body": { - "id": "/planes/AWS/aws/providers/System.AWS/credentials/default", - "name": "default", - "type": "System.AWS/credentials", - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "storage": { - "kind": "Internal", - "secretName": "aws-awscloud-default" - } - } - } - } - } -} \ No newline at end of file diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Delete.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Delete.json deleted file mode 100644 index 129dbbc139..0000000000 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Delete.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "operationId": "AwsCredentials_Delete", - "title": "Delete an AWS credential", - "parameters": { - "api-version": "2023-10-01-preview", - "planeType": "aws", - "planeName": "awscloud", - "credentialName": "default" - }, - "responses": { - "200": {}, - "204": {} - } -} \ No newline at end of file diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Get.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Get.json deleted file mode 100644 index e22222c8ce..0000000000 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AWSCredential_Get.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "operationId": "AwsCredentials_Get", - "title": "Get an AWS credential", - "parameters": { - "api-version": "2023-10-01-preview", - "planeType": "aws", - "planeName": "awscloud", - "credentialName": "default" - }, - "responses": { - "200": { - "body": { - "id": "/planes/AWS/aws/providers/System.AWS/credentials/default", - "name": "default", - "type": "System.AWS/credentials", - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "storage": { - "kind": "Internal", - "secretName": "aws-awscloud-default" - } - } - } - } - } -} \ No newline at end of file diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_CreateOrUpdate.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_CreateOrUpdate.json deleted file mode 100644 index 2a8b8b8fc9..0000000000 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_CreateOrUpdate.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "operationId": "AwsCredentials_CreateOrUpdate", - "title": "Create or update a AWS credential", - "parameters": { - "api-version": "2023-10-01-preview", - "planeType": "aws", - "planeName": "aws", - "credentialName": "default", - "Credential": { - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "accessKeyId": "enterAccessKeyIdHere", - "secretAccessKey": "enterSecretAccessKey", - "storage": { - "kind": "Internal" - } - } - } - }, - "responses": { - "200": { - "body": { - "id": "/planes/AWS/aws/providers/System.AWS/credentials/default", - "name": "default", - "type": "System.AWS/credentials", - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "storage": { - "kind": "Internal", - "secretName": "aws-awscloud-default" - } - } - } - }, - "201": { - "body": { - "id": "/planes/AWS/aws/providers/System.AWS/credentials/default", - "name": "default", - "type": "System.AWS/credentials", - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "storage": { - "kind": "Internal", - "secretName": "aws-awscloud-default" - } - } - } - } - } -} \ No newline at end of file diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Delete.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Delete.json deleted file mode 100644 index 2e42882a8a..0000000000 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Delete.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "operationId": "AwsCredentials_Delete", - "title": "Delete a AWS credential", - "parameters": { - "api-version": "2023-10-01-preview", - "planeType": "aws", - "planeName": "awscloud", - "credentialName": "default" - }, - "responses": { - "200": {}, - "204": {} - } -} \ No newline at end of file diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Get.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Get.json deleted file mode 100644 index d99d794a5b..0000000000 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_Get.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "operationId": "AwsCredentials_Get", - "title": "Get a AWS credential", - "parameters": { - "api-version": "2023-10-01-preview", - "planeType": "aws", - "planeName": "awscloud", - "credentialName": "default" - }, - "responses": { - "200": { - "body": { - "id": "/planes/AWS/aws/providers/System.AWS/credentials/default", - "name": "default", - "type": "System.AWS/credentials", - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "storage": { - "kind": "Internal", - "secretName": "aws-awscloud-default" - } - } - } - } - } -} \ No newline at end of file diff --git a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_List.json b/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_List.json deleted file mode 100644 index bd11cb7df2..0000000000 --- a/swagger/specification/ucp/resource-manager/UCP/preview/2023-10-01-preview/examples/AwsCredentials_List.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "operationId": "AwsCredentials_List", - "title": "List AWS credentials", - "parameters": { - "api-version": "2023-10-01-preview", - "planeType": "aws", - "planeName": "awscloud" - }, - "responses": { - "200": { - "body": { - "value": [ - { - "id": "/planes/AWS/aws/providers/System.AWS/credentials/default", - "name": "default", - "type": "System.AWS/credentials", - "location": "us-west-2", - "properties": { - "kind": "AccessKey", - "storage": { - "kind": "Internal", - "secretName": "aws-awscloud-default" - } - } - } - ] - } - } - } -} \ No newline at end of file