Skip to content

Commit

Permalink
#29 - implement support AWS provider for Cluster API
Browse files Browse the repository at this point in the history
  • Loading branch information
apanasiuk-el committed Oct 31, 2024
1 parent e7a2085 commit 0100612
Show file tree
Hide file tree
Showing 16 changed files with 712 additions and 647 deletions.
84 changes: 62 additions & 22 deletions cmd/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/client-go/tools/clientcmd"
clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest"

Expand Down Expand Up @@ -42,8 +43,15 @@ func newClusterCommands(conf *config.Config, ctx *cli.Context, workDir string) *
func (cc *ClusterCommands) clusterCTL(args ...string) *util.SpecCMD {
var envs []string

switch {
case cc.Conf.ClusterProvider == azure_provider.AzureClusterProvider:
switch cc.Conf.ClusterProvider {
case aws_provider.AWSClusterProvider:
envs = []string{
"AWS_B64ENCODED_CREDENTIALS=",
"CAPA_EKS_IAM=true",
"CAPA_EKS_ADD_ROLES=true",
"EXP_MACHINE_POOL=true",
}
case azure_provider.AzureClusterProvider:
envs = []string{
"EXP_AKS=true",
"EXP_MACHINE_POOL=true",
Expand Down Expand Up @@ -72,6 +80,15 @@ func (cc *ClusterCommands) kubectl(args ...string) *util.SpecCMD {
}
}

func createManifestFile(object interface{}, dir, fileName string) (string, error) {
data, err := json.Marshal(object)
if err != nil {
return "", err
}

return util.CreateTempYAMLFile(dir, fileName, data)
}

func createClusterCTLConfigFile(output []byte) (string, error) {
clusterCTL := &ClusterCTLConfig{}
if err := yaml.Unmarshal(output, &clusterCTL); err != nil {
Expand Down Expand Up @@ -99,7 +116,7 @@ func (cc *ClusterCommands) getClusterCTLConfig() (string, string, error) {
cc.SpecCMD = cc.prepareHelmfile("--log-level", "error", "-l", "config=clusterctl", "template")
cc.SpecCMD.DisableStdOut = true
if err := releaseRunner(cc).runCMD(); err != nil {
return "", "", fmt.Errorf("Helmfile failed to render template by label release: config=clusterctl\n%s",
return "", "", fmt.Errorf("Helmfile failed to render template by release label: config=clusterctl\n%s",
cc.SpecCMD.StderrBuf.String())
}

Expand Down Expand Up @@ -254,6 +271,23 @@ func (cc *ClusterCommands) switchKubeContext() error {
}

switch cc.Conf.ClusterProvider {
case aws_provider.AWSClusterProvider:
//if err := cc.awsClusterContext(); err != nil {
// return err
//}
//
//_, currentContext, err := cc.getKubeContext()
//if err != nil {
// return err
//}
//
//cc.SpecCMD = cc.kubectl("config", "set-credentials", currentContext,
// "--exec-env", "AWS_CONFIG_FILE="+strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""),
// "--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""),
//)
//cc.SpecCMD.DisableStdOut = true
//cc.SpecCMD.Debug = true
//return releaseRunner(cc).runCMD()
case azure_provider.AzureClusterProvider:
clusterContext, err := cc.azureClusterContext()
if err != nil {
Expand All @@ -263,23 +297,6 @@ func (cc *ClusterCommands) switchKubeContext() error {
if err := cc.mergeKubeConfig(clusterContext); err != nil {
return err
}
case aws_provider.AWSClusterProvider:
if err := cc.awsClusterContext(); err != nil {
return err
}

_, currentContext, err := cc.getKubeContext()
if err != nil {
return err
}

cc.SpecCMD = cc.kubectl("config", "set-credentials", currentContext,
"--exec-env", "AWS_CONFIG_FILE="+strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""),
"--exec-env", "AWS_SHARED_CREDENTIALS_FILE="+strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""),
)
cc.SpecCMD.DisableStdOut = true
cc.SpecCMD.Debug = true
return releaseRunner(cc).runCMD()
}

return nil
Expand All @@ -303,6 +320,15 @@ func (cc *ClusterCommands) provisionDestroyTargetCluster() error {
}

switch cc.Conf.ClusterProvider {
case aws_provider.AWSClusterProvider:
clusterContext, err := cc.AWSClusterContext()
if err != nil {
return err
}

if err := cc.mergeKubeConfig(clusterContext); err != nil {
return err
}
case azure_provider.AzureClusterProvider:
clusterContext, err := cc.azureClusterContext()
if err != nil {
Expand Down Expand Up @@ -378,7 +404,14 @@ func CAPIInitAction(conf *config.Config) cli.AfterFunc {
return err
}

return cc.applyAzureClusterIdentity()
switch cc.Conf.ClusterProvider {
case aws_provider.AWSClusterProvider:
return cc.applyAWSClusterIdentity()
case azure_provider.AzureClusterProvider:
return cc.applyAzureClusterIdentity()
}

return nil
}
}

Expand All @@ -401,7 +434,14 @@ func CAPIUpdateAction(conf *config.Config) cli.ActionFunc {
return err
}

return cc.applyAzureClusterIdentity()
switch cc.Conf.ClusterProvider {
case aws_provider.AWSClusterProvider:
return cc.applyAWSClusterIdentity()
case azure_provider.AzureClusterProvider:
return cc.applyAzureClusterIdentity()
}

return nil
}
}

Expand Down
175 changes: 152 additions & 23 deletions cmd/cluster_capa.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,167 @@
package cmd

import (
"strings"
"os"
"path/filepath"

"rmk/util"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/client-go/applyconfigurations/core/v1"

"rmk/providers/aws_provider"
)

const (
awsFlagsCategory = "AWS authentication"

awsIAMControllerCredentialsTemplate = `[default]
aws_access_key_id = {{ .AwsCredentialsProfile.AccessKeyID }}
aws_secret_access_key = {{ .AwsCredentialsProfile.SecretAccessKey }}
region = {{ .Region }}
{{- if .AwsCredentialsProfile.SessionToken }}
aws_session_token = {{ .AwsCredentialsProfile.SessionToken }}
{{- end }}
`
awsIAMControllerSecret = "aws-iam-controller-secret"
awsClusterStaticIdentityName = "aws-cluster-identity"
awsClusterStaticIdentityNamespace = "capa-system"
awsClusterStaticIdentitySecret = "aws-cluster-identity-secret"
)

func (cc *ClusterCommands) getAWSEksKubeConfig() *util.SpecCMD {
return &util.SpecCMD{
Envs: []string{
"AWS_PROFILE=" + cc.Conf.Profile,
"AWS_CONFIG_FILE=" + strings.Join(cc.Conf.AWSSharedConfigFile(cc.Conf.Profile), ""),
"AWS_SHARED_CREDENTIALS_FILE=" + strings.Join(cc.Conf.AWSSharedCredentialsFile(cc.Conf.Profile), ""),
},
Args: []string{"eks", "--region",
cc.Conf.Region,
"update-kubeconfig",
"--name",
cc.Conf.Name + "-eks",
"--profile",
cc.Conf.Profile,
var awsClusterStaticIdentitySecretType = corev1.SecretTypeOpaque

type AWSClusterStaticIdentityConfig struct {
*AWSClusterStaticIdentity
*v1.SecretApplyConfiguration
AWSIAMControllerSecret *v1.SecretApplyConfiguration
ManifestFiles []string
ManifestFilesDir string
}

type AWSClusterStaticIdentity struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec AWSClusterStaticIdentitySpec `json:"spec,omitempty"`
}

type AWSClusterStaticIdentitySpec struct {
AllowedNamespaces struct {
NamespaceList []string `json:"list"`
Selector *metav1.LabelSelector `json:"selector,omitempty"`
} `json:"allowedNamespaces,omitempty"`
SecretRef string `json:"secretRef"`
}

func NewAWSClusterStaticIdentityConfig(ac *aws_provider.AwsConfigure) *AWSClusterStaticIdentityConfig {
acic := &AWSClusterStaticIdentityConfig{
AWSClusterStaticIdentity: &AWSClusterStaticIdentity{
TypeMeta: metav1.TypeMeta{
Kind: "AWSClusterStaticIdentity",
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
},
ObjectMeta: metav1.ObjectMeta{
Name: awsClusterStaticIdentityName,
Namespace: awsClusterStaticIdentityNamespace,
Labels: map[string]string{"clusterctl.cluster.x-k8s.io/move-hierarchy": "true"},
},
Spec: AWSClusterStaticIdentitySpec{
AllowedNamespaces: struct {
NamespaceList []string `json:"list"`
Selector *metav1.LabelSelector `json:"selector,omitempty"`
}(struct {
NamespaceList []string
Selector *metav1.LabelSelector
}{
NamespaceList: []string{awsClusterStaticIdentityNamespace},
}),
SecretRef: awsClusterStaticIdentitySecret,
},
},
Command: "aws",
Ctx: cc.Ctx.Context,
Dir: cc.WorkDir,
Debug: true,
AWSIAMControllerSecret: v1.Secret(awsIAMControllerSecret, awsClusterStaticIdentityNamespace),
SecretApplyConfiguration: v1.Secret(awsClusterStaticIdentitySecret, awsClusterStaticIdentityNamespace),
ManifestFilesDir: filepath.Join("/tmp", awsClusterStaticIdentityName),
}

profile, err := ac.RenderAWSConfigProfile(awsIAMControllerCredentialsTemplate)
if err != nil {
return nil
}

acic.AWSIAMControllerSecret.Type = &awsClusterStaticIdentitySecretType
acic.AWSIAMControllerSecret.Data = map[string][]byte{"credentials": profile}

acic.SecretApplyConfiguration.Type = &awsClusterStaticIdentitySecretType
acic.SecretApplyConfiguration.Data = map[string][]byte{
"AccessKeyID": []byte(ac.AwsCredentialsProfile.AccessKeyID),
"SecretAccessKey": []byte(ac.AwsCredentialsProfile.SecretAccessKey),
}

if len(ac.AwsCredentialsProfile.SessionToken) > 0 {
acic.SecretApplyConfiguration.Data["SessionToken"] = []byte(ac.AwsCredentialsProfile.SessionToken)
}

return acic
}

func (acic *AWSClusterStaticIdentityConfig) createAWSClusterIdentityManifestFiles() error {
if err := os.MkdirAll(acic.ManifestFilesDir, 0775); err != nil {
return err
}

fileCR, err := createManifestFile(acic.AWSClusterStaticIdentity, acic.ManifestFilesDir, awsClusterStaticIdentityName)
if err != nil {
return err
}

acic.ManifestFiles = append(acic.ManifestFiles, fileCR)

fileCRSecret, err := createManifestFile(acic.SecretApplyConfiguration, acic.ManifestFilesDir, awsClusterStaticIdentitySecret)
if err != nil {
return err
}

acic.ManifestFiles = append(acic.ManifestFiles, fileCRSecret)

fileIAMControllerSecret, err := createManifestFile(acic.AWSIAMControllerSecret, acic.ManifestFilesDir, awsIAMControllerSecret)
if err != nil {
return err
}

acic.ManifestFiles = append(acic.ManifestFiles, fileIAMControllerSecret)

return nil
}

func (cc *ClusterCommands) applyAWSClusterIdentity() error {
var kubectlArgs = []string{"apply"}

ac := aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile)
if err := ac.ReadAWSConfigProfile(); err != nil {
return err
}

acic := NewAWSClusterStaticIdentityConfig(ac)
if err := acic.createAWSClusterIdentityManifestFiles(); err != nil {
return err
}

for _, val := range acic.ManifestFiles {
kubectlArgs = append(kubectlArgs, "-f", val)
}

cc.SpecCMD = cc.kubectl(kubectlArgs...)
if err := releaseRunner(cc).runCMD(); err != nil {
if err := os.RemoveAll(acic.ManifestFilesDir); err != nil {
return err
}

return err
}

return os.RemoveAll(acic.ManifestFilesDir)
}

func (cc *ClusterCommands) awsClusterContext() error {
cc.SpecCMD = cc.getAWSEksKubeConfig()
return releaseRunner(cc).runCMD()
func (cc *ClusterCommands) AWSClusterContext() ([]byte, error) {
return aws_provider.NewAwsConfigure(cc.Ctx.Context, cc.Conf.Profile).GetAWSClusterContext(cc.Conf.Name)
}
15 changes: 4 additions & 11 deletions cmd/cluster_capz.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import (

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/json"
v1 "k8s.io/client-go/applyconfigurations/core/v1"

"rmk/providers/azure_provider"
"rmk/util"
)

const (
Expand Down Expand Up @@ -88,15 +86,6 @@ func NewAzureClusterIdentityConfig(ac *azure_provider.AzureConfigure) *AzureClus
return acic
}

func createManifestFile(object interface{}, dir, fileName string) (string, error) {
data, err := json.Marshal(object)
if err != nil {
return "", err
}

return util.CreateTempYAMLFile(dir, fileName, data)
}

func (acic *AzureClusterIdentityConfig) createAzureClusterIdentityManifestFiles() error {
if err := os.MkdirAll(acic.ManifestFilesDir, 0775); err != nil {
return err
Expand Down Expand Up @@ -138,6 +127,10 @@ func (cc *ClusterCommands) applyAzureClusterIdentity() error {

cc.SpecCMD = cc.kubectl(kubectlArgs...)
if err := releaseRunner(cc).runCMD(); err != nil {
if err := os.RemoveAll(acic.ManifestFilesDir); err != nil {
return err
}

return err
}

Expand Down
Loading

0 comments on commit 0100612

Please sign in to comment.