Skip to content

Commit af5fa02

Browse files
support join a cluster via grpc (#518)
Signed-off-by: Zhiwei Yin <[email protected]> Co-authored-by: Zhiwei Yin <[email protected]>
1 parent c67ac4c commit af5fa02

File tree

159 files changed

+18533
-20
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+18533
-20
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ require (
4040
)
4141

4242
require (
43+
cloud.google.com/go/compute/metadata v0.5.0 // indirect
4344
dario.cat/mergo v1.0.1 // indirect
4445
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
4546
github.com/BurntSushi/toml v1.5.0 // indirect
@@ -53,6 +54,7 @@ require (
5354
github.com/blang/semver/v4 v4.0.0 // indirect
5455
github.com/cespare/xxhash/v2 v2.3.0 // indirect
5556
github.com/chai2010/gettext-go v1.0.2 // indirect
57+
github.com/cloudevents/sdk-go/v2 v2.16.2 // indirect
5658
github.com/containerd/containerd v1.7.27 // indirect
5759
github.com/containerd/errdefs v0.3.0 // indirect
5860
github.com/containerd/log v0.1.0 // indirect
@@ -75,6 +77,7 @@ require (
7577
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
7678
github.com/gobwas/glob v0.2.3 // indirect
7779
github.com/gogo/protobuf v1.3.2 // indirect
80+
github.com/golang/protobuf v1.5.4 // indirect
7881
github.com/google/btree v1.1.3 // indirect
7982
github.com/google/gnostic-models v0.6.9 // indirect
8083
github.com/google/go-cmp v0.7.0 // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
2+
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
13
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
24
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
35
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
@@ -38,6 +40,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
3840
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
3941
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
4042
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
43+
github.com/cloudevents/sdk-go/v2 v2.16.2 h1:ZYDFrYke4FD+jM8TZTJJO6JhKHzOQl2oqpFK1D+NnQM=
44+
github.com/cloudevents/sdk-go/v2 v2.16.2/go.mod h1:laOcGImm4nVJEU+PHnUrKL56CKmRL65RlQF0kRmW/kg=
4145
github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII=
4246
github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0=
4347
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
@@ -308,6 +312,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
308312
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
309313
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
310314
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
315+
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
316+
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
311317
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
312318
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
313319
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=

pkg/cmd/accept/exec.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ const (
2828
groupNameBootstrap = "system:bootstrappers:managedcluster"
2929
userNameSignatureBootstrapPrefix = "system:bootstrap:"
3030
userNameSignatureSA = "system:serviceaccount:open-cluster-management:agent-registration-bootstrap"
31+
userNameGRPCSignatureSA = "system:serviceaccount:open-cluster-management-hub:grpc-server-sa"
3132
groupNameSA = "system:serviceaccounts:open-cluster-management"
33+
groupNameGRPC = "system:serviceaccounts:open-cluster-management-hub"
3234
clusterLabel = "open-cluster-management.io/cluster-name"
3335
)
3436

@@ -143,13 +145,15 @@ func (o *Options) approveCSR(kubeClient *kubernetes.Clientset, clusterName strin
143145
for _, item := range csrs.Items {
144146
// Does not have the correct name prefix
145147
if !strings.HasPrefix(item.Spec.Username, userNameSignatureBootstrapPrefix) &&
146-
!strings.HasPrefix(item.Spec.Username, userNameSignatureSA) {
148+
!strings.HasPrefix(item.Spec.Username, userNameSignatureSA) &&
149+
!strings.HasPrefix(item.Spec.Username, userNameGRPCSignatureSA) {
147150
continue
148151
}
149152
// Check groups
150153
groups := sets.NewString(item.Spec.Groups...)
151154
if !groups.Has(groupNameBootstrap) &&
152-
!groups.Has(groupNameSA) {
155+
!groups.Has(groupNameSA) &&
156+
!groups.Has(groupNameGRPC) {
153157
continue
154158
}
155159
passedCSRs = append(passedCSRs, item)

pkg/cmd/join/cmd.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ var example = `
2121
%[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
2222
# Join a cluster to the hub and annotate the ManagedCluster
2323
%[1]s join --hub-token <tokenID.tokenSecret> --hub-apiserver <hub_apiserver_url> --cluster-name <cluster_name> --klusterlet-annotation foo=bar --klusterlet-annotation bar=foo
24+
# Join a cluster to the hub via gRPC
25+
%[1]s join --hub-token <tokenID.tokenSecret> --hub-apiserver <hub_apiserver_url> --cluster-name <cluster_name> --registration-auth grpc --grpc-server <grpc_server_address>
2426
`
2527

2628
// NewCmd ...
@@ -83,10 +85,12 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream
8385
cmd.Flags().BoolVar(&o.createNameSpace, "create-namespace", true, "If true, create the operator namespace(open-cluster-management) and the agent namespace(open-cluster-management-agent for Default mode, <klusterlet-name> for Hosted mode), otherwise use existing one")
8486
cmd.Flags().BoolVar(&o.enableSyncLabels, "enable-sync-labels", false, "If true, sync the labels from klusterlet to all agent resources.")
8587
cmd.Flags().Int32Var(&o.clientCertExpirationSeconds, "client-cert-expiration-seconds", 31536000, "clientCertExpirationSeconds represents the seconds of a client certificate to expire.")
86-
cmd.Flags().StringVar(&o.registrationAuth, "registration-auth", "", "The type of authentication to use for registering and authenticating with hub")
88+
cmd.Flags().StringVar(&o.registrationAuth, "registration-auth", "csr", "The type of authentication to use for registering and authenticating with hub. The supported types including: csr, grpc and awsirsa. The default type is csr.")
8789
cmd.Flags().StringVar(&o.hubClusterArn, "hub-cluster-arn", "", "The arn of the hub cluster(i.e. EKS cluster) to which managed-cluster will join")
8890
cmd.Flags().StringVar(&o.managedClusterArn, "managed-cluster-arn", "", "The arn of the managed cluster(i.e. EKS cluster) which will be joining the hub")
8991
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))
9092
cmd.Flags().StringVar(&o.klusterletValuesFile, "klusterlet-values-file", "", "The path to a YAML file containing klusterlet Helm chart values. The values from the file override both the default klusterlet chart values and the values from other flags.")
93+
cmd.Flags().StringVar(&o.grpcServer, "grpc-server", "", "The gRPC server address of the hub")
94+
cmd.Flags().StringVar(&o.grpcCAFile, "grpc-ca-file", "", "Path to gRPC server CA PEM; required if --grpc-server is set")
9195
return cmd
9296
}

pkg/cmd/join/exec.go

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ import (
3636
clusterv1 "open-cluster-management.io/api/cluster/v1"
3737
ocmfeature "open-cluster-management.io/api/feature"
3838
operatorv1 "open-cluster-management.io/api/operator/v1"
39-
4039
"open-cluster-management.io/clusteradm/pkg/cmd/join/preflight"
40+
"open-cluster-management.io/clusteradm/pkg/config"
4141
genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions"
4242
"open-cluster-management.io/clusteradm/pkg/helpers"
4343
"open-cluster-management.io/clusteradm/pkg/helpers/klusterlet"
@@ -48,13 +48,14 @@ import (
4848
"open-cluster-management.io/clusteradm/pkg/helpers/wait"
4949
"open-cluster-management.io/clusteradm/pkg/version"
5050
"open-cluster-management.io/ocm/pkg/operator/helpers/chart"
51+
"open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/cert"
52+
sdkgrpc "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc"
5153
sdkhelpers "open-cluster-management.io/sdk-go/pkg/helpers"
5254
)
5355

5456
const (
55-
AgentNamespacePrefix = "open-cluster-management-"
56-
57-
OperatorNamesapce = "open-cluster-management"
57+
AgentNamespacePrefix = "open-cluster-management-"
58+
OperatorNamespace = "open-cluster-management"
5859
DefaultOperatorName = "klusterlet"
5960
AwsIrsaAuthentication = "awsirsa"
6061
)
@@ -191,7 +192,7 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) {
191192
if err != nil {
192193
return err
193194
}
194-
o.HubCADate = cabytes
195+
o.HubCAData = cabytes
195196
}
196197

197198
// code logic of building hub client in join process:
@@ -212,6 +213,12 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) {
212213
return err
213214
}
214215

216+
if o.grpcServer != "" {
217+
if err = o.getGRPCCAData(externalClientUnSecure); err != nil {
218+
return err
219+
}
220+
}
221+
215222
// get managed cluster externalServerURL
216223
var kubeClient *kubernetes.Clientset
217224
switch o.mode {
@@ -284,6 +291,11 @@ func (o *Options) validate() error {
284291
return err
285292
}
286293

294+
err = o.setGRPCConfig()
295+
if err != nil {
296+
return err
297+
}
298+
287299
// get ManagedKubeconfig from given file
288300
if o.mode == string(operatorv1.InstallModeHosted) {
289301
managedConfig, err := os.ReadFile(o.managedKubeconfigFile)
@@ -323,8 +335,19 @@ func (o *Options) validate() error {
323335
return err
324336
}
325337

326-
if (o.registrationAuth == AwsIrsaAuthentication) && (o.hubClusterArn == "") {
327-
return gherrors.New("hubClusterArn cannot be empty if registrationAuth type is awsirsa")
338+
switch o.registrationAuth {
339+
case operatorv1.AwsIrsaAuthType:
340+
if o.hubClusterArn == "" {
341+
return gherrors.New("hub-cluster-arn is required when registration-auth type is awsirsa")
342+
}
343+
case operatorv1.GRPCAuthType:
344+
if o.grpcServer == "" {
345+
return gherrors.New("grpc-server is required when registration-auth type is grpc")
346+
}
347+
case operatorv1.CSRAuthType:
348+
// default auth type. do nothing
349+
default:
350+
return gherrors.New("invalid registration-Auth type")
328351
}
329352

330353
return nil
@@ -395,7 +418,7 @@ func (o *Options) applyKlusterlet(r *reader.ResourceReader, operatorClient opera
395418
o.klusterletChartConfig.NoOperator = true
396419
}
397420

398-
crds, raw, err := chart.RenderKlusterletChart(o.klusterletChartConfig, OperatorNamesapce)
421+
crds, raw, err := chart.RenderKlusterletChart(o.klusterletChartConfig, OperatorNamespace)
399422
if err != nil {
400423
return err
401424
}
@@ -457,7 +480,7 @@ func checkIfRegistrationOperatorAvailable(f util.Factory) (bool, error) {
457480
return false, err
458481
}
459482

460-
deploy, err := client.AppsV1().Deployments(OperatorNamesapce).
483+
deploy, err := client.AppsV1().Deployments(OperatorNamespace).
461484
Get(context.TODO(), DefaultOperatorName, metav1.GetOptions{})
462485
if err != nil {
463486
if errors.IsNotFound(err) {
@@ -550,7 +573,7 @@ func waitUntilRegistrationOperatorConditionIsTrue(w io.Writer, f util.Factory, t
550573

551574
return helpers.WatchUntil(
552575
func() (watch.Interface, error) {
553-
return client.CoreV1().Pods(OperatorNamesapce).
576+
return client.CoreV1().Pods(OperatorNamespace).
554577
Watch(context.TODO(), metav1.ListOptions{
555578
TimeoutSeconds: &timeout,
556579
LabelSelector: "app=klusterlet",
@@ -664,9 +687,9 @@ func (o *Options) createClientcmdapiv1Config(externalClientUnSecure *kubernetes.
664687
bootstrapConfig := bootstrapExternalConfigUnSecure.DeepCopy()
665688
bootstrapConfig.Clusters[0].Cluster.InsecureSkipTLSVerify = false
666689
bootstrapConfig.Clusters[0].Cluster.Server = o.hubAPIServer
667-
if o.HubCADate != nil {
690+
if o.HubCAData != nil {
668691
// directly set ca-data if --ca-file is set
669-
bootstrapConfig.Clusters[0].Cluster.CertificateAuthorityData = o.HubCADate
692+
bootstrapConfig.Clusters[0].Cluster.CertificateAuthorityData = o.HubCAData
670693
} else {
671694
// get ca data from externalClientUnsecure, ca may empty(cluster-info exists with no ca data)
672695
ca, err := sdkhelpers.GetCACert(externalClientUnSecure)
@@ -713,6 +736,59 @@ func (o *Options) setKubeconfig() error {
713736
return nil
714737
}
715738

739+
func (o *Options) setGRPCConfig() error {
740+
if o.registrationAuth != operatorv1.GRPCAuthType {
741+
return nil
742+
}
743+
744+
gRPCConfig := sdkgrpc.GRPCConfig{
745+
CertConfig: cert.CertConfig{
746+
CAData: cert.Bytes(o.grpcCAData),
747+
},
748+
URL: o.grpcServer,
749+
Token: o.token,
750+
}
751+
752+
configStr, err := yaml.Marshal(gRPCConfig)
753+
if err != nil {
754+
return fmt.Errorf("failed to marshal GRPC server configuration. %v", err)
755+
}
756+
757+
o.klusterletChartConfig.GRPCConfig = string(configStr)
758+
o.klusterletChartConfig.Klusterlet.RegistrationConfiguration.RegistrationDriver = operatorv1.RegistrationDriver{
759+
AuthType: operatorv1.GRPCAuthType,
760+
}
761+
return nil
762+
}
763+
764+
func (o *Options) getGRPCCAData(kubeClient kubernetes.Interface) error {
765+
if o.grpcCAFile != "" {
766+
caData, err := os.ReadFile(o.grpcCAFile)
767+
if err != nil {
768+
return fmt.Errorf("--grpc-ca-file %q read failed: %w", o.grpcCAFile, err)
769+
}
770+
if len(caData) == 0 {
771+
return fmt.Errorf("--grpc-ca-file %q is empty", o.grpcCAFile)
772+
}
773+
o.grpcCAData = caData
774+
return nil
775+
}
776+
777+
cm, err := kubeClient.CoreV1().ConfigMaps(config.HubClusterNamespace).Get(context.TODO(),
778+
config.CABundleConfigMap, metav1.GetOptions{})
779+
if err != nil {
780+
return fmt.Errorf("failed to get CA bundle configmap for gRPC server: %w", err)
781+
}
782+
783+
caBundle, ok := cm.Data["ca-bundle.crt"]
784+
if !ok || len(strings.TrimSpace(caBundle)) == 0 {
785+
return fmt.Errorf("ConfigMap %s/%s is missing or has empty key 'ca-bundle.crt'",
786+
config.HubClusterNamespace, config.CABundleConfigMap)
787+
}
788+
o.grpcCAData = []byte(caBundle)
789+
return nil
790+
}
791+
716792
func mergeCertificateData(caBundles ...[]byte) ([]byte, error) {
717793
var all []*x509.Certificate
718794
for _, caBundle := range caBundles {

pkg/cmd/join/options.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,15 @@ type Options struct {
1919
token string
2020
// The external hub apiserver url (https://<host>:<port>)
2121
hubAPIServer string
22+
// The grpc server of the hub cluster
23+
grpcServer string
24+
2225
// The hub ca-file(optional)
2326
caFile string
27+
28+
// The grpc ca file which can be found in the configmap ca-bundle-configmap in open-cluster-management-hub ns
29+
grpcCAFile string
30+
2431
// the name under the cluster must be imported
2532
clusterName string
2633

@@ -58,13 +65,16 @@ type Options struct {
5865
forceManagedInClusterEndpointLookup bool
5966
hubInClusterEndpoint string
6067

61-
// Values below are tempoary data
62-
// HubCADate: data in hub ca file
63-
HubCADate []byte
68+
// Values below are temporary data
69+
// HubCAData: data in hub ca file
70+
HubCAData []byte
6471
// hub config
6572
HubConfig *clientcmdapiv1.Config
6673

67-
// The URL of a forward proxy server which will be used by agnets on the managed cluster
74+
// grpcCAData: ca data used by the GRPC server
75+
grpcCAData []byte
76+
77+
// The URL of a forward proxy server which will be used by agents on the managed cluster
6878
// to connect to the hub cluster (optional)
6979
proxyURL string
7080
// The proxy server ca-file(optional)

pkg/config/env.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ const (
1616
ManagedClusterNamespace = "open-cluster-management-agent"
1717
ManagedProxyConfigurationName = "cluster-proxy"
1818
ImagePullSecret = "open-cluster-management-image-pull-credentials"
19+
CABundleConfigMap = "ca-bundle-configmap"
1920
)

0 commit comments

Comments
 (0)