diff --git a/cmd/eksctl-anywhere/cmd/createcluster.go b/cmd/eksctl-anywhere/cmd/createcluster.go index 5f8735e8944c5..a7398485185ce 100644 --- a/cmd/eksctl-anywhere/cmd/createcluster.go +++ b/cmd/eksctl-anywhere/cmd/createcluster.go @@ -232,6 +232,14 @@ func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) er } createValidations := createvalidations.New(validationOpts) + clusCreator := clustermanager.ClusterCreator{ + Applier: deps.ClusterApplier, + KM: clustermanager.KubeconfigManager{ + Client: deps.UnAuthKubeClient.KubeconfigClient(mgmt.KubeconfigFile), + FS: deps.Writer, + }, + } + if features.UseNewWorkflows().IsActive() { deps, err = factory. WithCNIInstaller(clusterSpec, deps.Provider). @@ -263,9 +271,9 @@ func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) er deps.ClusterManager, deps.GitOpsFlux, deps.Writer, - deps.ClusterApplier, deps.EksdInstaller, deps.PackageInstaller, + clusCreator, ) err = createWorkloadCluster.Run(ctx, clusterSpec, createValidations) diff --git a/pkg/clustermanager/cluster_creator.go b/pkg/clustermanager/cluster_creator.go new file mode 100644 index 0000000000000..3edacb9a5a912 --- /dev/null +++ b/pkg/clustermanager/cluster_creator.go @@ -0,0 +1,48 @@ +package clustermanager + +import ( + "bytes" + "context" + + "github.com/aws/eks-anywhere/pkg/cluster" + "github.com/aws/eks-anywhere/pkg/providers" + "github.com/aws/eks-anywhere/pkg/types" +) + +type ClusterCreator struct { + Applier Applier + KM KubeconfigManager +} + +// CreateSync creates a workload cluster using the EKS-A controller and returns the types.Cluster object for that cluster. +func (cc ClusterCreator) CreateSync(ctx context.Context, spec *cluster.Spec, managementCluster *types.Cluster, provider providers.Provider) (*types.Cluster, error) { + err := cc.Applier.Run(ctx, spec, *managementCluster) + if err != nil { + return nil, err + } + + return cc.getWorkloadCluster(ctx, spec.Cluster.Name, managementCluster, provider) +} + +func (cc ClusterCreator) getWorkloadCluster(ctx context.Context, clusterName string, management *types.Cluster, provider providers.Provider) (*types.Cluster, error) { + workloadCluster := &types.Cluster{ + Name: clusterName, + ExistingManagement: management.ExistingManagement, + } + + // Use a buffer to cache the kubeconfig. + var buf bytes.Buffer + + err := cc.KM.GetKubeconfig(ctx, clusterName, &buf) + if err != nil { + return nil, err + } + + kubeconfigPath, err := cc.KM.WriteKubeconfig(buf.Bytes(), clusterName, provider) + if err != nil { + return nil, err + } + workloadCluster.KubeconfigFile = kubeconfigPath + + return workloadCluster, nil +} diff --git a/pkg/clustermanager/kubeconfig_manager.go b/pkg/clustermanager/kubeconfig_manager.go new file mode 100644 index 0000000000000..137f841a67a99 --- /dev/null +++ b/pkg/clustermanager/kubeconfig_manager.go @@ -0,0 +1,73 @@ +package clustermanager + +import ( + "bytes" + "context" + "fmt" + "io" + "time" + + corev1 "k8s.io/api/core/v1" + + "github.com/aws/eks-anywhere/pkg/clients/kubernetes" + "github.com/aws/eks-anywhere/pkg/constants" + "github.com/aws/eks-anywhere/pkg/filewriter" + "github.com/aws/eks-anywhere/pkg/kubeconfig" + "github.com/aws/eks-anywhere/pkg/providers" + "github.com/aws/eks-anywhere/pkg/retrier" +) + +type KubeconfigManager struct { + Client kubernetes.Client + // FS is a file system abstraction providing file creation and write capabilities + FS filewriter.FileWriter +} + +// GetKubeconfig retrieves the contents of the specified cluster's kubeconfig from a secret and copies it to an io.Writer. +func (km KubeconfigManager) GetKubeconfig(ctx context.Context, clusterName string, w io.Writer) error { + kubeconfigSecret := &corev1.Secret{} + + err := retrier.New( + time.Minute, + retrier.WithRetryPolicy(retrier.BackOffPolicy(time.Second)), + ).Retry(func() error { + err := km.Client.Get(ctx, fmt.Sprintf("%s-kubeconfig", clusterName), constants.EksaSystemNamespace, kubeconfigSecret) + + if err != nil { + return err + } + + return nil + }) + + if err != nil { + return err + } + + if _, err := io.Copy(w, bytes.NewReader(kubeconfigSecret.Data["value"])); err != nil { + return err + } + + return nil +} + +// WriteKubeconfig takes a raw binary kubeconfig in memory, writes the kubeconfig to a file on disk, and returns the path of the kubeconfig file. +func (km KubeconfigManager) WriteKubeconfig(rawkubeconfig []byte, clusterName string, provider providers.Provider) (string, error) { + err := provider.UpdateKubeConfig(&rawkubeconfig, clusterName) + if err != nil { + return "", err + } + + kubeconfigPath, err := km.FS.Write( + kubeconfig.FormatWorkloadClusterKubeconfigFilename(clusterName), + rawkubeconfig, + filewriter.PersistentFile, + filewriter.Permission0600, + ) + + if err != nil { + return "", err + } + + return kubeconfigPath, nil +} diff --git a/pkg/task/task.go b/pkg/task/task.go index de2d5da191e7d..bba00e47ba7a2 100644 --- a/pkg/task/task.go +++ b/pkg/task/task.go @@ -10,6 +10,7 @@ import ( "sigs.k8s.io/yaml" "github.com/aws/eks-anywhere/pkg/cluster" + "github.com/aws/eks-anywhere/pkg/clustermanager" "github.com/aws/eks-anywhere/pkg/filewriter" "github.com/aws/eks-anywhere/pkg/logger" "github.com/aws/eks-anywhere/pkg/providers" @@ -37,7 +38,7 @@ type CommandContext struct { PackageInstaller interfaces.PackageInstaller EksdUpgrader interfaces.EksdUpgrader ClusterUpgrader interfaces.ClusterUpgrader - ClusterCreator interfaces.ClusterCreator + ClusterCreator clustermanager.ClusterCreator CAPIManager interfaces.CAPIManager ClusterSpec *cluster.Spec CurrentClusterSpec *cluster.Spec diff --git a/pkg/workflows/workload/create.go b/pkg/workflows/workload/create.go index 0ac0d238e8a38..5ad3f8b903018 100644 --- a/pkg/workflows/workload/create.go +++ b/pkg/workflows/workload/create.go @@ -4,6 +4,7 @@ import ( "context" "github.com/aws/eks-anywhere/pkg/cluster" + "github.com/aws/eks-anywhere/pkg/clustermanager" "github.com/aws/eks-anywhere/pkg/filewriter" "github.com/aws/eks-anywhere/pkg/providers" "github.com/aws/eks-anywhere/pkg/task" @@ -17,7 +18,7 @@ type Create struct { gitOpsManager interfaces.GitOpsManager writer filewriter.FileWriter eksdInstaller interfaces.EksdInstaller - clusterCreator interfaces.ClusterCreator + clusterCreator clustermanager.ClusterCreator packageInstaller interfaces.PackageInstaller } @@ -25,9 +26,9 @@ type Create struct { func NewCreate(provider providers.Provider, clusterManager interfaces.ClusterManager, gitOpsManager interfaces.GitOpsManager, writer filewriter.FileWriter, - clusterCreator interfaces.ClusterCreator, eksdInstaller interfaces.EksdInstaller, packageInstaller interfaces.PackageInstaller, + clusterCreator clustermanager.ClusterCreator, ) *Create { return &Create{ provider: provider, diff --git a/pkg/workflows/workload/createcluster.go b/pkg/workflows/workload/createcluster.go index 325c05ee00e7a..f03184e7da898 100644 --- a/pkg/workflows/workload/createcluster.go +++ b/pkg/workflows/workload/createcluster.go @@ -13,7 +13,7 @@ type createCluster struct{} // Run createCluster performs actions needed to create the management cluster. func (c *createCluster) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { logger.Info("Creating workload cluster") - if err := commandContext.ClusterCreator.Run(ctx, commandContext.ClusterSpec, *commandContext.ManagementCluster); err != nil { + if err := commandContext.ClusterCreator.Applier.Run(ctx, commandContext.ClusterSpec, *commandContext.ManagementCluster); err != nil { commandContext.SetError(err) return &workflows.CollectMgmtClusterDiagnosticsTask{} }