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 RoleArn option for the driver #2119

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
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
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func main() {
region = md.GetRegion()
}

cloud, err := cloud.NewCloud(region, options.AwsSdkDebugLog, options.UserAgentExtra, options.Batching)
cloud, err := cloud.NewCloud(region, options.AwsSdkDebugLog, options.UserAgentExtra, options.Batching, options.RoleARN)
if err != nil {
klog.ErrorS(err, "failed to create cloud service")
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
Expand Down
1 change: 1 addition & 0 deletions docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ There are a couple of driver options that can be passed as arguments when starti
| modify-volume-request-handler-timeout | 10s | 2s | Timeout for the window in which volume modification calls must be received in order for them to coalesce into a single volume modification call to AWS. If changing this, be aware that the ebs-csi-controller's csi-resizer and volumemodifier containers both have timeouts on the calls they make, if this value exceeds those timeouts it will cause them to always fail and fall into a retry loop, so adjust those values accordingly.
| warn-on-invalid-tag | true | false | To warn on invalid tags, instead of returning an error|
|reserved-volume-attachments | 2 | -1 | Number of volume attachments reserved for system use. Not used when --volume-attach-limit is specified. When -1, the amount of reserved attachments is loaded from instance metadata that captured state at node boot and may include not only system disks but also CSI volumes.|
| role-arn | arn:aws:iam::012345678910:role/ExampleRole | | The Role used to interact with AWS EC2 APIs |
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ module github.com/kubernetes-sigs/aws-ebs-csi-driver
require (
github.com/aws/aws-sdk-go-v2 v1.30.3
github.com/aws/aws-sdk-go-v2/config v1.27.27
github.com/aws/aws-sdk-go-v2/credentials v1.17.27
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11
github.com/aws/aws-sdk-go-v2/service/ec2 v1.171.0
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3
github.com/aws/smithy-go v1.20.3
github.com/awslabs/volume-modifier-for-k8s v0.3.1
github.com/container-storage-interface/spec v1.10.0
Expand Down Expand Up @@ -43,15 +45,13 @@ require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/awslabs/operatorpkg v0.0.0-20240715175203-3fe9588ed7de // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
Expand Down
24 changes: 20 additions & 4 deletions pkg/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import (

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/smithy-go"
"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/batcher"
dm "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/devicemanager"
Expand Down Expand Up @@ -94,6 +96,8 @@ const (
volumeDetachedState = "detached"
volumeAttachedState = "attached"
cacheForgetDelay = 1 * time.Hour

assumeRoleSessionDuration = 1 * time.Hour
)

// AWS provisioning limits.
Expand Down Expand Up @@ -333,12 +337,12 @@ var _ Cloud = &cloud{}

// NewCloud returns a new instance of AWS cloud
// It panics if session is invalid
func NewCloud(region string, awsSdkDebugLog bool, userAgentExtra string, batching bool) (Cloud, error) {
c := newEC2Cloud(region, awsSdkDebugLog, userAgentExtra, batching)
func NewCloud(region string, awsSdkDebugLog bool, userAgentExtra string, batching bool, roleARN string) (Cloud, error) {
c := newEC2Cloud(region, awsSdkDebugLog, userAgentExtra, batching, roleARN)
return c, nil
}

func newEC2Cloud(region string, awsSdkDebugLog bool, userAgentExtra string, batchingEnabled bool) Cloud {
func newEC2Cloud(region string, awsSdkDebugLog bool, userAgentExtra string, batchingEnabled bool, roleARN string) Cloud {
cfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region))
if err != nil {
panic(err)
Expand All @@ -355,7 +359,19 @@ func newEC2Cloud(region string, awsSdkDebugLog bool, userAgentExtra string, batc
os.Setenv("AWS_EXECUTION_ENV", "aws-ebs-csi-driver-"+driverVersion)
}

svc := ec2.NewFromConfig(cfg, func(o *ec2.Options) {
ec2Config := cfg
if roleARN != "" {
creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), roleARN, func(aro *stscreds.AssumeRoleOptions) {
aro.Duration = assumeRoleSessionDuration
})
ec2Config = aws.Config{
Region: cfg.Region,
DefaultsMode: aws.DefaultsModeStandard,
Credentials: aws.NewCredentialsCache(creds),
}
}

svc := ec2.NewFromConfig(ec2Config, func(o *ec2.Options) {
o.APIOptions = append(o.APIOptions,
RecordRequestsMiddleware(),
)
Expand Down
15 changes: 13 additions & 2 deletions pkg/cloud/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,12 @@ func extractVolumeIdentifiers(volumes []types.Volume) (volumeIDs []string, volum
}
return volumeIDs, volumeNames
}
func TestNewCloud(t *testing.T) {

func TestNewCloud(t *testing.T) {
testCases := []struct {
name string
region string
roleARN string
awsSdkDebugLog bool
userAgentExtra string
batchingEnabled bool
Expand All @@ -108,9 +109,14 @@ func TestNewCloud(t *testing.T) {
name: "success: with only region",
region: "us-east-1",
},
{
name: "success: with role & region",
region: "us-east-1",
roleARN: "arn:aws:iam::012345678910:role/ExampleRole",
},
}
for _, tc := range testCases {
ec2Cloud, err := NewCloud(tc.region, tc.awsSdkDebugLog, tc.userAgentExtra, tc.batchingEnabled)
ec2Cloud, err := NewCloud(tc.region, tc.awsSdkDebugLog, tc.userAgentExtra, tc.batchingEnabled, tc.roleARN)
if err != nil {
t.Fatalf("error %v", err)
}
Expand All @@ -121,6 +127,11 @@ func TestNewCloud(t *testing.T) {
} else {
assert.Nil(t, ec2CloudAscloud.bm)
}
if tc.roleARN != "" {
ec2, ok := ec2CloudAscloud.ec2.(*ec2.Client)
assert.True(t, ok)
assert.IsType(t, &aws.CredentialsCache{}, ec2.Options().Credentials)
}
}
}
func TestBatchDescribeVolumes(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions pkg/driver/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ type Options struct {
// If empty, the in-cluster config will be loaded.
Kubeconfig string

//RoleArn is the role driver will use to interact with the AWS EC2 APIs.
RoleARN string

// #### Server options ####

//Endpoint is the endpoint for the CSI driver server
Expand Down Expand Up @@ -91,6 +94,7 @@ type Options struct {

func (o *Options) AddFlags(f *flag.FlagSet) {
f.StringVar(&o.Kubeconfig, "kubeconfig", "", "Absolute path to a kubeconfig file. The default is the emtpy string, which causes the in-cluster config to be used")
f.StringVar(&o.RoleARN, "role-arn", "", "Arn of the role to be used while interacting with EC2 APIs. The default is the empty string, which causes the role provided by the Pod identity or OIDC to be used.")

// Server options
f.StringVar(&o.Endpoint, "endpoint", DefaultCSIEndpoint, "Endpoint for the CSI driver server")
Expand Down
6 changes: 6 additions & 0 deletions pkg/driver/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ func TestAddFlags(t *testing.T) {
if err := f.Set("reserved-volume-attachments", "5"); err != nil {
t.Errorf("error setting reserved-volume-attachments: %v", err)
}
if err := f.Set("role-arn", "arn:aws:iam::012345678910:role/ExampleRole"); err != nil {
t.Errorf("error setting role-arn: %v", err)
}

if o.Endpoint != "custom-endpoint" {
t.Errorf("unexpected Endpoint: got %s, want custom-endpoint", o.Endpoint)
Expand Down Expand Up @@ -107,6 +110,9 @@ func TestAddFlags(t *testing.T) {
if o.ReservedVolumeAttachments != 5 {
t.Errorf("unexpected ReservedVolumeAttachments: got %d, want 5", o.ReservedVolumeAttachments)
}
if o.RoleARN != "arn:aws:iam::012345678910:role/ExampleRole" {
t.Errorf("unexpected role-arn: got %s, want arn:aws:iam::012345678910:role/ExampleRole", o.RoleARN)
}
}

func TestValidateAttachmentLimits(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/dynamic_provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ var _ = Describe("[ebs-csi-e2e] [single-az] Dynamic Provisioning", func() {
availabilityZones := strings.Split(os.Getenv(awsAvailabilityZonesEnv), ",")
availabilityZone := availabilityZones[rand.Intn(len(availabilityZones))]
region := availabilityZone[0 : len(availabilityZone)-1]
cloud, err := awscloud.NewCloud(region, false, "", true)
cloud, err := awscloud.NewCloud(region, false, "", true, "")
if err != nil {
Fail(fmt.Sprintf("could not get NewCloud: %v", err))
}
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/pre_provsioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ var _ = Describe("[ebs-csi-e2e] [single-az] Pre-Provisioned", func() {
Tags: map[string]string{awscloud.VolumeNameTagKey: dummyVolumeName, awscloud.AwsEbsDriverTagKey: "true"},
}
var err error
cloud, err = awscloud.NewCloud(region, false, "", true)
cloud, err = awscloud.NewCloud(region, false, "", true, "")
if err != nil {
Fail(fmt.Sprintf("could not get NewCloud: %v", err))
}
Expand Down Expand Up @@ -261,7 +261,7 @@ var _ = Describe("[ebs-csi-e2e] [single-az] Pre-Provisioned with Multi-Attach",
Tags: map[string]string{awscloud.VolumeNameTagKey: dummyVolumeName, awscloud.AwsEbsDriverTagKey: "true"},
}
var err error
cloud, err = awscloud.NewCloud(region, false, "", true)
cloud, err = awscloud.NewCloud(region, false, "", true, "")
if err != nil {
Fail(fmt.Sprintf("could not get NewCloud: %v", err))
}
Expand Down