Skip to content

Commit

Permalink
Write AWS IAM kubeconfig file
Browse files Browse the repository at this point in the history
  • Loading branch information
mitalipaygude committed Feb 15, 2024
1 parent 7543166 commit 78e6089
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 11 deletions.
20 changes: 20 additions & 0 deletions pkg/awsiamauth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,23 @@ func (c RetrierClient) GetClusterCACert(ctx context.Context, cluster *types.Clus

return nil, fmt.Errorf("tls.crt not found in secret [%s]", secretName)
}

// GetAWSIAMKubeconfigSecretValue gets the AWS IAM kubeconfig value for a cluster from a secret.
func (c RetrierClient) GetAWSIAMKubeconfigSecretValue(ctx context.Context, cluster *types.Cluster, clusterName string) ([]byte, error) {
secret := &corev1.Secret{}
secretName := fmt.Sprintf("%s-aws-iam-kubeconfig", clusterName)
err := c.retrier.Retry(
func() error {
return c.client.GetObject(ctx, "secret", secretName, constants.EksaSystemNamespace, cluster.KubeconfigFile, secret)
},
)
if err != nil {
return nil, err
}

if secretValue, ok := secret.Data["value"]; ok {
return secretValue, nil
}

return nil, fmt.Errorf("AWS IAM kubeconfig token not found in secret [%s]", secretName)
}
41 changes: 41 additions & 0 deletions pkg/awsiamauth/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,44 @@ func TestRetrierClientGetClusterCACertNotFound(t *testing.T) {
tt.Expect(cert).To(BeNil())
tt.Expect(err).To(MatchError(ContainSubstring("tls.crt not found in secret [test-cluster-ca]")))
}

func TestRetrierGetAWSIAMKubeconfigSecretValueSuccess(t *testing.T) {
tt := newRetrierTest(t)
tt.c.EXPECT().GetObject(tt.ctx, "secret", "test-cluster-aws-iam-kubeconfig", "eksa-system", tt.cluster.KubeconfigFile, &corev1.Secret{}).Return(errors.New("error in GetObject")).Times(4)
tt.c.EXPECT().
GetObject(tt.ctx, "secret", "test-cluster-aws-iam-kubeconfig", "eksa-system", tt.cluster.KubeconfigFile, &corev1.Secret{}).
DoAndReturn(func(_ context.Context, _, _, _, _ string, obj *corev1.Secret) error {
obj.Data = map[string][]byte{
"value": []byte("val"),
}
return nil
}).Times(1)

kubeconfig, err := tt.r.GetAWSIAMKubeconfigSecretValue(tt.ctx, tt.cluster, "test-cluster")
tt.Expect(kubeconfig).To(Equal([]byte("val")))
tt.Expect(err).To(Succeed(), "retrierClient.GetObject() should succeed after 5 tries")
}

func TestRetrierGetAWSIAMKubeconfigSecretValueError(t *testing.T) {
tt := newRetrierTest(t)
tt.c.EXPECT().GetObject(tt.ctx, "secret", "test-cluster-aws-iam-kubeconfig", "eksa-system", tt.cluster.KubeconfigFile, &corev1.Secret{}).Return(errors.New("error in GetObject")).Times(5)
tt.c.EXPECT().GetObject(tt.ctx, "secret", "test-cluster-aws-iam-kubeconfig", "eksa-system", tt.cluster.KubeconfigFile, &corev1.Secret{}).Return(nil).AnyTimes()

kubeconfig, err := tt.r.GetAWSIAMKubeconfigSecretValue(tt.ctx, tt.cluster, "test-cluster")
tt.Expect(kubeconfig).To(BeNil())
tt.Expect(err).To(MatchError(ContainSubstring("error in GetObject")), "retrierClient.GetObject() should fail after 5 tries")
}

func TestRetrierGetAWSIAMKubeconfigSecretValueTokenError(t *testing.T) {
tt := newRetrierTest(t)
tt.c.EXPECT().GetObject(tt.ctx, "secret", "test-cluster-aws-iam-kubeconfig", "eksa-system", tt.cluster.KubeconfigFile, &corev1.Secret{}).Return(errors.New("error in GetObject")).Times(4)
tt.c.EXPECT().
GetObject(tt.ctx, "secret", "test-cluster-aws-iam-kubeconfig", "eksa-system", tt.cluster.KubeconfigFile, &corev1.Secret{}).
DoAndReturn(func(_ context.Context, _, _, _, _ string, obj *corev1.Secret) error {
return nil
}).Times(1)

kubeconfig, err := tt.r.GetAWSIAMKubeconfigSecretValue(tt.ctx, tt.cluster, "test-cluster")
tt.Expect(kubeconfig).To(BeNil())
tt.Expect(err).To(MatchError(ContainSubstring("AWS IAM kubeconfig token not found in secret")))
}
31 changes: 31 additions & 0 deletions pkg/awsiamauth/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type KubernetesClient interface {
Apply(ctx context.Context, cluster *types.Cluster, data []byte) error
GetAPIServerURL(ctx context.Context, cluster *types.Cluster) (string, error)
GetClusterCACert(ctx context.Context, cluster *types.Cluster, clusterName string) ([]byte, error)
GetAWSIAMKubeconfigSecretValue(ctx context.Context, cluster *types.Cluster, clusterName string) ([]byte, error)
}

// Installer provides the necessary behavior for installing the AWS IAM Authenticator.
Expand Down Expand Up @@ -154,3 +155,33 @@ func (i *Installer) GenerateKubeconfig(

return nil
}

// GenerateManagementAWSIAMKubeconfig generates the AWS IAM auth kubeconfig.
func (i *Installer) GenerateManagementAWSIAMKubeconfig(
ctx context.Context,
cluster *types.Cluster,
) error {
fileName := fmt.Sprintf("%s-aws.kubeconfig", cluster.Name)

decodedKubeconfigSecretValue, err := i.k8s.GetAWSIAMKubeconfigSecretValue(
ctx,
cluster,
cluster.Name,
)
if err != nil {
return fmt.Errorf("generating aws-iam-authenticator kubeconfig: %v", err)
}

writtenFile, err := i.writer.Write(
fileName,
decodedKubeconfigSecretValue,
filewriter.PersistentFile,
filewriter.Permission0600,
)
if err != nil {
return fmt.Errorf("writing aws-iam-authenticator kubeconfig to %s: %v", writtenFile, err)
}

logger.V(3).Info("Generated aws-iam-authenticator kubeconfig", "kubeconfig", writtenFile)
return nil
}
37 changes: 37 additions & 0 deletions pkg/awsiamauth/installer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,40 @@ func TestUpgradeAWSIAMAuth(t *testing.T) {
}
test.AssertContentToFile(t, string(manifest), "testdata/UpgradeAWSIAMAuth-manifest.yaml")
}

func TestGenerateManagementAWSIAMKubeconfig(t *testing.T) {
ctrl := gomock.NewController(t)
certs := cryptomocks.NewMockCertificateGenerator(ctrl)
clusterID := uuid.MustParse("36db102f-9e1e-4ca4-8300-271d30b14161")

k8s := NewMockKubernetesClient(ctrl)
k8s.EXPECT().GetAWSIAMKubeconfigSecretValue(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("kubeconfig"), nil)

writer := filewritermock.NewMockFileWriter(ctrl)
writer.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()).Return("kubeconfig", nil)

installer := awsiamauth.NewInstaller(certs, clusterID, k8s, writer)

err := installer.GenerateManagementAWSIAMKubeconfig(context.Background(), &types.Cluster{})
if err != nil {
t.Fatal(err)
}
}

func TestGenerateManagementAWSIAMKubeconfigError(t *testing.T) {
ctrl := gomock.NewController(t)
certs := cryptomocks.NewMockCertificateGenerator(ctrl)
clusterID := uuid.MustParse("36db102f-9e1e-4ca4-8300-271d30b14161")

k8s := NewMockKubernetesClient(ctrl)
k8s.EXPECT().GetAWSIAMKubeconfigSecretValue(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte{}, errors.New("test"))

writer := filewritermock.NewMockFileWriter(ctrl)

installer := awsiamauth.NewInstaller(certs, clusterID, k8s, writer)

err := installer.GenerateManagementAWSIAMKubeconfig(context.Background(), &types.Cluster{})
if err == nil {
t.Fatal(err)
}
}
15 changes: 15 additions & 0 deletions pkg/awsiamauth/mock_test.go

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

7 changes: 4 additions & 3 deletions pkg/clustermanager/cluster_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ type AwsIamAuth interface {
InstallAWSIAMAuth(ctx context.Context, management, workload *types.Cluster, spec *cluster.Spec) error
UpgradeAWSIAMAuth(ctx context.Context, cluster *types.Cluster, spec *cluster.Spec) error
GenerateKubeconfig(ctx context.Context, management, workload *types.Cluster, spec *cluster.Spec) error
GenerateManagementAWSIAMKubeconfig(ctx context.Context, cluster *types.Cluster) error
}

// EKSAComponents allows to manage the eks-a components installation in a cluster.
Expand Down Expand Up @@ -840,9 +841,9 @@ func (c *ClusterManager) InstallAwsIamAuth(ctx context.Context, management, work
return c.awsIamAuth.InstallAWSIAMAuth(ctx, management, workload, spec)
}

// GenerateIamAuthKubeconfig generates a kubeconfig for interacting with the cluster with aws-iam-authenticator client.
func (c *ClusterManager) GenerateIamAuthKubeconfig(ctx context.Context, management, workload *types.Cluster, spec *cluster.Spec) error {
return c.awsIamAuth.GenerateKubeconfig(ctx, management, workload, spec)
// GenerateAWSIAMKubeconfig generates a kubeconfig for interacting with the cluster with aws-iam-authenticator client.
func (c *ClusterManager) GenerateAWSIAMKubeconfig(ctx context.Context, cluster *types.Cluster) error {
return c.awsIamAuth.GenerateManagementAWSIAMKubeconfig(ctx, cluster)
}

func (c *ClusterManager) CreateAwsIamAuthCaSecret(ctx context.Context, managementCluster *types.Cluster, workloadClusterName string) error {
Expand Down
14 changes: 14 additions & 0 deletions pkg/clustermanager/mocks/client_and_networking.go

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

2 changes: 1 addition & 1 deletion pkg/workflows/interfaces/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type ClusterManager interface {
Upgrade(ctx context.Context, cluster *types.Cluster, currentManagementComponents, newManagementComponents *cluster.ManagementComponents, newSpec *cluster.Spec) (*types.ChangeDiff, error)
InstallAwsIamAuth(ctx context.Context, managementCluster, workloadCluster *types.Cluster, clusterSpec *cluster.Spec) error
CreateAwsIamAuthCaSecret(ctx context.Context, bootstrapCluster *types.Cluster, workloadClusterName string) error
GenerateIamAuthKubeconfig(ctx context.Context, management, workload *types.Cluster, spec *cluster.Spec) error
GenerateAWSIAMKubeconfig(ctx context.Context, cluster *types.Cluster) error
DeletePackageResources(ctx context.Context, managementCluster *types.Cluster, clusterName string) error
CreateRegistryCredSecret(ctx context.Context, mgmt *types.Cluster) error
CreateNamespace(ctx context.Context, targetCluster *types.Cluster, namespace string) error
Expand Down
12 changes: 6 additions & 6 deletions pkg/workflows/interfaces/mocks/clients.go

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

23 changes: 23 additions & 0 deletions pkg/workflows/management/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,29 @@ func TestCreateSyncFailure(t *testing.T) {
}
}

func TestCreateAWSIAMFailure(t *testing.T) {
test := newCreateTest(t)
test.expectSetup()
test.expectPreflightValidationsToPass()
test.expectCreateBootstrap()
test.expectCAPIInstall(nil, nil, nil)
test.expectInstallEksaComponentsBootstrap(nil, nil, nil, nil)
test.clusterSpec.AWSIamConfig = &v1alpha1.AWSIamConfig{}

test.clusterManager.EXPECT().CreateNamespace(test.ctx, test.bootstrapCluster, test.clusterSpec.Cluster.Namespace).Return(nil)
test.clusterCreator.EXPECT().CreateSync(test.ctx, test.clusterSpec, test.bootstrapCluster).Return(test.workloadCluster, nil)
test.clusterManager.EXPECT().GenerateAWSIAMKubeconfig(test.ctx, test.workloadCluster).Return(errors.New("test"))

test.clusterManager.EXPECT().SaveLogsManagementCluster(test.ctx, test.clusterSpec, test.bootstrapCluster)
test.clusterManager.EXPECT().SaveLogsWorkloadCluster(test.ctx, test.provider, test.clusterSpec, test.workloadCluster)
test.writer.EXPECT().Write(fmt.Sprintf("%s-checkpoint.yaml", test.clusterSpec.Cluster.Name), gomock.Any())

err := test.run()
if err == nil {
t.Fatalf("Create.Run() expected to return an error %v", err)
}
}

func TestCreateEKSANamespaceFailure(t *testing.T) {
test := newCreateTest(t)
test.expectSetup()
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflows/management/create_workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (s *createWorkloadClusterTask) Run(ctx context.Context, commandContext *tas

if commandContext.ClusterSpec.AWSIamConfig != nil {
logger.Info("Generating the aws iam kubeconfig file")
err = commandContext.ClusterManager.GenerateIamAuthKubeconfig(ctx, commandContext.BootstrapCluster, workloadCluster, commandContext.ClusterSpec)
err = commandContext.ClusterManager.GenerateAWSIAMKubeconfig(ctx, commandContext.WorkloadCluster)
if err != nil {
commandContext.SetError(err)
return &workflows.CollectDiagnosticsTask{}
Expand Down

0 comments on commit 78e6089

Please sign in to comment.