diff --git a/pkg/driver/node/credentialprovider/provider_driver.go b/pkg/driver/node/credentialprovider/provider_driver.go index ccf245af..5f5a170a 100644 --- a/pkg/driver/node/credentialprovider/provider_driver.go +++ b/pkg/driver/node/credentialprovider/provider_driver.go @@ -13,7 +13,8 @@ import ( ) const ( - driverLevelServiceAccountTokenName = "token" + webIdentityServiceAccountTokenName = "token" + eksPodIdentityServiceAccountTokenName = "eks-pod-identity-token" ) // provideFromDriver provides driver-level AWS credentials. @@ -26,6 +27,7 @@ func (c *Provider) provideFromDriver(provideCtx ProvideContext) (envprovider.Env accessKeyID := os.Getenv(envprovider.EnvAccessKeyID) secretAccessKey := os.Getenv(envprovider.EnvSecretAccessKey) if accessKeyID != "" && secretAccessKey != "" { + klog.V(4).Infof("Providing credentials from driver with Long-term AWS credentials") sessionToken := os.Getenv(envprovider.EnvSessionToken) longTermCredsEnv, err := provideLongTermCredentialsFromDriver(provideCtx, accessKeyID, secretAccessKey, sessionToken) if err != nil { @@ -37,6 +39,7 @@ func (c *Provider) provideFromDriver(provideCtx ProvideContext) (envprovider.Env } else { // Profile provider // TODO: This is not officially supported and won't work by default with containerization. + klog.V(4).Infof("Providing credentials from driver with Profile provider") configFile := os.Getenv(envprovider.EnvConfigFile) sharedCredentialsFile := os.Getenv(envprovider.EnvSharedCredentialsFile) if configFile != "" && sharedCredentialsFile != "" { @@ -45,10 +48,11 @@ func (c *Provider) provideFromDriver(provideCtx ProvideContext) (envprovider.Env } } - // STS Web Identity provider + // STS Web Identity provider (IRSA) webIdentityTokenFile := os.Getenv(envprovider.EnvWebIdentityTokenFile) roleARN := os.Getenv(envprovider.EnvRoleARN) if webIdentityTokenFile != "" && roleARN != "" { + klog.V(4).Infof("Providing credentials from driver with STS Web Identity provider (IRSA)") stsWebIdentityCredsEnv, err := provideStsWebIdentityCredentialsFromDriver(provideCtx) if err != nil { klog.V(4).ErrorS(err, "credentialprovider: Failed to provide STS Web Identity credentials from driver") @@ -58,6 +62,19 @@ func (c *Provider) provideFromDriver(provideCtx ProvideContext) (envprovider.Env env.Merge(stsWebIdentityCredsEnv) } + // Container credential provider (EKS Pod Identity) + containerAuthorizationTokenFile := os.Getenv(envprovider.EnvContainerAuthorizationTokenFile) + containerCredentialsFullURI := os.Getenv(envprovider.EnvContainerCredentialsFullURI) + if util.UsePodMounter() && containerAuthorizationTokenFile != "" && containerCredentialsFullURI != "" { + klog.V(4).Infof("Providing credentials from driver with Container credential provider (EKS Pod Identity)") + containerCredsEnv, err := provideContainerCredentialsFromDriver(provideCtx, containerAuthorizationTokenFile, containerCredentialsFullURI) + if err != nil { + klog.V(4).ErrorS(err, "credentialprovider: Failed to provide container credentials from driver") + return nil, err + } + env.Merge(containerCredsEnv) + } + return env, nil } @@ -74,7 +91,7 @@ func (c *Provider) cleanupFromDriver(cleanupCtx CleanupContext) error { // It basically copies driver's injected service account token to [provideCtx.WritePath]. func provideStsWebIdentityCredentialsFromDriver(provideCtx ProvideContext) (envprovider.Environment, error) { driverServiceAccountTokenFile := os.Getenv(envprovider.EnvWebIdentityTokenFile) - tokenFile := filepath.Join(provideCtx.WritePath, driverLevelServiceAccountTokenName) + tokenFile := filepath.Join(provideCtx.WritePath, webIdentityServiceAccountTokenName) err := util.ReplaceFile(tokenFile, driverServiceAccountTokenFile, CredentialFilePerm) if err != nil { return nil, fmt.Errorf("credentialprovider: sts-web-identity: failed to copy driver's service account token: %w", err) @@ -82,7 +99,22 @@ func provideStsWebIdentityCredentialsFromDriver(provideCtx ProvideContext) (envp return envprovider.Environment{ envprovider.EnvRoleARN: os.Getenv(envprovider.EnvRoleARN), - envprovider.EnvWebIdentityTokenFile: filepath.Join(provideCtx.EnvPath, driverLevelServiceAccountTokenName), + envprovider.EnvWebIdentityTokenFile: filepath.Join(provideCtx.EnvPath, webIdentityServiceAccountTokenName), + }, nil +} + +// provideContainerCredentialsFromDriver provides Container credentials from the driver's service account. +// It basically copies driver's injected service account token to [provideCtx.WritePath]. +func provideContainerCredentialsFromDriver(provideCtx ProvideContext, containerAuthorizationTokenFile string, containerCredentialsFullURI string) (envprovider.Environment, error) { + tokenFile := filepath.Join(provideCtx.WritePath, eksPodIdentityServiceAccountTokenName) + err := util.ReplaceFile(tokenFile, containerAuthorizationTokenFile, CredentialFilePerm) + if err != nil { + return nil, fmt.Errorf("credentialprovider: container: failed to copy driver's service account token: %w", err) + } + + return envprovider.Environment{ + envprovider.EnvContainerAuthorizationTokenFile: filepath.Join(provideCtx.EnvPath, eksPodIdentityServiceAccountTokenName), + envprovider.EnvContainerCredentialsFullURI: containerCredentialsFullURI, }, nil } diff --git a/pkg/driver/node/credentialprovider/provider_test.go b/pkg/driver/node/credentialprovider/provider_test.go index 9bf0057c..74614e05 100644 --- a/pkg/driver/node/credentialprovider/provider_test.go +++ b/pkg/driver/node/credentialprovider/provider_test.go @@ -28,12 +28,16 @@ const testSessionToken = "test-session-token" const testRoleARN = "arn:aws:iam::111122223333:role/pod-a-role" const testWebIdentityToken = "test-web-identity-token" +const testContainerAuthorizationToken = "test-container-authorization-token" +const testContainerCredentialsFullURI = "http://169.254.170.23/v1/credentials" + const testPodID = "2a17db00-0bf3-4052-9b3f-6c89dcee5d79" const testVolumeID = "test-vol" const testProfilePrefix = testPodID + "-" + testVolumeID + "-" const testPodLevelServiceAccountToken = testPodID + "-" + testVolumeID + ".token" -const testDriverLevelServiceAccountToken = "token" +const testWebIdentityServiceAccountToken = "token" +const testEKSPodIdentityServiceAccountToken = "eks-pod-identity-token" const testPodServiceAccount = "test-sa" const testPodNamespace = "test-ns" @@ -98,9 +102,27 @@ func TestProvidingDriverLevelCredentials(t *testing.T) { assert.Equals(t, credentialprovider.AuthenticationSourceDriver, source) assert.Equals(t, envprovider.Environment{ "AWS_ROLE_ARN": testRoleARN, - "AWS_WEB_IDENTITY_TOKEN_FILE": filepath.Join(testEnvPath, testDriverLevelServiceAccountToken), + "AWS_WEB_IDENTITY_TOKEN_FILE": filepath.Join(testEnvPath, testWebIdentityServiceAccountToken), + }, env) + assertWebIdentityTokenFile(t, filepath.Join(writePath, testWebIdentityServiceAccountToken)) + } + }) + + t.Run("only container credentials", func(t *testing.T) { + for _, authSource := range authenticationSourceVariants { + setEnvForContainerCredentials(t) + + writePath := t.TempDir() + provideCtx := provideCtx(t, writePath, authSource) + + env, source, err := provider.Provide(context.Background(), provideCtx) + assert.NoError(t, err) + assert.Equals(t, credentialprovider.AuthenticationSourceDriver, source) + assert.Equals(t, envprovider.Environment{ + "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE": filepath.Join(testEnvPath, testEKSPodIdentityServiceAccountToken), + "AWS_CONTAINER_CREDENTIALS_FULL_URI": testContainerCredentialsFullURI, }, env) - assertWebIdentityTokenFile(t, filepath.Join(writePath, testDriverLevelServiceAccountToken)) + assertContainerTokenFile(t, filepath.Join(writePath, testEKSPodIdentityServiceAccountToken)) } }) @@ -148,10 +170,55 @@ func TestProvidingDriverLevelCredentials(t *testing.T) { "AWS_CONFIG_FILE": "/test-env/" + testProfilePrefix + "s3-csi-config", "AWS_SHARED_CREDENTIALS_FILE": "/test-env/" + testProfilePrefix + "s3-csi-credentials", "AWS_ROLE_ARN": testRoleARN, - "AWS_WEB_IDENTITY_TOKEN_FILE": filepath.Join(testEnvPath, testDriverLevelServiceAccountToken), + "AWS_WEB_IDENTITY_TOKEN_FILE": filepath.Join(testEnvPath, testWebIdentityServiceAccountToken), + }, env) + assertLongTermCredentials(t, writePath) + assertWebIdentityTokenFile(t, filepath.Join(writePath, testWebIdentityServiceAccountToken)) + } + }) + + t.Run("long-term and container credentials", func(t *testing.T) { + for _, authSource := range authenticationSourceVariants { + setEnvForLongTermCredentials(t) + setEnvForContainerCredentials(t) + + writePath := t.TempDir() + provideCtx := provideCtx(t, writePath, authSource) + + env, source, err := provider.Provide(context.Background(), provideCtx) + assert.NoError(t, err) + assert.Equals(t, credentialprovider.AuthenticationSourceDriver, source) + assert.Equals(t, envprovider.Environment{ + "AWS_PROFILE": testProfilePrefix + "s3-csi", + "AWS_CONFIG_FILE": "/test-env/" + testProfilePrefix + "s3-csi-config", + "AWS_SHARED_CREDENTIALS_FILE": "/test-env/" + testProfilePrefix + "s3-csi-credentials", + "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE": filepath.Join(testEnvPath, testEKSPodIdentityServiceAccountToken), + "AWS_CONTAINER_CREDENTIALS_FULL_URI": testContainerCredentialsFullURI, }, env) assertLongTermCredentials(t, writePath) - assertWebIdentityTokenFile(t, filepath.Join(writePath, testDriverLevelServiceAccountToken)) + assertContainerTokenFile(t, filepath.Join(writePath, testEKSPodIdentityServiceAccountToken)) + } + }) + + t.Run("sts web identity credentials and containter credentials", func(t *testing.T) { + for _, authSource := range authenticationSourceVariants { + setEnvForContainerCredentials(t) + setEnvForStsWebIdentityCredentials(t) + + writePath := t.TempDir() + provideCtx := provideCtx(t, writePath, authSource) + + env, source, err := provider.Provide(context.Background(), provideCtx) + assert.NoError(t, err) + assert.Equals(t, credentialprovider.AuthenticationSourceDriver, source) + assert.Equals(t, envprovider.Environment{ + "AWS_ROLE_ARN": testRoleARN, + "AWS_WEB_IDENTITY_TOKEN_FILE": filepath.Join(testEnvPath, testWebIdentityServiceAccountToken), + "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE": filepath.Join(testEnvPath, testEKSPodIdentityServiceAccountToken), + "AWS_CONTAINER_CREDENTIALS_FULL_URI": testContainerCredentialsFullURI, + }, env) + assertContainerTokenFile(t, filepath.Join(writePath, testEKSPodIdentityServiceAccountToken)) + assertWebIdentityTokenFile(t, filepath.Join(writePath, testWebIdentityServiceAccountToken)) } }) @@ -215,6 +282,31 @@ func TestProvidingDriverLevelCredentials(t *testing.T) { assert.Equals(t, envprovider.Environment{}, env) }) + t.Run("incomplete container credentials", func(t *testing.T) { + // Only set container credentials full URI without token file + t.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", testContainerCredentialsFullURI) + + provider := credentialprovider.New(nil, dummyRegionProvider) + + provideCtx := provideCtx(t, t.TempDir(), credentialprovider.AuthenticationSourceDriver) + + env, source, err := provider.Provide(context.Background(), provideCtx) + assert.NoError(t, err) + assert.Equals(t, credentialprovider.AuthenticationSourceDriver, source) + assert.Equals(t, envprovider.Environment{}, env) + + // Only set token file without role ARN + tokenPath := filepath.Join(t.TempDir(), "token") + assert.NoError(t, os.WriteFile(tokenPath, []byte(testContainerAuthorizationToken), 0600)) + t.Setenv("AWS_ROLE_ARN", "") + t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", tokenPath) + + env, source, err = provider.Provide(context.Background(), provideCtx) + assert.NoError(t, err) + assert.Equals(t, credentialprovider.AuthenticationSourceDriver, source) + assert.Equals(t, envprovider.Environment{}, env) + }) + t.Run("no credentials", func(t *testing.T) { for _, authSource := range authenticationSourceVariants { writePath := t.TempDir() @@ -817,6 +909,16 @@ func TestCleanup(t *testing.T) { //-- Utilities for tests +func provideCtx(t *testing.T, writePath string, authSource string) credentialprovider.ProvideContext { + return credentialprovider.ProvideContext{ + AuthenticationSource: authSource, + WritePath: writePath, + EnvPath: testEnvPath, + PodID: testPodID, + VolumeID: testVolumeID, + } +} + func setEnvForLongTermCredentials(t *testing.T) { t.Setenv("AWS_ACCESS_KEY_ID", testAccessKeyID) t.Setenv("AWS_SECRET_ACCESS_KEY", testSecretAccessKey) @@ -848,6 +950,18 @@ func setEnvForStsWebIdentityCredentials(t *testing.T) { t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", tokenPath) } +func setEnvForContainerCredentials(t *testing.T) { + t.Helper() + + t.Setenv("MOUNTER_KIND", "pod") + + tokenPath := filepath.Join(t.TempDir(), "token") + assert.NoError(t, os.WriteFile(tokenPath, []byte(testContainerAuthorizationToken), 0600)) + + t.Setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", tokenPath) + t.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", testContainerCredentialsFullURI) +} + func assertWebIdentityTokenFile(t *testing.T, path string) { t.Helper() @@ -856,6 +970,14 @@ func assertWebIdentityTokenFile(t *testing.T, path string) { assert.Equals(t, []byte(testWebIdentityToken), got) } +func assertContainerTokenFile(t *testing.T, path string) { + t.Helper() + + got, err := os.ReadFile(path) + assert.NoError(t, err) + assert.Equals(t, []byte(testContainerAuthorizationToken), got) +} + type tokens = map[string]struct { Token string `json:"token"` ExpirationTimestamp time.Time diff --git a/pkg/driver/node/envprovider/provider.go b/pkg/driver/node/envprovider/provider.go index ced9be34..42dc8349 100644 --- a/pkg/driver/node/envprovider/provider.go +++ b/pkg/driver/node/envprovider/provider.go @@ -9,20 +9,22 @@ import ( ) const ( - EnvRegion = "AWS_REGION" - EnvDefaultRegion = "AWS_DEFAULT_REGION" - EnvSTSRegionalEndpoints = "AWS_STS_REGIONAL_ENDPOINTS" - EnvMaxAttempts = "AWS_MAX_ATTEMPTS" - EnvProfile = "AWS_PROFILE" - EnvConfigFile = "AWS_CONFIG_FILE" - EnvSharedCredentialsFile = "AWS_SHARED_CREDENTIALS_FILE" - EnvRoleARN = "AWS_ROLE_ARN" - EnvWebIdentityTokenFile = "AWS_WEB_IDENTITY_TOKEN_FILE" - EnvEC2MetadataDisabled = "AWS_EC2_METADATA_DISABLED" - EnvAccessKeyID = "AWS_ACCESS_KEY_ID" - EnvSecretAccessKey = "AWS_SECRET_ACCESS_KEY" - EnvSessionToken = "AWS_SESSION_TOKEN" - EnvMountpointCacheKey = "UNSTABLE_MOUNTPOINT_CACHE_KEY" + EnvRegion = "AWS_REGION" + EnvDefaultRegion = "AWS_DEFAULT_REGION" + EnvSTSRegionalEndpoints = "AWS_STS_REGIONAL_ENDPOINTS" + EnvMaxAttempts = "AWS_MAX_ATTEMPTS" + EnvProfile = "AWS_PROFILE" + EnvConfigFile = "AWS_CONFIG_FILE" + EnvSharedCredentialsFile = "AWS_SHARED_CREDENTIALS_FILE" + EnvRoleARN = "AWS_ROLE_ARN" + EnvWebIdentityTokenFile = "AWS_WEB_IDENTITY_TOKEN_FILE" + EnvContainerAuthorizationTokenFile = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE" + EnvContainerCredentialsFullURI = "AWS_CONTAINER_CREDENTIALS_FULL_URI" + EnvEC2MetadataDisabled = "AWS_EC2_METADATA_DISABLED" + EnvAccessKeyID = "AWS_ACCESS_KEY_ID" + EnvSecretAccessKey = "AWS_SECRET_ACCESS_KEY" + EnvSessionToken = "AWS_SESSION_TOKEN" + EnvMountpointCacheKey = "UNSTABLE_MOUNTPOINT_CACHE_KEY" ) // Key represents an environment variable name. diff --git a/tests/e2e-kubernetes/e2e_test.go b/tests/e2e-kubernetes/e2e_test.go index d584fc6d..ab01d0b9 100644 --- a/tests/e2e-kubernetes/e2e_test.go +++ b/tests/e2e-kubernetes/e2e_test.go @@ -23,6 +23,7 @@ func init() { flag.StringVar(&CommitId, "commit-id", "local", "commit id will be used to name buckets") flag.StringVar(&BucketRegion, "bucket-region", "us-east-1", "region where temporary buckets will be created") + flag.StringVar(&ClusterName, "cluster-name", "", "name of the cluster") flag.StringVar(&BucketPrefix, "bucket-prefix", "local", "prefix for temporary buckets") flag.BoolVar(&Performance, "performance", false, "run performance tests") flag.BoolVar(&IMDSAvailable, "imds-available", false, "indicates whether instance metadata service is available") @@ -31,6 +32,7 @@ func init() { s3client.DefaultRegion = BucketRegion custom_testsuites.DefaultRegion = BucketRegion + custom_testsuites.ClusterName = ClusterName custom_testsuites.IMDSAvailable = IMDSAvailable custom_testsuites.IsPodMounter = IsPodMounter } diff --git a/tests/e2e-kubernetes/go.mod b/tests/e2e-kubernetes/go.mod index 79b7d043..ea3da700 100644 --- a/tests/e2e-kubernetes/go.mod +++ b/tests/e2e-kubernetes/go.mod @@ -3,7 +3,7 @@ module github.com/awslabs/aws-s3-csi-driver/tests/e2e-kubernetes go 1.24 require ( - github.com/aws/aws-sdk-go-v2 v1.30.5 + github.com/aws/aws-sdk-go-v2 v1.36.3 github.com/aws/aws-sdk-go-v2/config v1.27.33 github.com/aws/aws-sdk-go-v2/service/iam v1.34.3 github.com/aws/aws-sdk-go-v2/service/s3 v1.47.3 @@ -29,17 +29,19 @@ require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.32 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 // indirect + github.com/aws/aws-sdk-go-v2/service/eks v1.64.0 + github.com/aws/aws-sdk-go-v2/service/eksauth v1.8.2 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.8 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 // indirect - github.com/aws/smithy-go v1.20.4 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect diff --git a/tests/e2e-kubernetes/go.sum b/tests/e2e-kubernetes/go.sum index 291ba502..31a8de92 100644 --- a/tests/e2e-kubernetes/go.sum +++ b/tests/e2e-kubernetes/go.sum @@ -10,8 +10,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g= -github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 h1:Zx9+31KyB8wQna6SXFWOewlgoY5uGdDAu6PTOEU3OQI= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3/go.mod h1:zxbEJhRdKTH1nqS2qu6UJ7zGe25xaHxZXaC2CvuQFnA= github.com/aws/aws-sdk-go-v2/config v1.27.33 h1:Nof9o/MsmH4oa0s2q9a0k7tMz5x/Yj5k06lDODWz3BU= @@ -20,14 +20,18 @@ github.com/aws/aws-sdk-go-v2/credentials v1.17.32 h1:7Cxhp/BnT2RcGy4VisJ9miUPecY github.com/aws/aws-sdk-go-v2/credentials v1.17.32/go.mod h1:P5/QMF3/DCHbXGEGkdbilXHsyTBX5D3HSwcrSc9p20I= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 h1:pfQ2sqNpMVK6xz2RbqLEL0GH87JOwSxPV2rzm8Zsb74= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13/go.mod h1:NG7RXPUlqfsCLLFfi0+IpKN4sCB9D9fw/qTaSB+xRoU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 h1:abKT+RuM1sdCNZIGIfZpLkvxEX3Rpsto019XG/rkYG8= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8/go.mod h1:Owc4ysUE71JSruVTTa3h4f2pp3E4hlcAtmeNXxDmjj4= +github.com/aws/aws-sdk-go-v2/service/eks v1.64.0 h1:EYeOThTRysemFtC6J6h6b7dNg3jN03QuO5cg92ojIQE= +github.com/aws/aws-sdk-go-v2/service/eks v1.64.0/go.mod h1:v1xXy6ea0PHtWkjFUvAUh6B/5wv7UF909Nru0dOIJDk= +github.com/aws/aws-sdk-go-v2/service/eksauth v1.8.2 h1:LWAfceOidV9NIjIq5eF211lx3MzXTbVv3zEio87KeSk= +github.com/aws/aws-sdk-go-v2/service/eksauth v1.8.2/go.mod h1:3fxBKsxzP0ZPYdfJ9W5vP4tP7va9EWOEl8OUN7ul8D0= github.com/aws/aws-sdk-go-v2/service/iam v1.34.3 h1:p4L/tixJ3JUIxCteMGT6oMlqCbEv/EzSZoVwdiib8sU= github.com/aws/aws-sdk-go-v2/service/iam v1.34.3/go.mod h1:rfOWxxwdecWvSC9C2/8K/foW3Blf+aKnIIPP9kQ2DPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= @@ -46,8 +50,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 h1:/Cfdu0XV3mONYKaOt1Gr0k1K github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7/go.mod h1:bCbAxKDqNvkHxRaIMnyVPXPo+OaPRwvmgzMxbz1VKSA= github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 h1:NKTa1eqZYw8tiHSRGpP0VtTdub/8KNk8sDkNPFaOKDE= github.com/aws/aws-sdk-go-v2/service/sts v1.30.7/go.mod h1:NXi1dIAGteSaRLqYgarlhP/Ij0cFT+qmCwiJqWh/U5o= -github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= -github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/tests/e2e-kubernetes/scripts/run.sh b/tests/e2e-kubernetes/scripts/run.sh index e454c9a0..62b330dd 100755 --- a/tests/e2e-kubernetes/scripts/run.sh +++ b/tests/e2e-kubernetes/scripts/run.sh @@ -223,14 +223,14 @@ elif [[ "${ACTION}" == "install_driver" ]]; then elif [[ "${ACTION}" == "run_tests" ]]; then set +e pushd tests/e2e-kubernetes - KUBECONFIG=${KUBECONFIG} ginkgo -p -vv -timeout 60m -- --bucket-region=${REGION} --commit-id=${TAG} --bucket-prefix=${CLUSTER_NAME} --imds-available=true --pod-mounter=${USE_POD_MOUNTER} + KUBECONFIG=${KUBECONFIG} ginkgo -p -vv -timeout 60m -- --bucket-region=${REGION} --commit-id=${TAG} --bucket-prefix=${CLUSTER_NAME} --imds-available=true --pod-mounter=${USE_POD_MOUNTER} --cluster-name=${CLUSTER_NAME} EXIT_CODE=$? print_cluster_info exit $EXIT_CODE elif [[ "${ACTION}" == "run_perf" ]]; then set +e pushd tests/e2e-kubernetes - KUBECONFIG=${KUBECONFIG} go test -ginkgo.vv --bucket-region=${REGION} --commit-id=${TAG} --bucket-prefix=${CLUSTER_NAME} --performance=true --imds-available=true --pod-mounter=${USE_POD_MOUNTER} + KUBECONFIG=${KUBECONFIG} go test -ginkgo.vv --bucket-region=${REGION} --commit-id=${TAG} --bucket-prefix=${CLUSTER_NAME} --performance=true --imds-available=true --pod-mounter=${USE_POD_MOUNTER} --cluster-name=${CLUSTER_NAME} EXIT_CODE=$? print_cluster_info popd diff --git a/tests/e2e-kubernetes/testdriver.go b/tests/e2e-kubernetes/testdriver.go index c4ef4456..4d99b5d8 100644 --- a/tests/e2e-kubernetes/testdriver.go +++ b/tests/e2e-kubernetes/testdriver.go @@ -16,6 +16,7 @@ import ( var ( CommitId string BucketRegion string // assumed to be the same as k8s cluster's region + ClusterName string BucketPrefix string Performance bool IMDSAvailable bool diff --git a/tests/e2e-kubernetes/testsuites/credentials.go b/tests/e2e-kubernetes/testsuites/credentials.go index d0cb7466..996c202f 100644 --- a/tests/e2e-kubernetes/testsuites/credentials.go +++ b/tests/e2e-kubernetes/testsuites/credentials.go @@ -3,6 +3,7 @@ package custom_testsuites import ( "context" "encoding/json" + goerrors "errors" "fmt" "slices" "strings" @@ -10,6 +11,9 @@ import ( awsarn "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go-v2/aws/retry" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go-v2/service/eks/types" + "github.com/aws/aws-sdk-go-v2/service/eksauth" "github.com/aws/aws-sdk-go-v2/service/iam" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" "github.com/aws/aws-sdk-go-v2/service/sts" @@ -17,6 +21,7 @@ import ( "github.com/google/uuid" . "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" authenticationv1 "k8s.io/api/authentication/v1" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -37,6 +42,7 @@ const ( iamPolicyS3FullAccess = "AmazonS3FullAccess" iamPolicyS3ReadOnlyAccess = "AmazonS3ReadOnlyAccess" iamPolicyS3NoAccess = "AmazonEC2ReadOnlyAccess" // `AmazonEC2ReadOnlyAccess` gives no S3 access + iamPolicyEKSClusterPolicy = "AmazonEKSClusterPolicy" ) const ( @@ -50,12 +56,21 @@ const ( stsAssumeRoleRetryMaxBackoffDelay = 10 * time.Second ) +const ( + eksauthAssumeRoleRetryCode = "AccessDeniedException" + eksauthAssumeRoleRetryMaxAttemps = 0 // This will cause SDK to retry indefinetly, but we do have a timeout on the operation + eksauthAssumeRoleRetryMaxBackoffDelay = 10 * time.Second +) + const serviceAccountTokenAudienceSTS = "sts.amazonaws.com" const roleARNAnnotation = "eks.amazonaws.com/role-arn" const credentialSecretName = "aws-secret" +const serviceAccountTokenAudienceEKS = "pods.eks.amazonaws.com" + // DefaultRegion specifies the STS region explicitly. var DefaultRegion string +var ClusterName string // IMDSAvailable indicates whether the Instance Metadata Service is accessible. // When true, it enables a test that rely on automatic detection of the STS region. @@ -132,7 +147,7 @@ func (t *s3CSICredentialsTestSuite) DefineTests(driver storageframework.TestDriv }) createVolume := func(ctx context.Context) *storageframework.VolumeResource { - vol := createVolumeResourceWithMountOptions(ctx, l.config, pattern, nil) + vol := createVolumeResourceWithMountOptions(ctx, l.config, pattern, []string{"debug", "debug-crt"}) deferCleanup(vol.CleanupResource) return vol @@ -278,6 +293,10 @@ func (t *s3CSICredentialsTestSuite) DefineTests(driver storageframework.TestDriv sa := csiDriverServiceAccount(ctx, f) overrideServiceAccountRole(ctx, f, sa, "") + if IsPodMounter && eksPodIdentityAgentDaemonSetForCluster(ctx, f) != nil { + deletePodIdentityAssociations(ctx, sa) + } + framework.ExpectNoError(deleteCredentialSecret(ctx, f)) // Trigger recreation of our pods to ensure they're not using deleted resources @@ -289,8 +308,13 @@ func (t *s3CSICredentialsTestSuite) DefineTests(driver storageframework.TestDriv policyRoleMapping = map[string]*iamtypes.Role{} ) + var ( + eksPodIdentityAgentDaemonSet *appsv1.DaemonSet + ) + BeforeAll(func(ctx context.Context) { oidcProvider = oidcProviderForCluster(ctx, f) + eksPodIdentityAgentDaemonSet = eksPodIdentityAgentDaemonSetForCluster(ctx, f) var afterAllCleanup []func(context.Context) error @@ -319,7 +343,7 @@ func (t *s3CSICredentialsTestSuite) DefineTests(driver storageframework.TestDriv }) updateCSIDriversServiceAccountRole := func(ctx context.Context, policyName string) { - By("Updating CSI Driver's Service Account Role") + By("Updating CSI Driver's Service Account Role for IRSA") sa := csiDriverServiceAccount(ctx, f) role, removeRole := createRole(ctx, f, assumeRoleWithWebIdentityPolicyDocument(ctx, oidcProvider, sa), policyName) @@ -334,6 +358,23 @@ func (t *s3CSICredentialsTestSuite) DefineTests(driver storageframework.TestDriv killCSIDriverPods(ctx, f) } + updateCSIDriversServiceAccountRoleEKSPodIdentity := func(ctx context.Context, policyName string) { + By("Updating CSI Driver's Service Account Role for EKS Pod Identity") + sa := csiDriverServiceAccount(ctx, f) + + role, removeRole := createRole(ctx, f, eksPodIdentityRoleTrustPolicyDocument(), policyName, iamPolicyEKSClusterPolicy) + deferCleanup(removeRole) + + removeAssociation := createPodIdentityAssociation(ctx, f, sa, *role.Arn) + deferCleanup(removeAssociation) + + pod := csiDriverPod(ctx, f) + waitUntilRoleIsAssumableWithEKS(ctx, f, sa, pod) + + // Trigger recreation of our pods to use the new IAM role + killCSIDriverPods(ctx, f) + } + updateDriverLevelKubernetesSecret := func(ctx context.Context, policyName string) { By("Updating Kubernetes Secret with temporary credentials") @@ -400,6 +441,40 @@ func (t *s3CSICredentialsTestSuite) DefineTests(driver storageframework.TestDriv }) }) + Context("EKS Pod Identity", Ordered, func() { + BeforeAll(func(ctx context.Context) { + if !IsPodMounter { + Skip("Pod Mounter is not enabled, skipping EKS Pod Identity tests") + } + if eksPodIdentityAgentDaemonSet == nil { + Skip("EKS Pod Identity Agent is not configured, skipping EKS Pod Identity tests") + } + }) + + It("should use service account's read-only role", func(ctx context.Context) { + updateCSIDriversServiceAccountRoleEKSPodIdentity(ctx, iamPolicyS3ReadOnlyAccess) + pod := createPodWithVolume(ctx) + expectReadOnly(pod) + }) + + It("should use service account's full access role", func(ctx context.Context) { + updateCSIDriversServiceAccountRoleEKSPodIdentity(ctx, iamPolicyS3FullAccess) + pod := createPodAllowsDelete(ctx) + expectFullAccess(pod) + }) + + It("should use service account's full access role as non-root", func(ctx context.Context) { + updateCSIDriversServiceAccountRoleEKSPodIdentity(ctx, iamPolicyS3FullAccess) + pod := createPodAllowsDeleteNonRoot(ctx) + expectFullAccess(pod) + }) + + It("should fail to mount if service account's role does not allow s3::ListObjectsV2", func(ctx context.Context) { + updateCSIDriversServiceAccountRoleEKSPodIdentity(ctx, iamPolicyS3NoAccess) + expectFailToMount(ctx, "", nil) + }) + }) + Context("Credentials via Kubernetes Secrets", func() { It("should use read-only access aws credentials", func(ctx context.Context) { updateDriverLevelKubernetesSecret(ctx, iamPolicyS3ReadOnlyAccess) @@ -681,6 +756,24 @@ func assumeRoleWithWebIdentityPolicyDocument(ctx context.Context, oidcProvider s return string(buf) } +func eksPodIdentityRoleTrustPolicyDocument() string { + buf, err := json.Marshal(&jsonMap{ + "Version": "2012-10-17", + "Statement": []jsonMap{ + { + "Effect": "Allow", + "Principal": jsonMap{ + "Service": serviceAccountTokenAudienceEKS, + }, + "Action": []string{"sts:AssumeRole", "sts:TagSession"}, + }, + }, + }) + framework.ExpectNoError(err) + + return string(buf) +} + func getARNPartition(arn string) string { parsedArn, err := awsarn.Parse(arn) framework.ExpectNoError(err) @@ -736,7 +829,7 @@ func assumeRole(ctx context.Context, f *framework.Framework, roleArn string) *st framework.Logf("Assuming IAM role %s", roleArn) client := sts.NewFromConfig(awsConfig(ctx)) - return waitUntilRoleIsAssumable(ctx, client.AssumeRole, &sts.AssumeRoleInput{ + return waitUntilRoleIsAssumableSTS(ctx, client.AssumeRole, &sts.AssumeRoleInput{ RoleArn: ptr.To(roleArn), RoleSessionName: ptr.To(f.BaseName), DurationSeconds: ptr.To(int32(stsAssumeRoleCredentialDuration.Seconds())), @@ -746,19 +839,44 @@ func assumeRole(ctx context.Context, f *framework.Framework, roleArn string) *st // waitUntilRoleIsAssumable waits until the given role is assumable. // This is needed because we're creating new roles in our test cases and then trying to assume those roles, // but there is a delay between IAM and STS services and newly created roles/policies does not appear on STS immediately. -func waitUntilRoleIsAssumable[Input any, Output any](ctx context.Context, assumeFunc func(context.Context, *Input, ...func(*sts.Options)) (*Output, error), input *Input) *Output { +func waitUntilRoleIsAssumable[Input any, Output any, O any]( + ctx context.Context, + assumeFunc func(context.Context, *Input, ...func(O)) (*Output, error), + input *Input, + optionsFunc func(O), +) *Output { ctx, cancel := context.WithTimeout(ctx, stsAssumeRoleTimeout) defer cancel() - output, err := assumeFunc(ctx, input, func(o *sts.Options) { + output, err := assumeFunc(ctx, input, optionsFunc) + framework.ExpectNoError(err) + gomega.Expect(output).ToNot(gomega.BeNil()) + + return output +} + +func waitUntilRoleIsAssumableSTS[Input any, Output any]( + ctx context.Context, + assumeFunc func(context.Context, *Input, ...func(*sts.Options)) (*Output, error), + input *Input, +) *Output { + return waitUntilRoleIsAssumable(ctx, assumeFunc, input, func(o *sts.Options) { o.Retryer = retry.AddWithErrorCodes(o.Retryer, stsAssumeRoleRetryCode) o.Retryer = retry.AddWithMaxAttempts(o.Retryer, stsAssumeRoleRetryMaxAttemps) o.Retryer = retry.AddWithMaxBackoffDelay(o.Retryer, stsAssumeRoleRetryMaxBackoffDelay) }) - framework.ExpectNoError(err) - gomega.Expect(output).ToNot(gomega.BeNil()) +} - return output +func waitUntilRoleIsAssumableEKS[Input any, Output any]( + ctx context.Context, + assumeFunc func(context.Context, *Input, ...func(*eksauth.Options)) (*Output, error), + input *Input, +) *Output { + return waitUntilRoleIsAssumable(ctx, assumeFunc, input, func(o *eksauth.Options) { + o.Retryer = retry.AddWithErrorCodes(o.Retryer, eksauthAssumeRoleRetryCode) + o.Retryer = retry.AddWithMaxAttempts(o.Retryer, eksauthAssumeRoleRetryMaxAttemps) + o.Retryer = retry.AddWithMaxBackoffDelay(o.Retryer, eksauthAssumeRoleRetryMaxBackoffDelay) + }) } func waitUntilRoleIsAssumableWithWebIdentity(ctx context.Context, f *framework.Framework, sa *v1.ServiceAccount) { @@ -774,7 +892,7 @@ func waitUntilRoleIsAssumableWithWebIdentity(ctx context.Context, f *framework.F framework.ExpectNoError(err) client := sts.NewFromConfig(awsConfig(ctx)) - waitUntilRoleIsAssumable(ctx, client.AssumeRoleWithWebIdentity, &sts.AssumeRoleWithWebIdentityInput{ + waitUntilRoleIsAssumableSTS(ctx, client.AssumeRoleWithWebIdentity, &sts.AssumeRoleWithWebIdentityInput{ RoleArn: ptr.To(roleARN), RoleSessionName: ptr.To(f.BaseName), WebIdentityToken: ptr.To(serviceAccountToken.Status.Token), @@ -782,6 +900,33 @@ func waitUntilRoleIsAssumableWithWebIdentity(ctx context.Context, f *framework.F }) } +func waitUntilRoleIsAssumableWithEKS(ctx context.Context, f *framework.Framework, sa *v1.ServiceAccount, pod *v1.Pod) { + framework.Logf("Waiting until IAM role for ServiceAccount %s is assumable for EKS Pod Identity", sa.Name) + + saClient := f.ClientSet.CoreV1().ServiceAccounts(sa.Namespace) + serviceAccountToken, err := saClient.CreateToken(ctx, sa.Name, &authenticationv1.TokenRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: pod.Namespace, + }, + Spec: authenticationv1.TokenRequestSpec{ + Audiences: []string{serviceAccountTokenAudienceEKS}, + BoundObjectRef: &authenticationv1.BoundObjectReference{ + Kind: "Pod", + APIVersion: "v1", + Name: pod.Name, + UID: pod.UID, + }, + }, + }, metav1.CreateOptions{}) + framework.ExpectNoError(err) + + client := eksauth.NewFromConfig(awsConfig(ctx)) + waitUntilRoleIsAssumableEKS(ctx, client.AssumeRoleForPodIdentity, &eksauth.AssumeRoleForPodIdentityInput{ + ClusterName: ptr.To(ClusterName), + Token: ptr.To(serviceAccountToken.Status.Token), + }) +} + //-- Credential Secret utils // createCredentialSecret creates a Kubernetes Secret with given AWS credentials with the namespace and @@ -870,6 +1015,36 @@ func overrideServiceAccountRole(ctx context.Context, f *framework.Framework, sa } } +func createPodIdentityAssociation(ctx context.Context, f *framework.Framework, sa *v1.ServiceAccount, roleArn string) func(context.Context) error { + framework.Logf("Creating Pod Identity Association for ServiceAccount %s with role %s", sa.Name, roleArn) + + client := eks.NewFromConfig(awsConfig(ctx)) + + input := &eks.CreatePodIdentityAssociationInput{ + ClusterName: &ClusterName, + Namespace: &sa.Namespace, + RoleArn: &roleArn, + ServiceAccount: &sa.Name, + } + + output, err := client.CreatePodIdentityAssociation(ctx, input) + framework.ExpectNoError(err) + + return func(ctx context.Context) error { + framework.Logf("Deleting Pod Identity Association for ServiceAccount %s", sa.Name) + _, err := client.DeletePodIdentityAssociation(ctx, &eks.DeletePodIdentityAssociationInput{ + AssociationId: output.Association.AssociationId, + ClusterName: &ClusterName, + }) + + var rsf *types.ResourceNotFoundException + if goerrors.As(err, &rsf) { + return nil + } + return err + } +} + //-- OIDC utils // oidcProviderForCluster tries to find configured OpenID Connect (OIDC) provider for the cluster we're testing against. @@ -903,6 +1078,19 @@ func oidcProviderForCluster(ctx context.Context, f *framework.Framework) string return strings.TrimPrefix(issuer, "https://") } +//-- EKS Pod Identity utils + +// eksPodIdentityAgentDaemonSetForCluster tries to find configured EKS Pod Identity Agent for the cluster we're testing against. +func eksPodIdentityAgentDaemonSetForCluster(ctx context.Context, f *framework.Framework) *appsv1.DaemonSet { + eksPodIdentityAgentDaemonSetName := "eks-pod-identity-agent" + client := f.ClientSet.AppsV1().DaemonSets(csiDriverDaemonSetNamespace) + ds, err := client.Get(ctx, eksPodIdentityAgentDaemonSetName, metav1.GetOptions{}) + if err != nil { + return nil + } + return ds +} + //-- Test Driver Context utils type contextKey string diff --git a/tests/e2e-kubernetes/testsuites/util.go b/tests/e2e-kubernetes/testsuites/util.go index 8d851cef..689585ee 100644 --- a/tests/e2e-kubernetes/testsuites/util.go +++ b/tests/e2e-kubernetes/testsuites/util.go @@ -12,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/awslabs/aws-s3-csi-driver/pkg/mountpoint" "github.com/google/uuid" "github.com/onsi/gomega" @@ -244,6 +245,39 @@ func killCSIDriverPods(ctx context.Context, f *framework.Framework) { } } +func deletePodIdentityAssociations(ctx context.Context, sa *v1.ServiceAccount) { + framework.Logf("Deleting Pod Identity Associations of Service Account with name %s", sa.Name) + eksClient := eks.NewFromConfig(awsConfig(ctx)) + + listOutput, listErr := eksClient.ListPodIdentityAssociations(ctx, &eks.ListPodIdentityAssociationsInput{ + ClusterName: &ClusterName, + Namespace: &sa.Namespace, + ServiceAccount: &sa.Name, + }) + framework.ExpectNoError(listErr) + + for _, association := range listOutput.Associations { + _, deleteErr := eksClient.DeletePodIdentityAssociation(ctx, &eks.DeletePodIdentityAssociationInput{ + ClusterName: &ClusterName, + AssociationId: association.AssociationId, + }) + framework.ExpectNoError(deleteErr) + } +} + +func csiDriverPod(ctx context.Context, f *framework.Framework) *v1.Pod { + ds := csiDriverDaemonSet(ctx, f) + client := f.ClientSet.CoreV1().Pods(csiDriverDaemonSetNamespace) + + pods, err := client.List(ctx, metav1.ListOptions{ + LabelSelector: metav1.FormatLabelSelector(ds.Spec.Selector), + }) + framework.ExpectNoError(err) + + pod := pods.Items[0] + return &pod +} + func csiDriverDaemonSet(ctx context.Context, f *framework.Framework) *appsv1.DaemonSet { client := f.ClientSet.AppsV1().DaemonSets(csiDriverDaemonSetNamespace) ds, err := client.Get(ctx, csiDriverDaemonSetName, metav1.GetOptions{})