Skip to content

Commit 9d0fa06

Browse files
authored
✨ Expose ManagedCluster annotations during join via clusteradm flag (#481)
* feat: expose klusterlet/managedcluster annotations via clusteradm flags Signed-off-by: Artur Shad Nik <[email protected]> * feat: improve handling annotations; add e2e test Signed-off-by: Artur Shad Nik <[email protected]> * test: simplify assertion Signed-off-by: Artur Shad Nik <[email protected]> --------- Signed-off-by: Artur Shad Nik <[email protected]>
1 parent 73281f6 commit 9d0fa06

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

pkg/cmd/join/cmd.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/spf13/cobra"
88
"k8s.io/cli-runtime/pkg/genericiooptions"
9+
operatorv1 "open-cluster-management.io/api/operator/v1"
910
genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions"
1011
"open-cluster-management.io/clusteradm/pkg/helpers"
1112
)
@@ -18,6 +19,8 @@ var example = `
1819
# join a cluster to the hub while the hub provided no valid CA data in kube-public namespace
1920
%[1]s join --hub-token <tokenID.tokenSecret> --hub-apiserver <hub_apiserver_url> --cluster-name <cluster_name> --ca-file <ca-file>
2021
%[1]s join --hub-token <tokenID.tokenSecret> --hub-apiserver <hub_apiserver_url> --cluster-name <cluster_name> --registration-auth awsirsa --hub-cluster-arn arn:aws:eks:us-west-2:123456789012:cluster/hub-cluster-1
22+
# Join a cluster to the hub and annotate the ManagedCluster
23+
%[1]s join --hub-token <tokenID.tokenSecret> --hub-apiserver <hub_apiserver_url> --cluster-name <cluster_name> --klusterlet-annotation foo=bar --klusterlet-annotation bar=foo
2124
`
2225

2326
// NewCmd ...
@@ -80,5 +83,6 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream
8083
cmd.Flags().Int32Var(&o.clientCertExpirationSeconds, "client-cert-expiration-seconds", 31536000, "clientCertExpirationSeconds represents the seconds of a client certificate to expire.")
8184
cmd.Flags().StringVar(&o.registrationAuth, "registration-auth", "", "The type of authentication to use for registering and authenticating with hub")
8285
cmd.Flags().StringVar(&o.hubClusterArn, "hub-cluster-arn", "", "The arn of the hub cluster(i.e. EKS cluster) to which managed-cluster will join")
86+
cmd.Flags().StringArrayVar(&o.klusterletAnnotations, "klusterlet-annotation", []string{}, fmt.Sprintf("Annotations to set on the ManagedCluster, in key=value format. Note: each key will be automatically prefixed with '%s/', if not set.", operatorv1.ClusterAnnotationsKeyPrefix))
8387
return cmd
8488
}

pkg/cmd/join/exec.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) {
152152
genericclioptionsclusteradm.SpokeMutableFeatureGate, ocmfeature.DefaultSpokeRegistrationFeatureGates),
153153
ClientCertExpirationSeconds: o.clientCertExpirationSeconds,
154154
}
155+
o.setKlusterletRegistrationAnnotations()
155156

156157
// set registration auth type
157158
if o.registrationAuth == AwsIrsaAuthentication {
@@ -742,3 +743,34 @@ func mergeCertificateData(caBundles ...[]byte) ([]byte, error) {
742743
}
743744
return b.Bytes(), nil
744745
}
746+
747+
func (o *Options) setKlusterletRegistrationAnnotations() {
748+
if len(o.klusterletAnnotations) == 0 {
749+
return
750+
}
751+
752+
if o.klusterletChartConfig.Klusterlet.RegistrationConfiguration.ClusterAnnotations == nil {
753+
o.klusterletChartConfig.Klusterlet.RegistrationConfiguration.ClusterAnnotations = map[string]string{}
754+
}
755+
756+
for _, annotation := range o.klusterletAnnotations {
757+
i := strings.Index(annotation, "=")
758+
if i == -1 {
759+
klog.Warningf("Skipping malformed annotation (missing '='): %s", annotation)
760+
continue
761+
}
762+
763+
k, v := strings.TrimSpace(annotation[:i]), strings.TrimSpace(annotation[i+1:])
764+
765+
if k == "" {
766+
klog.Warningf("Skipping annotation with empty key: %s", annotation)
767+
continue
768+
}
769+
770+
if !strings.HasPrefix(k, operatorv1.ClusterAnnotationsKeyPrefix) {
771+
k = fmt.Sprintf("%s/%s", operatorv1.ClusterAnnotationsKeyPrefix, k)
772+
}
773+
774+
o.klusterletChartConfig.Klusterlet.RegistrationConfiguration.ClusterAnnotations[k] = v
775+
}
776+
}

pkg/cmd/join/options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ type Options struct {
9292

9393
// The arn of hub cluster(i.e. EKS) to which managed-cluster will join
9494
hubClusterArn string
95+
96+
// Annotations for registration controller to set on the ManagedCluster
97+
klusterletAnnotations []string
9598
}
9699

97100
func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericiooptions.IOStreams) *Options {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright Contributors to the Open Cluster Management project
2+
package clusteradme2e
3+
4+
import (
5+
"context"
6+
"time"
7+
8+
"github.com/onsi/ginkgo/v2"
9+
"github.com/onsi/gomega"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
operatorv1 "open-cluster-management.io/api/operator/v1"
12+
"open-cluster-management.io/clusteradm/test/e2e/util"
13+
)
14+
15+
var _ = ginkgo.Describe("test clusteradm join with annotations", func() {
16+
ginkgo.BeforeEach(func() {
17+
ginkgo.By("clear e2e environment...")
18+
err := e2e.ClearEnv()
19+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
20+
})
21+
22+
ginkgo.Context("join hub scenario with annotations", func() {
23+
var err error
24+
25+
ginkgo.It("should managedclusters join with annotations and be accepted successfully", func() {
26+
ginkgo.By("init hub")
27+
err = e2e.Clusteradm().Init(
28+
"--context", e2e.Cluster().Hub().Context(),
29+
"--bundle-version=latest",
30+
)
31+
gomega.Expect(err).NotTo(gomega.HaveOccurred(), "clusteradm init error")
32+
33+
ginkgo.By("managedcluster1 join hub with annotations")
34+
err = e2e.Clusteradm().Join(
35+
"--context", e2e.Cluster().ManagedCluster1().Context(),
36+
"--hub-token", e2e.CommandResult().Token(), "--hub-apiserver", e2e.CommandResult().Host(),
37+
"--cluster-name", e2e.Cluster().ManagedCluster1().Name(),
38+
"--wait",
39+
"--bundle-version=latest",
40+
"--force-internal-endpoint-lookup",
41+
"--klusterlet-annotation", "foo=bar",
42+
"--klusterlet-annotation", "test=value",
43+
)
44+
gomega.Expect(err).NotTo(gomega.HaveOccurred(), "managedcluster1 join error")
45+
gomega.Eventually(func() error {
46+
return util.ValidateImagePullSecret(managedClusterKubeClient,
47+
"e30=", "open-cluster-management")
48+
}, time.Second*120, time.Second*2).ShouldNot(gomega.HaveOccurred())
49+
50+
ginkgo.By("hub accept managedcluster1")
51+
err = e2e.Clusteradm().Accept(
52+
"--clusters", e2e.Cluster().ManagedCluster1().Name(),
53+
"--wait",
54+
"--context", e2e.Cluster().Hub().Context(),
55+
)
56+
gomega.Expect(err).NotTo(gomega.HaveOccurred(), "clusteradm accept error")
57+
58+
ginkgo.By("verify managedcluster1 has correct annotations")
59+
managedCluster, err := clusterClient.ClusterV1().ManagedClusters().Get(
60+
context.TODO(), e2e.Cluster().ManagedCluster1().Name(), metav1.GetOptions{})
61+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
62+
annotations := managedCluster.GetAnnotations()
63+
gomega.Expect(annotations).NotTo(gomega.BeNil())
64+
gomega.Expect(annotations[operatorv1.ClusterAnnotationsKeyPrefix+"/foo"]).To(gomega.Equal("bar"))
65+
gomega.Expect(annotations[operatorv1.ClusterAnnotationsKeyPrefix+"/test"]).To(gomega.Equal("value"))
66+
})
67+
})
68+
})

0 commit comments

Comments
 (0)