Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding rad init command changes to support irsa #7761

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 28 additions & 17 deletions pkg/cli/aws/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package aws
import (
"context"

"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
Expand All @@ -28,9 +28,9 @@ import (
// Client is an interface that abstracts `rad init`'s interactions with AWS. This is for testing purposes. This is only exported because mockgen requires it.
type Client interface {
// GetCallerIdentity gets information about the provided credentials.
GetCallerIdentity(ctx context.Context, region string, accessKeyID string, secretAccessKey string) (*sts.GetCallerIdentityOutput, error)
GetCallerIdentity(ctx context.Context, region string) (*sts.GetCallerIdentityOutput, error)
// ListRegions lists the AWS regions available (fetched from EC2.DescribeRegions API).
ListRegions(ctx context.Context, region string, accessKeyID string, secretAccessKey string) (*ec2.DescribeRegionsOutput, error)
ListRegions(ctx context.Context, region string) (*ec2.DescribeRegionsOutput, error)
}

// NewClient returns a new Client.
Expand All @@ -43,13 +43,18 @@ type client struct{}
var _ Client = &client{}

// GetCallerIdentity gets information about the provided credentials.
func (c *client) GetCallerIdentity(ctx context.Context, region string, accessKeyID string, secretAccessKey string) (*sts.GetCallerIdentityOutput, error) {
credentialsProvider := credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, "")
stsClient := sts.New(sts.Options{
Region: region,
Credentials: credentialsProvider,
})
result, err := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})
func (c *client) GetCallerIdentity(ctx context.Context, region string) (*sts.GetCallerIdentityOutput, error) {
// Load the AWS SDK config and credentials
cfg, err := config.LoadDefaultConfig(ctx, config.WithSharedConfigProfile("default"))
if err != nil {
return nil, err
}

// Create an STS client
svc := sts.NewFromConfig(cfg)

// Call GetCallerIdentity
result, err := svc.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})
if err != nil {
return nil, err
}
Expand All @@ -58,13 +63,19 @@ func (c *client) GetCallerIdentity(ctx context.Context, region string, accessKey
}

// ListRegions lists the AWS regions available (fetched from EC2.DescribeRegions API).
func (c *client) ListRegions(ctx context.Context, region string, accessKeyID string, secretAccessKey string) (*ec2.DescribeRegionsOutput, error) {
credentialsProvider := credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, "")
ec2Client := ec2.New(ec2.Options{
Region: region,
Credentials: credentialsProvider,
})
result, err := ec2Client.DescribeRegions(ctx, &ec2.DescribeRegionsInput{})
func (c *client) ListRegions(ctx context.Context, region string) (*ec2.DescribeRegionsOutput, error) {
// Load the AWS SDK config and credentials
cfg, err := config.LoadDefaultConfig(ctx, config.WithSharedConfigProfile("default"))
if err != nil {
return nil, err
}

// Create an EC2 client
svc := ec2.NewFromConfig(cfg)

// Call DescribeRegions
input := &ec2.DescribeRegionsInput{}
result, err := svc.DescribeRegions(ctx, input)
if err != nil {
return nil, err
}
Expand Down
24 changes: 12 additions & 12 deletions pkg/cli/aws/client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 25 additions & 6 deletions pkg/cli/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,41 @@ limitations under the License.

package aws

// AwsCredentialKind - Aws credential kinds supported.
type AwsCredentialKind string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the convention in our repository for Aws? Should it be AWS or do we use both Aws and AWS? I believe we should use AWS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we are using mix of these 2 formats, mostly i see Aws in ucp code and AWS on the cli side. I have updated it to AWS format


const (
// ProviderDisplayName is the text used in display for AWS.
ProviderDisplayName = "AWS"
ProviderDisplayName = "AWS"
AwsCredentialKindAccessKey = "AccessKey"
AwsCredentialKindIRSA = "IRSA"
)

// Provider specifies the properties required to configure AWS provider for cloud resources.
type Provider struct {
// AccessKeyID is the access key id for the AWS account.
AccessKeyID string

// SecretAccessKey is the secret access key for the AWS account.
SecretAccessKey string

// Region is the AWS region to use.
Region string

// AccountID is the AWS account id.
AccountID string

CredentialKind AwsCredentialKind

AccessKey *AccessKeyCredential

IRSA *IRSACredential
}

type AccessKeyCredential struct {
// AccessKeyID is the access key id for the AWS account.
AccessKeyID string

// SecretAccessKey is the secret access key for the AWS account.
SecretAccessKey string
}

type IRSACredential struct {
// RoleARN for AWS IRSA identity
RoleARN string
}
2 changes: 1 addition & 1 deletion pkg/cli/azure/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type Provider struct {
ServicePrincipal *ServicePrincipalCredential
}

// Wor specifies the properties of an Azure service principal
// WorkloadIdentityCredential specifies the properties of an Azure service principal
type WorkloadIdentityCredential struct {
ClientID string
TenantID string
Expand Down
118 changes: 90 additions & 28 deletions pkg/cli/cmd/radinit/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,48 +31,94 @@ const (
QueryRegion = "us-east-1"

selectAWSRegionPrompt = "Select the region you would like to deploy AWS resources to:"
selectAwsCredentialKindPrompt = "Select a credential kind for the AWS credential:"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use AWS and Aws in two lines that follow each other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated it

enterAWSIAMAcessKeyIDPrompt = "Enter the IAM access key id:"
enterAWSRoleARNPrompt = "Enter the role ARN:"
enterAWSRoleARNPlaceholder = "Enter IAM role ARN..."
enterAWSIAMAcessKeyIDPlaceholder = "Enter IAM access key id..."
enterAWSIAMSecretAccessKeyPrompt = "Enter your IAM Secret Access Key:"
enterAWSIAMSecretAccessKeyPlaceholder = "Enter IAM secret access key..."
errNotEmptyTemplate = "%s cannot be empty"

awsAccessKeysCreateInstructionFmt = "\nAWS IAM Access keys (Access key ID and Secret access key) are required to access and create AWS resources.\n\nFor example, you can create one using the following command:\n\033[36maws iam create-access-key\033[0m\n\nFor more information refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html.\n\n"
awsIRSACredentialKind = "IRSA"
awsAccessKeyCredentialKind = "Access Key"
)

func (r *Runner) enterAWSCloudProvider(ctx context.Context) (*aws.Provider, error) {
r.Output.LogInfo(awsAccessKeysCreateInstructionFmt)

accessKeyID, err := r.Prompter.GetTextInput(enterAWSIAMAcessKeyIDPrompt, prompt.TextInputOptions{Placeholder: enterAWSIAMAcessKeyIDPlaceholder})
if err != nil {
return nil, err
}

secretAccessKey, err := r.Prompter.GetTextInput(enterAWSIAMSecretAccessKeyPrompt, prompt.TextInputOptions{Placeholder: enterAWSIAMSecretAccessKeyPlaceholder, EchoMode: textinput.EchoPassword})
func (r *Runner) enterAWSCloudProvider(ctx context.Context, options *initOptions) (*aws.Provider, error) {
credentialKind, err := r.selectAwsCredentialKind()
if err != nil {
return nil, err
}

accountId, err := r.getAccountId(ctx, accessKeyID, secretAccessKey)
if err != nil {
return nil, err
}

region, err := r.selectAWSRegion(ctx, QueryRegion, accessKeyID, secretAccessKey)
if err != nil {
return nil, err
switch credentialKind {
case awsAccessKeyCredentialKind:
r.Output.LogInfo(awsAccessKeysCreateInstructionFmt)

accessKeyID, err := r.Prompter.GetTextInput(enterAWSIAMAcessKeyIDPrompt, prompt.TextInputOptions{Placeholder: enterAWSIAMAcessKeyIDPlaceholder})
if err != nil {
return nil, err
}

secretAccessKey, err := r.Prompter.GetTextInput(enterAWSIAMSecretAccessKeyPrompt, prompt.TextInputOptions{Placeholder: enterAWSIAMSecretAccessKeyPlaceholder, EchoMode: textinput.EchoPassword})
if err != nil {
return nil, err
}

accountId, err := r.getAccountId(ctx)
if err != nil {
return nil, err
}

region, err := r.selectAWSRegion(ctx, QueryRegion)
if err != nil {
return nil, err
}

return &aws.Provider{
AccessKey: &aws.AccessKeyCredential{
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
},
CredentialKind: aws.AwsCredentialKindAccessKey,
AccountID: accountId,
Region: region,
}, nil
case awsIRSACredentialKind:
r.Output.LogInfo(awsAccessKeysCreateInstructionFmt)

roleARN, err := r.Prompter.GetTextInput(enterAWSRoleARNPrompt, prompt.TextInputOptions{Placeholder: enterAWSRoleARNPlaceholder})
if err != nil {
return nil, err
}

accountId, err := r.getAccountId(ctx)
if err != nil {
return nil, err
}

region, err := r.selectAWSRegion(ctx, QueryRegion)
if err != nil {
return nil, err
}

// Set the value for the Helm chart
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need these comments in production, I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

today i see comments in azure provider section as well, we can remove it if its not recommended https://github.com/vishwahiremat/radius/blob/b7484430568763f05fba769b325e6f0471092fcb/pkg/cli/cmd/radinit/azure.go#L127-L128

options.SetValues = append(options.SetValues, "global.aws.irsa.enabled=true")
return &aws.Provider{
AccountID: accountId,
Region: region,
CredentialKind: aws.AwsCredentialKindIRSA,
IRSA: &aws.IRSACredential{
RoleARN: roleARN,
},
}, nil
default:
return nil, clierrors.Message("Invalid Azure credential kind: %s", credentialKind)
}

return &aws.Provider{
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
AccountID: accountId,
Region: region,
}, nil
}

func (r *Runner) getAccountId(ctx context.Context, accessKeyID, secretAccessKey string) (string, error) {
callerIdentityOutput, err := r.awsClient.GetCallerIdentity(ctx, QueryRegion, accessKeyID, secretAccessKey)
func (r *Runner) getAccountId(ctx context.Context) (string, error) {
callerIdentityOutput, err := r.awsClient.GetCallerIdentity(ctx, QueryRegion)
if err != nil {
return "", clierrors.MessageWithCause(err, "AWS credential verification failed.")
}
Expand All @@ -84,8 +130,8 @@ func (r *Runner) getAccountId(ctx context.Context, accessKeyID, secretAccessKey
return *callerIdentityOutput.Account, nil
}

func (r *Runner) selectAWSRegion(ctx context.Context, region, accessKeyID, secretAccessKey string) (string, error) {
listRegionsOutput, err := r.awsClient.ListRegions(ctx, region, accessKeyID, secretAccessKey)
func (r *Runner) selectAWSRegion(ctx context.Context, region string) (string, error) {
listRegionsOutput, err := r.awsClient.ListRegions(ctx, region)
if err != nil {
return "", clierrors.MessageWithCause(err, "Listing AWS regions failed.")
}
Expand All @@ -107,3 +153,19 @@ func (r *Runner) buildAWSRegionsList(listRegionsOutput *ec2.DescribeRegionsOutpu

return regions
}

func (r *Runner) selectAwsCredentialKind() (string, error) {
credentialKinds, err := r.buildAwsCredentialKind()
if err != nil {
return "", err
}

return r.Prompter.GetListInput(credentialKinds, selectAwsCredentialKindPrompt)
}

func (r *Runner) buildAwsCredentialKind() ([]string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This returns a list of available AWS credential kinds. Should we change the name? Also, can this ever return an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated it to remove error from the return type.
and for the name today we follow the similar format in azure provider:

func (r *Runner) buildAzureCredentialKind() ([]string, error) {

return []string{
awsAccessKeyCredentialKind,
awsIRSACredentialKind,
}, nil
}
Loading