Skip to content

Commit

Permalink
Completing aws registration on spoke
Browse files Browse the repository at this point in the history
Signed-off-by: Gaurav Jaswal <[email protected]>
  • Loading branch information
jaswalkiranavtar committed Jan 6, 2025
1 parent 037aa3c commit 909b210
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,16 @@ type ManagedClusterIamRole struct {
}

func (managedClusterIamRole *ManagedClusterIamRole) arn() string {
managedClusterAccountId, _ := getAwsAccountIdAndClusterName(managedClusterIamRole.AwsIrsa.ManagedClusterArn)
managedClusterAccountId, _ := GetAwsAccountIdAndClusterName(managedClusterIamRole.AwsIrsa.ManagedClusterArn)
md5HashUniqueIdentifier := managedClusterIamRole.md5HashSuffix()

//arn:aws:iam::<managed-cluster-account-id>:role/ocm-managed-cluster-<md5-hash-unique-identifier>
return "arn:aws:iam::" + managedClusterAccountId + ":role/ocm-managed-cluster-" + md5HashUniqueIdentifier
}

func (managedClusterIamRole *ManagedClusterIamRole) md5HashSuffix() string {
hubClusterAccountId, hubClusterName := getAwsAccountIdAndClusterName(managedClusterIamRole.AwsIrsa.HubClusterArn)
managedClusterAccountId, managedClusterName := getAwsAccountIdAndClusterName(managedClusterIamRole.AwsIrsa.ManagedClusterArn)
hubClusterAccountId, hubClusterName := GetAwsAccountIdAndClusterName(managedClusterIamRole.AwsIrsa.HubClusterArn)
managedClusterAccountId, managedClusterName := GetAwsAccountIdAndClusterName(managedClusterIamRole.AwsIrsa.ManagedClusterArn)

hash := md5.Sum([]byte(strings.Join([]string{hubClusterAccountId, hubClusterName, managedClusterAccountId, managedClusterName}, "#"))) // #nosec G401
return hex.EncodeToString(hash[:])
Expand Down Expand Up @@ -574,9 +574,14 @@ func serviceAccountName(suffix string, klusterlet *operatorapiv1.Klusterlet) str
return fmt.Sprintf("%s-%s", klusterlet.Name, suffix)
}

func getAwsAccountIdAndClusterName(clusterArn string) (string, string) {
func GetAwsAccountIdAndClusterName(clusterArn string) (string, string) {
clusterStringParts := strings.Split(clusterArn, ":")
clusterName := strings.Split(clusterStringParts[5], "/")[1]
awsAccountId := clusterStringParts[4]
return awsAccountId, clusterName
}

func GetAwsRegion(clusterArn string) string {
clusterStringParts := strings.Split(clusterArn, ":")
return clusterStringParts[3]
}
25 changes: 19 additions & 6 deletions pkg/registration/register/aws_irsa/aws.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package aws_irsa

import (
"context"
"fmt"

apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"

cluster "open-cluster-management.io/api/client/cluster/clientset/versioned"
managedclusterv1client "open-cluster-management.io/api/client/cluster/clientset/versioned/typed/cluster/v1"
managedclusterinformers "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster"
managedclusterv1lister "open-cluster-management.io/api/client/cluster/listers/cluster/v1"
v1 "open-cluster-management.io/api/cluster/v1"
)

type AWSIRSAControl interface {
Expand All @@ -26,23 +32,31 @@ type v1AWSIRSAControl struct {
}

func (v *v1AWSIRSAControl) isApproved(name string) (bool, error) {
// TODO: check if the managedclusuter cr on hub has required condition and is approved
managedcluster, err := v.get(name)
if err != nil {
return false, err
}
v1Managedcluster := managedcluster.(*v1.ManagedCluster)
approved := false

for _, condition := range v1Managedcluster.Status.Conditions {
if condition.Type == v1.ManagedClusterConditionHubDenied {
return false, nil
} else if condition.Type == v1.ManagedClusterConditionHubAccepted {
approved = true
}
}
return approved, nil
}

func (v *v1AWSIRSAControl) generateEKSKubeConfig(name string) ([]byte, error) {
// TODO: generate and return kubeconfig
// TODO: generate and return kubeconfig, remove this if not needed
return nil, nil
}

func (v *v1AWSIRSAControl) Informer() cache.SharedIndexInformer {
return v.hubManagedClusterInformer
}

//TODO: Uncomment the below once required in the aws irsa authentication implementation
/*
func (v *v1AWSIRSAControl) get(name string) (metav1.Object, error) {
managedcluster, err := v.hubManagedClusterLister.Get(name)
switch {
Expand All @@ -57,7 +71,6 @@ func (v *v1AWSIRSAControl) get(name string) (metav1.Object, error) {
}
return managedcluster, nil
}
*/

func NewAWSIRSAControl(hubManagedClusterInformer managedclusterinformers.Interface, hubManagedClusterClient cluster.Interface) (AWSIRSAControl, error) {
return &v1AWSIRSAControl{
Expand Down
55 changes: 25 additions & 30 deletions pkg/registration/register/aws_irsa/aws_irsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/cache"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/klog/v2"

clusterv1 "open-cluster-management.io/api/cluster/v1"
operatorv1 "open-cluster-management.io/api/operator/v1"

"open-cluster-management.io/ocm/pkg/operator/operators/klusterlet/controllers/klusterletcontroller"
"open-cluster-management.io/ocm/pkg/registration/register"
)

Expand All @@ -33,20 +33,19 @@ const (
type AWSIRSADriver struct {
name string
managedClusterArn string
hubClusterArn string
managedClusterRoleSuffix string
}

func (c *AWSIRSADriver) Process(
ctx context.Context, controllerName string, secret *corev1.Secret, additionalSecretData map[string][]byte,
recorder events.Recorder, opt any) (*corev1.Secret, *metav1.Condition, error) {
logger := klog.FromContext(ctx)

awsOption, ok := opt.(*AWSOption)
if !ok {
return nil, nil, fmt.Errorf("option type is not correct")
}

// TODO: skip if registration request is not accepted yet, that is the required condition is missing on ManagedCluster CR
isApproved, err := awsOption.AWSIRSAControl.isApproved(c.name)
if err != nil {
return nil, nil, err
Expand All @@ -55,37 +54,31 @@ func (c *AWSIRSADriver) Process(
return nil, nil, nil
}

// TODO: Generate kubeconfig if the request is accepted
eksKubeConfigData, err := awsOption.AWSIRSAControl.generateEKSKubeConfig(c.name)
if err != nil {
return nil, nil, err
}
if len(eksKubeConfigData) == 0 {
return nil, nil, nil
}

secret.Data["kubeconfig"] = eksKubeConfigData
logger.Info("Store kubeconfig into the secret.")

recorder.Eventf("EKSHubKubeconfigCreated", "A new eks hub Kubeconfig for %s is available", controllerName)
//return secret, cond, err
return secret, nil, err
recorder.Eventf("EKSRegistrationRequestApproved", "An EKS registration request is approved for %s", controllerName)
return secret, nil, nil
}

//TODO: Uncomment the below once required in the aws irsa authentication implementation

/*
func (c *AWSIRSADriver) reset() {
c.name = ""
}
*/

func (c *AWSIRSADriver) BuildKubeConfigFromTemplate(kubeConfig *clientcmdapi.Config) *clientcmdapi.Config {
hubClusterAccountId, hubClusterName := klusterletcontroller.GetAwsAccountIdAndClusterName(c.hubClusterArn)
awsRegion := klusterletcontroller.GetAwsRegion(c.hubClusterArn)
kubeConfig.AuthInfos = map[string]*clientcmdapi.AuthInfo{register.DefaultKubeConfigAuth: {
ClientCertificate: TLSCertFile,
ClientKey: TLSKeyFile,
Exec: &clientcmdapi.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1beta1",
Command: "aws",
Args: []string{
"--region",
awsRegion,
"eks",
"get-token",
"--cluster-name",
hubClusterName,
"--output",
"json",
"--role",
fmt.Sprintf("arn:aws:iam::%s:role/ocm-hub-%s", hubClusterAccountId, c.managedClusterRoleSuffix),
},
},
}}

return kubeConfig
}

Expand All @@ -111,9 +104,11 @@ func (c *AWSIRSADriver) ManagedClusterDecorator(cluster *clusterv1.ManagedCluste
return cluster
}

func NewAWSIRSADriver(managedClusterArn string, managedClusterRoleSuffix string) register.RegisterDriver {
func NewAWSIRSADriver(managedClusterArn string, managedClusterRoleSuffix string, hubClusterArn string, name string) register.RegisterDriver {
return &AWSIRSADriver{
managedClusterArn: managedClusterArn,
managedClusterRoleSuffix: managedClusterRoleSuffix,
hubClusterArn: hubClusterArn,
name: name,
}
}
2 changes: 1 addition & 1 deletion pkg/registration/register/aws_irsa/aws_irsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func TestIsHubKubeConfigValidFunc(t *testing.T) {
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
driver := NewAWSIRSADriver("", "")
driver := NewAWSIRSADriver("", "", "", "")
secretOption := register.SecretOption{
ClusterName: c.clusterName,
AgentName: c.agentName,
Expand Down
15 changes: 2 additions & 13 deletions pkg/registration/register/aws_irsa/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,18 @@ package aws_irsa

import (
"fmt"
"strings"

"github.com/openshift/library-go/pkg/controller/factory"
"k8s.io/apimachinery/pkg/api/meta"

addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
hubclusterclientset "open-cluster-management.io/api/client/cluster/clientset/versioned"
managedclusterinformers "open-cluster-management.io/api/client/cluster/informers/externalversions/cluster"
clusterv1 "open-cluster-management.io/api/cluster/v1"

"open-cluster-management.io/ocm/pkg/registration/register"
)

// AWSOption includes options that is used to monitor ManagedClusters
type AWSOption struct {
EventFilterFunc factory.EventFilterFunc

AWSIRSAControl AWSIRSAControl
AWSIRSAControl AWSIRSAControl
}

func NewAWSOption(
Expand All @@ -35,16 +29,11 @@ func NewAWSOption(
}
return &AWSOption{
EventFilterFunc: func(obj interface{}) bool {
// TODO: implement EventFilterFunc and update below
accessor, err := meta.Accessor(obj)
if err != nil {
return false
}
labels := accessor.GetLabels()
// only enqueue csr from a specific managed cluster
if labels[clusterv1.ClusterNameLabelKey] != secretOption.ClusterName {
return false
}

// should not contain addon key
_, ok := labels[addonv1alpha1.AddonLabelKey]
Expand All @@ -53,7 +42,7 @@ func NewAWSOption(
}

// only enqueue csr whose name starts with the cluster name
return strings.HasPrefix(accessor.GetName(), fmt.Sprintf("%s-", secretOption.ClusterName))
return accessor.GetName() == secretOption.ClusterName
},
AWSIRSAControl: awsIrsaControl,
}, nil
Expand Down
6 changes: 5 additions & 1 deletion pkg/registration/spoke/spokeagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ func (o *SpokeAgentConfig) RunSpokeAgentWithSpokeInformers(ctx context.Context,
var registerDriver register.RegisterDriver
var registrationOption = o.registrationOption
if registrationOption.RegistrationAuth == AwsIrsaAuthType {
registerDriver = awsIrsa.NewAWSIRSADriver(o.registrationOption.ManagedClusterArn, o.registrationOption.ManagedClusterRoleSuffix)
registerDriver = awsIrsa.NewAWSIRSADriver(o.registrationOption.ManagedClusterArn,
o.registrationOption.ManagedClusterRoleSuffix,
o.registrationOption.HubClusterArn,
o.agentOptions.SpokeClusterName)
} else {
registerDriver = csr.NewCSRDriver()
}
Expand Down Expand Up @@ -331,6 +334,7 @@ func (o *SpokeAgentConfig) RunSpokeAgentWithSpokeInformers(ctx context.Context,
secretOption, registrationAuthOption, o.driver, register.GenerateBootstrapStatusUpdater(), recorder, controllerName)

go bootstrapInformerFactory.Start(bootstrapCtx.Done())
go bootstrapClusterInformerFactory.Start(bootstrapCtx.Done())
go secretController.Run(bootstrapCtx, 1)

// Wait for the hub client config is ready.
Expand Down
Loading

0 comments on commit 909b210

Please sign in to comment.