diff --git a/go.mod b/go.mod index 1d67ce45..a51319a4 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( open-cluster-management.io/api v1.1.0 open-cluster-management.io/cluster-proxy v0.7.0 open-cluster-management.io/managed-serviceaccount v0.8.0 - open-cluster-management.io/ocm v1.1.0 + open-cluster-management.io/ocm v1.1.1-0.20251105064423-d80ec55608e7 open-cluster-management.io/sdk-go v1.1.0 sigs.k8s.io/apiserver-network-proxy v0.29.0 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 diff --git a/go.sum b/go.sum index f1ce6ae6..5cfcab9f 100644 --- a/go.sum +++ b/go.sum @@ -484,8 +484,8 @@ open-cluster-management.io/cluster-proxy v0.7.0 h1:qOok0BIBL6j4mLRArzJdz0gK5nyyn open-cluster-management.io/cluster-proxy v0.7.0/go.mod h1:6cgnExpuprO7Le7aqf7bI3H7Nvu3YnXBJCIbJ7wsC0s= open-cluster-management.io/managed-serviceaccount v0.8.0 h1:8+Z142IUqVT/enxXkyb0nzLUL7JaR7dBM2fDtlCA4pM= open-cluster-management.io/managed-serviceaccount v0.8.0/go.mod h1:eTixwpLA6XkPQARDjze3k0KRjwn6N22eFOEFx8CpB0I= -open-cluster-management.io/ocm v1.1.0 h1:Gu5+LYMHMCNxE1StB2x9SyH1E1K1cNOTdUrp1Id++T8= -open-cluster-management.io/ocm v1.1.0/go.mod h1:vuIzDonz/ypkaLNqXvjOHSwMA29x+e25s01S8Z0j3gc= +open-cluster-management.io/ocm v1.1.1-0.20251105064423-d80ec55608e7 h1:wRA9v3BH1mfxZiMZVgc0Ert2JziNbekUcg4PpPQUrLk= +open-cluster-management.io/ocm v1.1.1-0.20251105064423-d80ec55608e7/go.mod h1:LlEIZdZrQQduPS6HqFKvdadtFjOfVJzBj80/Ur+exP8= open-cluster-management.io/sdk-go v1.1.0 h1:vYGkoihIVetyVT4ICO7HjoUHsnh6Gf+Da4ZSmWCamhc= open-cluster-management.io/sdk-go v1.1.0/go.mod h1:DH4EMNDMiousmaj+noHYQxm48T+dbogiAfALhDnrjMg= oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= diff --git a/pkg/cmd/init/cmd.go b/pkg/cmd/init/cmd.go index 496bde9e..0bacb034 100644 --- a/pkg/cmd/init/cmd.go +++ b/pkg/cmd/init/cmd.go @@ -16,7 +16,7 @@ var example = ` %[1]s init # Initialize the hub cluster with the type of authentication. Either or both of csr,awsirsa -%[1]s init --registration-drivers "awsirsa,csr" +%[1]s init --registration-drivers "awsirsa,csr,grpc" --hubClusterArn arn:aws:eks:us-west-2:123456789012:cluster/hub-cluster1 --aws-resource-tags product:v1:tenant:app-name=My-App,product:v1:tenant:created-by=Team-1 --auto-approved-csr-identities="user1,user2" @@ -89,7 +89,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream o.Helm.AddFlags(singletonSet) cmd.Flags().AddFlagSet(singletonSet) cmd.Flags().StringSliceVar(&o.registrationDrivers, "registration-drivers", []string{}, - "The type of authentication to use for registering and authenticating with hub. Only csr and awsirsa are accepted as valid inputs. This flag can be repeated to specify multiple authentication types.") + "The type of authentication to use for registering and authenticating with hub. Only csr, awsirsa and grpc are accepted as valid inputs. This flag can be repeated to specify multiple authentication types.") cmd.Flags().StringVar(&o.hubClusterArn, "hub-cluster-arn", "", "The hubCluster ARN to be passed if awsirsa is one of the registrationAuths and the cluster name in EKS kubeconfig doesn't contain hubClusterArn") cmd.Flags().StringSliceVar(&o.awsResourceTags, "aws-resource-tags", []string{}, @@ -100,6 +100,8 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream cmd.Flags().StringSliceVar(&o.autoApprovedARNPatterns, "auto-approved-arn-patterns", []string{}, "List of AWS EKS ARN patterns so any EKS clusters with these patterns will be auto accepted to join with hub cluster") cmd.Flags().BoolVar(&o.enableSyncLabels, "enable-sync-labels", false, "If true, sync the labels from clustermanager to all hub resources.") - + cmd.Flags().StringVar(&o.grpcServer, "grpc-server", "", "The gRPC server address of the hub") + cmd.Flags().StringSliceVar(&o.autoApprovedGRPCIdentities, "auto-approved-grpc-identities", []string{}, + "List of users or identities that are accepted and whatever matches can be auto accepted to join hub for grpc clusters") return cmd } diff --git a/pkg/cmd/init/exec.go b/pkg/cmd/init/exec.go index 4f3e0289..913240ef 100644 --- a/pkg/cmd/init/exec.go +++ b/pkg/cmd/init/exec.go @@ -40,6 +40,8 @@ var ( releaseName = "multicluster-controlplane" ) +var validRegistrationDriver = sets.New[string](operatorv1.CSRAuthType, operatorv1.AwsIrsaAuthType, operatorv1.GRPCAuthType) + func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { klog.V(1).InfoS("init options:", "dry-run", o.ClusteradmFlags.DryRun, "force", o.force, "output-file", o.outputFile) @@ -95,6 +97,25 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { genericclioptionsclusteradm.HubMutableFeatureGate, ocmfeature.DefaultHubAddonManagerFeatureGates), }, } + if sets.New[string](o.registrationDrivers...).Has(operatorv1.GRPCAuthType) { + if o.grpcServer == "" { + return fmt.Errorf("grpc server should not be empty if registration driver has grpc type") + } + + o.clusterManagerChartConfig.ClusterManager.ServerConfiguration = operatorv1.ServerConfiguration{ + EndpointsExposure: []operatorv1.EndpointExposure{ + { + Protocol: operatorv1.GRPCAuthType, + GRPC: &operatorv1.Endpoint{ + Type: operatorv1.EndpointTypeHostname, + Hostname: &operatorv1.HostnameConfig{ + Host: o.grpcServer, + }, + }, + }, + }, + } + } o.clusterManagerChartConfig.CreateBootstrapToken = o.useBootstrapToken if o.imagePullCredFile != "" { @@ -155,25 +176,29 @@ func (o *Options) validate() error { return fmt.Errorf("registry should not be empty") } - validRegistrationDriver := sets.New[string]("csr", "awsirsa") for _, driver := range o.registrationDrivers { if !validRegistrationDriver.Has(driver) { - return fmt.Errorf("only csr and awsirsa are valid drivers") + return fmt.Errorf("only csr,awsirsa and grpc are valid drivers") } } if genericclioptionsclusteradm.HubMutableFeatureGate.Enabled("ManagedClusterAutoApproval") { // If hub registration does not accept awsirsa, we stop user if they also pass in a list of patterns for AWS EKS ARN. - if len(o.autoApprovedARNPatterns) > 0 && !sets.New[string](o.registrationDrivers...).Has("awsirsa") { + if len(o.autoApprovedARNPatterns) > 0 && !sets.New[string](o.registrationDrivers...).Has(operatorv1.AwsIrsaAuthType) { return fmt.Errorf("should not provide list of patterns for aws eks arn if not initializing hub with awsirsa registration") } // If hub registration does not accept csr, we stop user if they also pass in a list of users for CSR auto approval. - if len(o.autoApprovedCSRIdentities) > 0 && !sets.New[string](o.registrationDrivers...).Has("csr") { + if len(o.autoApprovedCSRIdentities) > 0 && !sets.New[string](o.registrationDrivers...).Has(operatorv1.CSRAuthType) { return fmt.Errorf("should not provide list of users for csr to auto approve if not initializing hub with csr registration") } - } else if len(o.autoApprovedARNPatterns) > 0 || len(o.autoApprovedCSRIdentities) > 0 { + + if len(o.autoApprovedGRPCIdentities) > 0 && !sets.New[string](o.registrationDrivers...).Has(operatorv1.GRPCAuthType) { + return fmt.Errorf("should not provide list of users or identities for grpc cluster to auto approve if not initializing hub with grpc registration") + } + + } else if len(o.autoApprovedARNPatterns) > 0 || len(o.autoApprovedCSRIdentities) > 0 || len(o.autoApprovedGRPCIdentities) > 0 { return fmt.Errorf("should enable feature gate ManagedClusterAutoApproval before passing list of identities") } @@ -394,22 +419,38 @@ func (o *Options) deploySingletonControlplane(kubeClient kubernetes.Interface) e func getRegistrationDrivers(o *Options) ([]operatorv1.RegistrationDriverHub, error) { registrationDrivers := []operatorv1.RegistrationDriverHub{} - var registrationDriver operatorv1.RegistrationDriverHub for _, driver := range o.registrationDrivers { - if driver == "csr" { - csr := &operatorv1.CSRConfig{AutoApprovedIdentities: o.autoApprovedCSRIdentities} - registrationDriver = operatorv1.RegistrationDriverHub{AuthType: driver, CSR: csr} - } else if driver == "awsirsa" { + var registrationDriver operatorv1.RegistrationDriverHub + switch driver { + case operatorv1.CSRAuthType: + registrationDriver = operatorv1.RegistrationDriverHub{AuthType: operatorv1.CSRAuthType} + if len(o.autoApprovedCSRIdentities) != 0 { + registrationDriver.CSR = &operatorv1.CSRConfig{ + AutoApprovedIdentities: o.autoApprovedCSRIdentities, + } + } + case operatorv1.AwsIrsaAuthType: hubClusterArn, err := getHubClusterArn(o) if err != nil { return registrationDrivers, err } awsirsa := &operatorv1.AwsIrsaConfig{HubClusterArn: hubClusterArn, Tags: o.awsResourceTags, AutoApprovedIdentities: o.autoApprovedARNPatterns} - registrationDriver = operatorv1.RegistrationDriverHub{AuthType: driver, AwsIrsa: awsirsa} + registrationDriver = operatorv1.RegistrationDriverHub{AuthType: operatorv1.AwsIrsaAuthType, AwsIrsa: awsirsa} + case operatorv1.GRPCAuthType: + registrationDriver = operatorv1.RegistrationDriverHub{AuthType: operatorv1.GRPCAuthType} + if len(o.autoApprovedGRPCIdentities) != 0 { + registrationDriver.GRPC = &operatorv1.GRPCRegistrationConfig{ + AutoApprovedIdentities: o.autoApprovedGRPCIdentities, + } + } + default: + return registrationDrivers, fmt.Errorf("unknown registration-drivers type: %s", driver) } + registrationDrivers = append(registrationDrivers, registrationDriver) } + return registrationDrivers, nil } diff --git a/pkg/cmd/init/options.go b/pkg/cmd/init/options.go index 6d488c24..163da6ea 100644 --- a/pkg/cmd/init/options.go +++ b/pkg/cmd/init/options.go @@ -67,6 +67,12 @@ type Options struct { awsResourceTags []string // enableSyncLabels is to enable the feature which can sync the labels from clustermanager to all hub resources. enableSyncLabels bool + + // grpcServer is the gRPC server of the hub. + grpcServer string + // autoApprovedGRPCIdentities are a list of users or identities that are accepted and whatever matches can + // be auto accepted to join hub for grpc clusters. + autoApprovedGRPCIdentities []string } func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericiooptions.IOStreams) *Options { diff --git a/test/e2e/clusteradm/joinhubscenario_grpc_test.go b/test/e2e/clusteradm/joinhubscenario_grpc_test.go index 64364034..c7a0ca40 100644 --- a/test/e2e/clusteradm/joinhubscenario_grpc_test.go +++ b/test/e2e/clusteradm/joinhubscenario_grpc_test.go @@ -38,58 +38,27 @@ var _ = ginkgo.Describe("test clusteradm join with grpc", ginkgo.Label("join-hub ginkgo.By("init hub") err = e2e.Clusteradm().Init( "--context", e2e.Cluster().Hub().Context(), + "--registration-drivers", "csr,grpc", + "--grpc-server", "cluster-manager-grpc-server.open-cluster-management-hub.svc:8090", + "--feature-gates=ManagedClusterAutoApproval=true", + "--auto-approved-grpc-identities", "system:serviceaccount:open-cluster-management:agent-registration-bootstrap", "--bundle-version=latest", ) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "clusteradm init error") + util.WaitClusterManagerApplied(operatorClient) - ginkgo.By("wait for cluster-manager CR to be created and update cluster-manager to enable grpc") var clusterManager *operatorv1.ClusterManager gomega.Eventually(func() error { - clusterManager, err = operatorClient.OperatorV1().ClusterManagers().Get( - context.TODO(), "cluster-manager", metav1.GetOptions{}) - if err != nil { - return err - } - - // Enable ManagedClusterAutoApproval feature gate - if clusterManager.Spec.RegistrationConfiguration == nil { - clusterManager.Spec.RegistrationConfiguration = &operatorv1.RegistrationHubConfiguration{} - } - clusterManager.Spec.RegistrationConfiguration.FeatureGates = append( - clusterManager.Spec.RegistrationConfiguration.FeatureGates, - operatorv1.FeatureGate{ - Feature: "ManagedClusterAutoApproval", - Mode: operatorv1.FeatureGateModeTypeEnable, - }, - ) - - // Add grpc authType in registrationDrivers - clusterManager.Spec.RegistrationConfiguration.RegistrationDrivers = []operatorv1.RegistrationDriverHub{ - { - AuthType: operatorv1.GRPCAuthType, - GRPC: &operatorv1.GRPCRegistrationConfig{ - AutoApprovedIdentities: []string{ - "system:serviceaccount:open-cluster-management:agent-registration-bootstrap", - }, - }, - }, - } - - // Add serverConfiguration with grpc protocol - clusterManager.Spec.ServerConfiguration = &operatorv1.ServerConfiguration{ - EndpointsExposure: []operatorv1.EndpointExposure{ - { - Protocol: "grpc", - }, - }, - } - - _, err = operatorClient.OperatorV1().ClusterManagers().Update( - context.TODO(), clusterManager, metav1.UpdateOptions{}) + clusterManager, err = operatorClient.OperatorV1().ClusterManagers().Get(context.TODO(), + "cluster-manager", metav1.GetOptions{}) return err - }, time.Second*30, time.Second*2).Should(gomega.Succeed()) + }, time.Second*60, time.Second*2).Should(gomega.Succeed()) - util.WaitClusterManagerApplied(operatorClient) + gomega.Expect(len(clusterManager.Spec.RegistrationConfiguration.RegistrationDrivers)).To( + gomega.Equal(2), "should have 2 registration drivers") + + gomega.Expect(clusterManager.Spec.ServerConfiguration.EndpointsExposure[0].Protocol).To( + gomega.Equal("grpc"), "server config endpoint exposure protocol should be grpc") ginkgo.By(fmt.Sprintf("join hub as managedCluster %s with grpc", e2e.Cluster().Hub().Name())) err = e2e.Clusteradm().Join( diff --git a/test/e2e/util/helper.go b/test/e2e/util/helper.go index 3f37729d..f56e6f4f 100644 --- a/test/e2e/util/helper.go +++ b/test/e2e/util/helper.go @@ -120,7 +120,9 @@ func WaitClustersDeleted(restcfg *rest.Config) error { return err } } - return fmt.Errorf("wait all clusters are deleted: %v", clusterList.Items) + + fmt.Printf("wait for all clusters to be deleted: %+v\n", clusterList.Items) + return fmt.Errorf("not all clusters are deleted") }, time.Second*300, time.Second*2).Should(gomega.Succeed()) return nil diff --git a/test/e2e/util/util.go b/test/e2e/util/util.go index e3674fd4..0104ff6a 100644 --- a/test/e2e/util/util.go +++ b/test/e2e/util/util.go @@ -73,7 +73,7 @@ func initE2E() (*TestE2eConfig, error) { return err } - fmt.Println("unjoin managedcluster1...") + fmt.Println("unjoin managedCluster on the spoke cluster...") err := e2eConf.Clusteradm().Unjoin( "--context", e2eConf.Cluster().ManagedCluster1().Context(), "--cluster-name", e2eConf.Cluster().ManagedCluster1().Name(), diff --git a/vendor/modules.txt b/vendor/modules.txt index 4cd153d4..c47e94ef 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1270,7 +1270,7 @@ open-cluster-management.io/managed-serviceaccount/pkg/generated/clientset/versio open-cluster-management.io/managed-serviceaccount/pkg/generated/clientset/versioned/scheme open-cluster-management.io/managed-serviceaccount/pkg/generated/clientset/versioned/typed/authentication/v1alpha1 open-cluster-management.io/managed-serviceaccount/pkg/generated/clientset/versioned/typed/authentication/v1beta1 -# open-cluster-management.io/ocm v1.1.0 +# open-cluster-management.io/ocm v1.1.1-0.20251105064423-d80ec55608e7 ## explicit; go 1.24.0 open-cluster-management.io/ocm/deploy/cluster-manager/chart open-cluster-management.io/ocm/deploy/klusterlet/chart diff --git a/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/templates/cluster_manager.yaml b/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/templates/cluster_manager.yaml index ecee98da..1c5763ca 100644 --- a/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/templates/cluster_manager.yaml +++ b/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/templates/cluster_manager.yaml @@ -41,4 +41,8 @@ spec: addOnManagerConfiguration: {{- toYaml . | nindent 4 }} {{- end }} + {{- with .Values.clusterManager.serverConfiguration }} + serverConfiguration: + {{- toYaml . | nindent 4 }} + {{- end }} {{- end }} diff --git a/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/values.yaml b/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/values.yaml index c9bc078f..717c185f 100644 --- a/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/values.yaml +++ b/vendor/open-cluster-management.io/ocm/deploy/cluster-manager/chart/cluster-manager/values.yaml @@ -21,6 +21,19 @@ images: # Please set the userName/password or the dockerConfigJson if you use a private image registry. # The registry will be set in the generated credential if you set userName/password. # Suggest to use dockerConfigJson if you set overrides here. + # For example: + # --set-file images.imageCredentials.dockerConfigJson= , + # or set the json context in values.yaml file + # dockerConfigJson: | + # { + # "auths": { + # "": { + # "auth": "xxx", + # "email": "xxx" + # } + # } + # } + # # The image pull secret is fixed into the serviceAccount, you can also set # `createImageCredentials` to `false` and create the pull secret manually. imageCredentials: @@ -100,9 +113,20 @@ clusterManager: mode: Enable registrationDrivers: - authType: csr +# - authType: grpc +# grpc: +# autoApprovedIdentities: +# - system:serviceaccount:open-cluster-management:agent-registration-bootstrap workConfiguration: workDriver: kube addOnManagerConfiguration: {} # featureGates: # - feature: "" # mode: "" +# serverConfiguration: +# endpointsExposure: +# - protocol: grpc +# grpc: +# type: hostname +# hostname: +# host: grpc-server-open-cluster-management-hub.apps.server-foundation-sno-lite-w8rlq.dev04.red-chesterfield.com diff --git a/vendor/open-cluster-management.io/ocm/deploy/klusterlet/chart/klusterlet/values.yaml b/vendor/open-cluster-management.io/ocm/deploy/klusterlet/chart/klusterlet/values.yaml index 6db39c1d..e4e630bc 100644 --- a/vendor/open-cluster-management.io/ocm/deploy/klusterlet/chart/klusterlet/values.yaml +++ b/vendor/open-cluster-management.io/ocm/deploy/klusterlet/chart/klusterlet/values.yaml @@ -11,13 +11,26 @@ images: # registry and tag work on all images, but the image will be replaced by overrides if the image in overrides is not empty. # The registry name must NOT contain a trailing slash. registry: quay.io/open-cluster-management - # The image tag is the appVersion by default, can be replaced by this version. + # The image tag is the appVersion by default, can be replaced by this version, for example v1.0.0. tag: "" imagePullPolicy: IfNotPresent # The image pull secret name is open-cluster-management-image-pull-credentials. # Please set the userName/password or the dockerConfigJson if you use a private image registry. # The registry will be set in the generated credential if you set userName/password. - # Suggest to use dockerConfigJson if you set overrides here. + # Suggest to use dockerConfigJson if you set overrides here, + # For example: + # --set-file images.imageCredentials.dockerConfigJson= , + # or set the json context in values.yaml file + # dockerConfigJson: | + # { + # "auths": { + # "": { + # "auth": "xxx", + # "email": "xxx" + # } + # } + # } + # # The image pull secret is fixed into the serviceAccount, you can also set # `createImageCredentials` to `false` and create the pull secret manually. imageCredentials: @@ -75,11 +88,30 @@ affinity: # enableSyncLabels is to enable the feature which can sync the labels from klusterlet to all agent resources. enableSyncLabels: false -# should be the kubeConfig file of the hub cluster via setting --set-file= optional +# The content of the kubeConfig for the hub cluster. Can be set via --set-file bootstrapHubKubeConfig= +# or set the kubeConfig content directly here. +# bootstrapHubKubeConfig: | +# apiVersion: v1 +# clusters: +# - cluster: +# certificate-authority-data: xxx +# server: +# name: xx +# contexts: +# - context: +# ... bootstrapHubKubeConfig: "" -# grpcConfig includes the information needed to build connect to gRPC server in the bootstrap secret for -# cluster importing via setting --set-file= optional. +# grpcConfig includes the information needed to build connection to gRPC server in the bootstrap secret for +# cluster importing. Can be set via --set-file grpcConfig= +# or set the grpcConfig content directly here. +# caData is the PEM-encoded certificate authority certificates with base64 encoding for the gRPC server. +# The default caData can be got on the Hub cluster by `kubectl get configmaps -n open-cluster-management-hub ca-bundle-configmap -o jsonpath='{.data.ca-bundle\.crt}'`. +# token can be got on the Hub cluster by `clusteradm get token`. +# grpcConfig: | +# caData: xxx +# token: xxx +# url: # required grpcConfig: "" # when MultipleHubs feature gate in klusterlet.registrationConfiguration is enabled, could set multiple bootstrap hub kubeConfigs here. @@ -90,8 +122,8 @@ multiHubBootstrapHubKubeConfigs: # - name: xxx # kubeConfig: xxx -# should be the kubeConfig file of the managed cluster via setting --set-file= -# only need to set in the hosted mode. optional +# The content of the kubeConfig for the managed cluster. Can be set via --set-file externalManagedKubeConfig= +# Only needed in Hosted or SingletonHosted mode. Optional. externalManagedKubeConfig: "" # only install the klusterlet CR if set true. diff --git a/vendor/open-cluster-management.io/ocm/pkg/operator/helpers/chart/config.go b/vendor/open-cluster-management.io/ocm/pkg/operator/helpers/chart/config.go index 40fe67da..0f6f732d 100644 --- a/vendor/open-cluster-management.io/ocm/pkg/operator/helpers/chart/config.go +++ b/vendor/open-cluster-management.io/ocm/pkg/operator/helpers/chart/config.go @@ -141,6 +141,10 @@ type ClusterManagerConfig struct { // +optional AddOnManagerConfiguration operatorv1.AddOnManagerConfiguration `json:"addOnManagerConfiguration,omitempty"` + // ServerConfiguration contains the configuration of http/grpc server. + // +optional + ServerConfiguration operatorv1.ServerConfiguration `json:"serverConfiguration,omitempty"` + // ResourceRequirement specify QoS classes of deployments managed by clustermanager. // It applies to all the containers in the deployments. // +optional