Skip to content

Commit

Permalink
implement controllers for AWS IRSA (radius-project#7739)
Browse files Browse the repository at this point in the history
# 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: radius-project#7618

---------

Signed-off-by: nithyatsu <nithyasu@microsoft.com>
Signed-off-by: Nithya Subramanian <98416062+nithyatsu@users.noreply.github.com>
Signed-off-by: Reshma Abdul Rahim <reshmarahim.abdul@microsoft.com>
  • Loading branch information
nithyatsu authored and Reshrahim committed Aug 27, 2024
1 parent 7de109f commit 7e64704
Show file tree
Hide file tree
Showing 22 changed files with 250 additions and 268 deletions.
20 changes: 14 additions & 6 deletions pkg/recipes/terraform/config/providers/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
49 changes: 33 additions & 16 deletions pkg/recipes/terraform/config/providers/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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",
},
}
)

Expand All @@ -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,
},
},
}
}
Expand All @@ -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")
}

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
},
},
{
Expand All @@ -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{},
},
Expand Down
52 changes: 46 additions & 6 deletions pkg/ucp/api/v20231001preview/aws_credential_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
63 changes: 59 additions & 4 deletions pkg/ucp/api/v20231001preview/aws_credential_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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,
Expand Down Expand Up @@ -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"),
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit 7e64704

Please sign in to comment.