diff --git a/fleetconfig-controller/Makefile b/fleetconfig-controller/Makefile index 563035a1..25014390 100644 --- a/fleetconfig-controller/Makefile +++ b/fleetconfig-controller/Makefile @@ -83,9 +83,8 @@ helm-doc-gen: helmdoc ## Generate helm chart README.md readme-generator -v charts/fleetconfig-controller/values.yaml -r charts/fleetconfig-controller/README.md .PHONY: manifests -manifests: kustomize controller-gen ## Generate CustomResourceDefinition and WebhookConfiguration objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases - $(KUSTOMIZE) build config/crd -o charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io-crds.yaml +manifests: controller-gen ## Generate CustomResourceDefinition and WebhookConfiguration objects. + $(CONTROLLER_GEN) webhook crd paths="./..." output:crd:artifacts:config=charts/fleetconfig-controller/crds ./hack/install_crds.sh ##@ Testing Targets @@ -142,9 +141,9 @@ ifndef ignore-not-found endif .PHONY: install-crds -install-crds: manifests kustomize ## Install CRDs. - $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - +install-crds: manifests ## Install CRDs. + find charts/fleetconfig-controller/crds/ -name "fleetconfig.open-cluster-management.*.yaml" -exec $(KUBECTL) apply -f {} \; .PHONY: uninstall-crds -uninstall-crds: manifests kustomize ## Uninstall CRDs. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - +uninstall-crds: manifests ## Uninstall CRDs. Call with ignore-not-found=true to ignore resource not found errors during deletion. + find charts/fleetconfig-controller/crds/ -name "fleetconfig.open-cluster-management.*.yaml" -exec $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f {} \; diff --git a/fleetconfig-controller/PROJECT b/fleetconfig-controller/PROJECT new file mode 100644 index 00000000..e93ba4a2 --- /dev/null +++ b/fleetconfig-controller/PROJECT @@ -0,0 +1,42 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +cliVersion: v4.7.1 +layout: +- go.kubebuilder.io/v4 +projectName: fleetconfig-controller +repo: github.com/open-cluster-management-io/lab/fleetconfig-controller +resources: +- api: + crdVersion: v1 + namespaced: true + group: fleetconfig.open-cluster-management.io + kind: FleetConfig + path: github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 +- api: + crdVersion: v1 + group: fleetconfig.open-cluster-management.io + kind: Hub + path: github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1 + version: v1beta1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 +- api: + crdVersion: v1 + group: fleetconfig.open-cluster-management.io + kind: Spoke + path: github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1 + version: v1beta1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 +version: "3" diff --git a/fleetconfig-controller/api/v1alpha1/fleetconfig_types.go b/fleetconfig-controller/api/v1alpha1/fleetconfig_types.go index 6621d80f..96e8f49f 100644 --- a/fleetconfig-controller/api/v1alpha1/fleetconfig_types.go +++ b/fleetconfig-controller/api/v1alpha1/fleetconfig_types.go @@ -168,6 +168,9 @@ func (c Condition) Equal(other Condition) bool { c.Reason == other.Reason && c.Message == other.Message } +// +kubebuilder:object:root=false +// +kubebuilder:skipversion + // Hub provides specifications for an OCM hub cluster. type Hub struct { // APIServer is the API server URL for the Hub cluster. If provided, spokes clusters will @@ -339,6 +342,9 @@ type ISpoke interface { var _ ISpoke = &Spoke{} var _ ISpoke = &JoinedSpoke{} +// +kubebuilder:object:root=false +// +kubebuilder:skipversion + // Spoke provides specifications for joining and potentially upgrading spokes. type Spoke struct { // The name of the spoke cluster. diff --git a/fleetconfig-controller/api/v1alpha1/webhook_suite_test.go b/fleetconfig-controller/api/v1alpha1/webhook_suite_test.go index a437fd28..d183795e 100644 --- a/fleetconfig-controller/api/v1alpha1/webhook_suite_test.go +++ b/fleetconfig-controller/api/v1alpha1/webhook_suite_test.go @@ -74,9 +74,12 @@ var _ = BeforeSuite(func() { err = AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + root, err := test.GetProjectDir() + Expect(err).NotTo(HaveOccurred()) + By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{filepath.Join(root, "charts", "fleetconfig-controller", "crds")}, ErrorIfCRDPathMissing: false, WebhookInstallOptions: envtest.WebhookInstallOptions{ Paths: []string{filepath.Join("..", "..", "config", "webhook")}, diff --git a/fleetconfig-controller/api/v1beta1/groupversion_info.go b/fleetconfig-controller/api/v1beta1/groupversion_info.go new file mode 100644 index 00000000..d2d94a76 --- /dev/null +++ b/fleetconfig-controller/api/v1beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains API Schema definitions for the v1beta1 API group. +// +kubebuilder:object:generate=true +// +groupName=fleetconfig.open-cluster-management.io +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "fleetconfig.open-cluster-management.io", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/fleetconfig-controller/api/v1beta1/hub_types.go b/fleetconfig-controller/api/v1beta1/hub_types.go new file mode 100644 index 00000000..7705aa07 --- /dev/null +++ b/fleetconfig-controller/api/v1beta1/hub_types.go @@ -0,0 +1,76 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// HubSpec defines the desired state of Hub +type HubSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + // The following markers will use OpenAPI v3 schema to validate the value + // More info: https://book.kubebuilder.io/reference/markers/crd-validation.html + + // foo is an example field of Hub. Edit hub_types.go to remove/update + // +optional + Foo *string `json:"foo,omitempty"` +} + +// HubStatus defines the observed state of Hub. +type HubStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster + +// Hub is the Schema for the hubs API +type Hub struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of Hub + // +required + Spec HubSpec `json:"spec"` + + // status defines the observed state of Hub + // +optional + Status HubStatus `json:"status,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// HubList contains a list of Hub +type HubList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Hub `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Hub{}, &HubList{}) +} diff --git a/fleetconfig-controller/api/v1beta1/spoke_types.go b/fleetconfig-controller/api/v1beta1/spoke_types.go new file mode 100644 index 00000000..9365caa0 --- /dev/null +++ b/fleetconfig-controller/api/v1beta1/spoke_types.go @@ -0,0 +1,76 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// SpokeSpec defines the desired state of Spoke +type SpokeSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + // The following markers will use OpenAPI v3 schema to validate the value + // More info: https://book.kubebuilder.io/reference/markers/crd-validation.html + + // foo is an example field of Spoke. Edit spoke_types.go to remove/update + // +optional + Foo *string `json:"foo,omitempty"` +} + +// SpokeStatus defines the observed state of Spoke. +type SpokeStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=spokes,scope=Cluster + +// Spoke is the Schema for the spokes API +type Spoke struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty,omitzero"` + + // spec defines the desired state of Spoke + // +required + Spec SpokeSpec `json:"spec"` + + // status defines the observed state of Spoke + // +optional + Status SpokeStatus `json:"status,omitempty,omitzero"` +} + +// +kubebuilder:object:root=true + +// SpokeList contains a list of Spoke +type SpokeList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Spoke `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Spoke{}, &SpokeList{}) +} diff --git a/fleetconfig-controller/api/v1beta1/zz_generated.deepcopy.go b/fleetconfig-controller/api/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 00000000..3f7c92e6 --- /dev/null +++ b/fleetconfig-controller/api/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,213 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Hub) DeepCopyInto(out *Hub) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Hub. +func (in *Hub) DeepCopy() *Hub { + if in == nil { + return nil + } + out := new(Hub) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Hub) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HubList) DeepCopyInto(out *HubList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Hub, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HubList. +func (in *HubList) DeepCopy() *HubList { + if in == nil { + return nil + } + out := new(HubList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HubList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HubSpec) DeepCopyInto(out *HubSpec) { + *out = *in + if in.Foo != nil { + in, out := &in.Foo, &out.Foo + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HubSpec. +func (in *HubSpec) DeepCopy() *HubSpec { + if in == nil { + return nil + } + out := new(HubSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HubStatus) DeepCopyInto(out *HubStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HubStatus. +func (in *HubStatus) DeepCopy() *HubStatus { + if in == nil { + return nil + } + out := new(HubStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Spoke) DeepCopyInto(out *Spoke) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spoke. +func (in *Spoke) DeepCopy() *Spoke { + if in == nil { + return nil + } + out := new(Spoke) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Spoke) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SpokeList) DeepCopyInto(out *SpokeList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Spoke, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpokeList. +func (in *SpokeList) DeepCopy() *SpokeList { + if in == nil { + return nil + } + out := new(SpokeList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SpokeList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SpokeSpec) DeepCopyInto(out *SpokeSpec) { + *out = *in + if in.Foo != nil { + in, out := &in.Foo, &out.Foo + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpokeSpec. +func (in *SpokeSpec) DeepCopy() *SpokeSpec { + if in == nil { + return nil + } + out := new(SpokeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SpokeStatus) DeepCopyInto(out *SpokeStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpokeStatus. +func (in *SpokeStatus) DeepCopy() *SpokeStatus { + if in == nil { + return nil + } + out := new(SpokeStatus) + in.DeepCopyInto(out) + return out +} diff --git a/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io-crds.yaml b/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io-crds.yaml deleted file mode 100644 index 017460d7..00000000 --- a/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io-crds.yaml +++ /dev/null @@ -1,2762 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME - controller-gen.kubebuilder.io/version: v0.17.0 - name: fleetconfigs.fleetconfig.open-cluster-management.io -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - name: webhook-service - namespace: system - path: /convert - conversionReviewVersions: - - v1 - group: fleetconfig.open-cluster-management.io - names: - kind: FleetConfig - listKind: FleetConfigList - plural: fleetconfigs - singular: fleetconfig - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.phase - name: PHASE - type: string - - jsonPath: .metadata.creationTimestamp - name: AGE - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: FleetConfig is the Schema for the fleetconfigs API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: FleetConfigSpec defines the desired state of FleetConfig. - properties: - addOnConfigs: - items: - description: AddOnConfig is the configuration of a custom AddOn - that can be installed on a cluster. - properties: - clusterRoleBinding: - description: The rolebinding to the clusterrole in the cluster - namespace for the addon agent - type: string - hubRegistration: - default: false - description: Enable the agent to register to the hub cluster. - Optional, defaults to false. - type: boolean - name: - description: The name of the add-on. - type: string - overwrite: - default: false - description: Whether to overwrite the add-on if it already exists. - Optional, defaults to false. - type: boolean - version: - default: v0.0.1 - description: The add-on version. Optional, defaults to "v0.0.1" - type: string - required: - - name - type: object - type: array - hub: - description: Hub provides specifications for an OCM hub cluster. - properties: - apiServer: - description: |- - APIServer is the API server URL for the Hub cluster. If provided, spokes clusters will - join the hub using this API server instead of the one in the bootstrap kubeconfig. - Spoke clusters with ForceInternalEndpointLookup set to true will ignore this field. - type: string - ca: - description: Hub cluster CA certificate, optional - type: string - clusterManager: - description: ClusterManager configuration. - properties: - featureGates: - default: AddonManagement=true - description: |- - A set of comma-separated pairs of the form 'key1=value1,key2=value2' that describe feature gates for alpha/experimental features. - Options are: - - AddonManagement (ALPHA - default=true) - - AllAlpha (ALPHA - default=false) - - AllBeta (BETA - default=false) - - CloudEventsDrivers (ALPHA - default=false) - - DefaultClusterSet (ALPHA - default=false) - - ManagedClusterAutoApproval (ALPHA - default=false) - - ManifestWorkReplicaSet (ALPHA - default=false) - - NilExecutorValidating (ALPHA - default=false) - - ResourceCleanup (BETA - default=true) - - V1beta1CSRAPICompatibility (ALPHA - default=false) - type: string - purgeOperator: - default: true - description: |- - If set, the cluster manager operator will be purged and the open-cluster-management namespace deleted - when the FleetConfig CR is deleted. - type: boolean - resources: - default: {} - description: Resource specifications for all clustermanager-managed - containers. - properties: - limits: - description: The resource limits of all the containers - managed by the Cluster Manager or Klusterlet operators. - properties: - cpu: - description: The number of CPU units to request, e.g., - '800m'. - type: string - memory: - description: The amount of memory to request, e.g., - '8Gi'. - type: string - type: object - qosClass: - default: Default - description: |- - The resource QoS class of all the containers managed by the Cluster Manager or Klusterlet operators. - One of Default, BestEffort or ResourceRequirement. - enum: - - Default - - BestEffort - - ResourceRequirement - type: string - requests: - description: The resource requests of all the containers - managed by the Cluster Manager or Klusterlet operators. - properties: - cpu: - description: The number of CPU units to request, e.g., - '800m'. - type: string - memory: - description: The amount of memory to request, e.g., - '8Gi'. - type: string - type: object - type: object - source: - default: {} - description: Version and image registry details for the cluster - manager. - properties: - bundleVersion: - default: default - description: |- - The version of predefined compatible image versions (e.g. v0.6.0). Defaults to the latest released version. - You can also set "latest" to install the latest development version. - type: string - registry: - default: quay.io/open-cluster-management - description: The name of the image registry serving OCM - images, which will be used for all OCM components." - type: string - type: object - useBootstrapToken: - description: If set, the bootstrap token will used instead - of a service account token. - type: boolean - type: object - createNamespace: - default: true - description: If true, create open-cluster-management namespace, - otherwise use existing one. - type: boolean - force: - description: If set, the hub will be reinitialized. - type: boolean - kubeconfig: - description: Kubeconfig details for the Hub cluster. - properties: - context: - description: The context to use in the kubeconfig file. - type: string - inCluster: - description: |- - If set, the kubeconfig will be read from the cluster. - Only applicable for same-cluster operations. - Defaults to false. - type: boolean - secretReference: - description: |- - A reference to an existing secret containing a kubeconfig. - Must be provided for remote clusters. - For same-cluster, must be provided unless InCluster is set to true. - properties: - kubeconfigKey: - default: kubeconfig - description: The map key to access the kubeconfig. Defaults - to 'kubeconfig'. - type: string - name: - description: The name of the secret. - type: string - namespace: - description: The namespace the secret is in. - type: string - required: - - name - - namespace - type: object - type: object - singleton: - description: |- - Singleton control plane configuration. If provided, deploy a singleton control plane instead of clustermanager. - This is an alpha stage flag. - properties: - helm: - description: |- - Helm configuration for the multicluster-controlplane Helm chart. - For now https://open-cluster-management.io/helm-charts/ocm/multicluster-controlplane is always used - no private registry support. - See: https://github.com/open-cluster-management-io/multicluster-controlplane/blob/main/charts/multicluster-controlplane/values.yaml - properties: - set: - description: Comma-separated Helm values, e.g., key1=val1,key2=val2. - items: - type: string - type: array - setJson: - description: Comma-separated Helm JSON values, e.g., key1=jsonval1,key2=jsonval2. - items: - type: string - type: array - setLiteral: - description: Comma-separated Helm literal STRING values. - items: - type: string - type: array - setString: - description: Comma-separated Helm STRING values, e.g., - key1=val1,key2=val2. - items: - type: string - type: array - values: - description: Raw, YAML-formatted Helm values. - type: string - type: object - name: - default: singleton-controlplane - description: The name of the singleton control plane. - type: string - type: object - required: - - kubeconfig - type: object - hubAddOns: - items: - description: HubAddOn is the configuration for enabling a built-in - AddOn. - properties: - createNamespace: - default: false - description: Whether or not the selected namespace should be - created. If left empty, defaults to false. - type: boolean - installNamespace: - description: The namespace to install the add-on in. If left - empty, installs into the "open-cluster-management-addon" namespace. - type: string - name: - description: Name is the name of the HubAddOn. - enum: - - argocd - - governance-policy-framework - type: string - required: - - name - type: object - type: array - logVerbosity: - default: 0 - description: LogVerbosity is the verbosity of the logs. - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - type: integer - registrationAuth: - default: {} - description: RegistrationAuth provides specifications for registration - authentication. - properties: - autoApprovedARNPatterns: - description: |- - List of AWS EKS ARN patterns so any EKS clusters with these patterns will be auto accepted to join with hub cluster. - Example pattern: "arn:aws:eks:us-west-2:123456789013:cluster/.*" - items: - type: string - type: array - driver: - default: csr - description: |- - The registration authentication driver to use. - Options are: - - csr: Use the default CSR-based registration authentication. - - awsirsa: Use AWS IAM Role for Service Accounts (IRSA) registration authentication. - The set of valid options is open for extension. - enum: - - csr - - awsirsa - type: string - hubClusterARN: - description: The Hub cluster ARN for awsirsa registration authentication. - Required when Type is awsirsa, otherwise ignored. - type: string - type: object - spokes: - items: - description: Spoke provides specifications for joining and potentially - upgrading spokes. - properties: - addOns: - description: AddOns are the add-ons to enable for the spoke - cluster. - items: - description: AddOn enables add-on installation on the cluster. - properties: - annotations: - additionalProperties: - type: string - description: Annotations to apply to the add-on. - type: object - configName: - description: The name of the add-on being enabled. Must - match one of the AddOnConfigs or HubAddOns names. - type: string - installNamespace: - description: The namespace to install the add-on in. If - left empty, installs into the "open-cluster-management-addon" - namespace. - type: string - required: - - configName - type: object - type: array - clusterARN: - description: |- - ClusterARN is the ARN of the spoke cluster. - This field is optionally used for AWS IRSA registration authentication. - type: string - createNamespace: - default: true - description: |- - If true, create open-cluster-management namespace and agent namespace (open-cluster-management-agent for Default mode, - for Hosted mode), otherwise use existing one. - type: boolean - klusterlet: - default: {} - description: Klusterlet configuration. - properties: - annotations: - additionalProperties: - type: string - description: |- - Annotations to apply to the spoke cluster. If not present, the 'agent.open-cluster-management.io/' prefix is added to each key. - Each annotation is added to klusterlet.spec.registrationConfiguration.clusterAnnotations on the spoke and subsequently to the ManagedCluster on the hub. - type: object - featureGates: - default: AddonManagement=true,ClusterClaim=true - description: |- - A set of comma-separated pairs of the form 'key1=value1,key2=value2' that describe feature gates for alpha/experimental features. - Options are: - - AddonManagement (ALPHA - default=true) - - AllAlpha (ALPHA - default=false) - - AllBeta (BETA - default=false) - - ClusterClaim (ALPHA - default=true) - - ExecutorValidatingCaches (ALPHA - default=false) - - RawFeedbackJsonString (ALPHA - default=false) - - V1beta1CSRAPICompatibility (ALPHA - default=false) - type: string - forceInternalEndpointLookup: - description: |- - If true, the installed klusterlet agent will start the cluster registration process by looking for the - internal endpoint from the public cluster-info in the Hub cluster instead of using hubApiServer. - type: boolean - forceInternalEndpointLookupManaged: - description: |- - If true, the klusterlet accesses the managed cluster using the internal endpoint from the public - cluster-info in the managed cluster instead of using managedClusterKubeconfig. - type: boolean - managedClusterKubeconfig: - description: External managed cluster kubeconfig, required - if using hosted mode. - properties: - context: - description: The context to use in the kubeconfig file. - type: string - inCluster: - description: |- - If set, the kubeconfig will be read from the cluster. - Only applicable for same-cluster operations. - Defaults to false. - type: boolean - secretReference: - description: |- - A reference to an existing secret containing a kubeconfig. - Must be provided for remote clusters. - For same-cluster, must be provided unless InCluster is set to true. - properties: - kubeconfigKey: - default: kubeconfig - description: The map key to access the kubeconfig. - Defaults to 'kubeconfig'. - type: string - name: - description: The name of the secret. - type: string - namespace: - description: The namespace the secret is in. - type: string - required: - - name - - namespace - type: object - type: object - mode: - default: Default - description: Deployent mode for klusterlet - enum: - - Default - - Hosted - type: string - purgeOperator: - default: true - description: |- - If set, the klusterlet operator will be purged and all open-cluster-management namespaces deleted - when the klusterlet is unjoined from its Hub cluster. - type: boolean - resources: - default: {} - description: Resource specifications for all klusterlet-managed - containers. - properties: - limits: - description: The resource limits of all the containers - managed by the Cluster Manager or Klusterlet operators. - properties: - cpu: - description: The number of CPU units to request, - e.g., '800m'. - type: string - memory: - description: The amount of memory to request, e.g., - '8Gi'. - type: string - type: object - qosClass: - default: Default - description: |- - The resource QoS class of all the containers managed by the Cluster Manager or Klusterlet operators. - One of Default, BestEffort or ResourceRequirement. - enum: - - Default - - BestEffort - - ResourceRequirement - type: string - requests: - description: The resource requests of all the containers - managed by the Cluster Manager or Klusterlet operators. - properties: - cpu: - description: The number of CPU units to request, - e.g., '800m'. - type: string - memory: - description: The amount of memory to request, e.g., - '8Gi'. - type: string - type: object - type: object - singleton: - description: |- - If true, deploy klusterlet in singleton mode, with registration and work agents running in a single pod. - This is an alpha stage flag. - type: boolean - source: - default: {} - description: Version and image registry details for the - klusterlet. - properties: - bundleVersion: - default: default - description: |- - The version of predefined compatible image versions (e.g. v0.6.0). Defaults to the latest released version. - You can also set "latest" to install the latest development version. - type: string - registry: - default: quay.io/open-cluster-management - description: The name of the image registry serving - OCM images, which will be used for all OCM components." - type: string - type: object - values: - description: Values for the klusterlet Helm chart. - properties: - affinity: - description: Affinity is the affinity of the operator - deployment - properties: - nodeAffinity: - description: Describes node affinity scheduling - rules for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector - requirements by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector - requirements by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, - in the range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector - requirements by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector - requirements by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - type: array - x-kubernetes-list-type: atomic - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules - (e.g. co-locate this pod in the same node, zone, - etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added - per-node to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity - term, associated with the corresponding - weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - x-kubernetes-list-type: atomic - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling - rules (e.g. avoid putting this pod in the same - node, zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added - per-node to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity - term, associated with the corresponding - weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions - is a list of label selector - requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the - label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: object - bootstrapHubKubeConfig: - description: BootstrapHubKubeConfig should be the kubeConfig - file of the hub cluster via setting --set-file= optional - type: string - createNamespace: - description: CreateNamespace is used in the render function - to append the release ns in the objects. - type: boolean - enableSyncLabels: - description: EnableSyncLabels is to enable the feature - which can sync the labels from klusterlet to all agent - resources. - type: boolean - externalManagedKubeConfig: - description: |- - ExternalManagedKubeConfig should be the kubeConfig file of the managed cluster via setting --set-file= - only need to set in the hosted mode. optional - type: string - images: - description: Images is the configurations for all images - used in operator deployment and klusterlet CR. - properties: - imageCredentials: - description: |- - The image pull secret name is open-cluster-management-image-pull-credentials. - Please set the userName and password if you use a private image registry. - properties: - createImageCredentials: - type: boolean - dockerConfigJson: - type: string - password: - type: string - userName: - type: string - type: object - imagePullPolicy: - description: ImagePullPolicy is the image pull policy - of operator image. Default is IfNotPresent. - type: string - overrides: - description: |- - Overrides is to override the image of the component, if this is specified, - the registry and tag will be ignored. - properties: - addOnManagerImage: - description: AddOnManagerImage is the image - of the addOnManager component - type: string - operatorImage: - description: OperatorImage is the image of the - operator component. - type: string - placementImage: - description: PlacementImage is the image of - the placement component - type: string - registrationImage: - description: RegistrationImage is the image - of the registration component. - type: string - workImage: - description: WorkImage is the image of the work - component. - type: string - type: object - registry: - description: Registry is registry name must NOT - contain a trailing slash. - type: string - tag: - description: Tag is the operator image tag. - type: string - type: object - klusterlet: - description: Klusterlet is the configuration of klusterlet - CR - properties: - clusterName: - type: string - create: - description: Create determines if create the klusterlet - CR, default is true. - type: boolean - externalServerURLs: - description: |- - ExternalServerURLs represents a list of apiserver urls and ca bundles that is accessible externally - If it is set empty, managed cluster has no externally accessible url that hub cluster can visit. - items: - description: ServerURL represents the apiserver - url and ca bundle that is accessible externally - properties: - caBundle: - description: |- - CABundle is the ca bundle to connect to apiserver of the managed cluster. - System certs are used if it is not set. - format: byte - type: string - url: - description: URL is the url of apiserver endpoint - of the managed cluster. - type: string - required: - - url - type: object - type: array - mode: - description: InstallMode represents the mode of - deploy klusterlet - type: string - name: - type: string - namespace: - type: string - nodePlacement: - description: NodePlacement enables explicit control - over the scheduling of the deployed pods. - properties: - nodeSelector: - additionalProperties: - type: string - description: NodeSelector defines which Nodes - the Pods are scheduled on. The default is - an empty list. - type: object - tolerations: - description: |- - Tolerations are attached by pods to tolerate any taint that matches - the triple using the matching operator . - The default is an empty list. - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - type: object - type: array - type: object - registrationConfiguration: - description: RegistrationConfiguration contains - the configuration of registration - properties: - bootstrapKubeConfigs: - description: |- - BootstrapKubeConfigs defines the ordered list of bootstrap kubeconfigs. The order decides which bootstrap kubeconfig to use first when rebootstrap. - - When the agent loses the connection to the current hub over HubConnectionTimeoutSeconds, or the managedcluster CR - is set `hubAcceptsClient=false` on the hub, the controller marks the related bootstrap kubeconfig as "failed". - - A failed bootstrapkubeconfig won't be used for the duration specified by SkipFailedBootstrapKubeConfigSeconds. - But if the user updates the content of a failed bootstrapkubeconfig, the "failed" mark will be cleared. - properties: - localSecretsConfig: - description: |- - LocalSecretsConfig include a list of secrets that contains the kubeconfigs for ordered bootstrap kubeconifigs. - The secrets must be in the same namespace where the agent controller runs. - properties: - hubConnectionTimeoutSeconds: - default: 600 - description: |- - HubConnectionTimeoutSeconds is used to set the timeout of connecting to the hub cluster. - When agent loses the connection to the hub over the timeout seconds, the agent do a rebootstrap. - By default is 10 mins. - format: int32 - minimum: 180 - type: integer - kubeConfigSecrets: - description: KubeConfigSecrets is a - list of secret names. The secrets - are in the same namespace where the - agent controller runs. - items: - properties: - name: - description: Name is the name - of the secret. - type: string - required: - - name - type: object - type: array - required: - - kubeConfigSecrets - type: object - type: - default: None - description: |- - Type specifies the type of priority bootstrap kubeconfigs. - By default, it is set to None, representing no priority bootstrap kubeconfigs are set. - enum: - - None - - LocalSecrets - type: string - required: - - type - type: object - clientCertExpirationSeconds: - description: |- - clientCertExpirationSeconds represents the seconds of a client certificate to expire. If it is not set or 0, the default - duration seconds will be set by the hub cluster. If the value is larger than the max signing duration seconds set on - the hub cluster, the max signing duration seconds will be set. - format: int32 - type: integer - clusterAnnotations: - additionalProperties: - type: string - description: |- - ClusterAnnotations is annotations with the reserve prefix "agent.open-cluster-management.io" set on - ManagedCluster when creating only, other actors can update it afterwards. - type: object - clusterClaimConfiguration: - description: |- - ClusterClaimConfiguration represents the configuration of ClusterClaim - Effective only when the `ClusterClaim` feature gate is enabled. - properties: - maxCustomClusterClaims: - default: 20 - description: Maximum number of custom ClusterClaims - allowed. - format: int32 - type: integer - reservedClusterClaimSuffixes: - description: Custom suffixes for reserved - ClusterClaims. - items: - maxLength: 64 - minLength: 1 - type: string - maxItems: 10 - type: array - required: - - maxCustomClusterClaims - type: object - featureGates: - description: "FeatureGates represents the list - of feature gates for registration\nIf it is - set empty, default feature gates will be used.\nIf - it is set, featuregate/Foo is an example of - one item in FeatureGates:\n 1. If featuregate/Foo - does not exist, registration-operator will - discard it\n 2. If featuregate/Foo exists - and is false by default. It is now possible - to set featuregate/Foo=[false|true]\n 3. - If featuregate/Foo exists and is true by default. - If a cluster-admin upgrading from 1 to 2 wants - to continue having featuregate/Foo=false,\n - \the can set featuregate/Foo=false before - upgrading. Let's say the cluster-admin wants - featuregate/Foo=false." - items: - properties: - feature: - description: Feature is the key of feature - gate. e.g. featuregate/Foo. - type: string - mode: - default: Disable - description: |- - Mode is either Enable, Disable, "" where "" is Disable by default. - In Enable mode, a valid feature gate `featuregate/Foo` will be set to "--featuregate/Foo=true". - In Disable mode, a valid feature gate `featuregate/Foo` will be set to "--featuregate/Foo=false". - enum: - - Enable - - Disable - type: string - required: - - feature - type: object - type: array - kubeAPIBurst: - default: 100 - description: |- - KubeAPIBurst indicates the maximum burst of the throttle while talking with apiserver on the spoke cluster. - If it is set empty, use the default value: 100 - format: int32 - type: integer - kubeAPIQPS: - default: 50 - description: |- - KubeAPIQPS indicates the maximum QPS while talking with apiserver on the spoke cluster. - If it is set empty, use the default value: 50 - format: int32 - type: integer - registrationDriver: - description: This provides driver details required - to register with hub - properties: - authType: - default: csr - description: Type of the authentication - used by managedcluster to register as - well as pull work from hub. Possible values - are csr and awsirsa. - enum: - - csr - - awsirsa - type: string - awsIrsa: - description: |- - Contain the details required for registering with hub cluster (ie: an EKS cluster) using AWS IAM roles for service account. - This is required only when the authType is awsirsa. - properties: - hubClusterArn: - description: |- - The arn of the hub cluster (ie: an EKS cluster). This will be required to pass information to hub, which hub will use to create IAM identities for this klusterlet. - Example - arn:eks:us-west-2:12345678910:cluster/hub-cluster1. - minLength: 1 - pattern: ^arn:aws:eks:([a-zA-Z0-9-]+):(\d{12}):cluster/([a-zA-Z0-9-]+)$ - type: string - managedClusterArn: - description: |- - The arn of the managed cluster (ie: an EKS cluster). This will be required to generate the md5hash which will be used as a suffix to create IAM role on hub - as well as used by kluslerlet-agent, to assume role suffixed with the md5hash, on startup. - Example - arn:eks:us-west-2:12345678910:cluster/managed-cluster1. - minLength: 1 - pattern: ^arn:aws:eks:([a-zA-Z0-9-]+):(\d{12}):cluster/([a-zA-Z0-9-]+)$ - type: string - required: - - hubClusterArn - - managedClusterArn - type: object - required: - - authType - type: object - type: object - resourceRequirement: - description: |- - ResourceRequirement specify QoS classes of deployments managed by clustermanager. - It applies to all the containers in the deployments. - properties: - resourceRequirements: - description: ResourceRequirements defines resource - requests and limits when Type is ResourceQosClassResourceRequirement - properties: - claims: - description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. - - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. - - This field is immutable. It can only be set for containers. - items: - description: ResourceClaim references - one entry in PodSpec.ResourceClaims. - properties: - name: - description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. - type: string - request: - description: |- - Request is the name chosen for a request in the referenced claim. - If empty, everything from the claim is made available, otherwise - only the result of this request. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Limits describes the maximum amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Requests describes the minimum amount of compute resources required. - If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - type: object - type: - default: Default - enum: - - Default - - BestEffort - - ResourceRequirement - type: string - type: object - workConfiguration: - description: WorkConfiguration contains the configuration - of work - properties: - appliedManifestWorkEvictionGracePeriod: - description: |- - AppliedManifestWorkEvictionGracePeriod is the eviction grace period the work agent will wait before - evicting the AppliedManifestWorks, whose corresponding ManifestWorks are missing on the hub cluster, from - the managed cluster. If not present, the default value of the work agent will be used. - pattern: ^([0-9]+(s|m|h))+$ - type: string - featureGates: - description: "FeatureGates represents the list - of feature gates for work\nIf it is set empty, - default feature gates will be used.\nIf it - is set, featuregate/Foo is an example of one - item in FeatureGates:\n 1. If featuregate/Foo - does not exist, registration-operator will - discard it\n 2. If featuregate/Foo exists - and is false by default. It is now possible - to set featuregate/Foo=[false|true]\n 3. - If featuregate/Foo exists and is true by default. - If a cluster-admin upgrading from 1 to 2 wants - to continue having featuregate/Foo=false,\n - \the can set featuregate/Foo=false before - upgrading. Let's say the cluster-admin wants - featuregate/Foo=false." - items: - properties: - feature: - description: Feature is the key of feature - gate. e.g. featuregate/Foo. - type: string - mode: - default: Disable - description: |- - Mode is either Enable, Disable, "" where "" is Disable by default. - In Enable mode, a valid feature gate `featuregate/Foo` will be set to "--featuregate/Foo=true". - In Disable mode, a valid feature gate `featuregate/Foo` will be set to "--featuregate/Foo=false". - enum: - - Enable - - Disable - type: string - required: - - feature - type: object - type: array - hubKubeAPIBurst: - default: 100 - description: |- - HubKubeAPIBurst indicates the maximum burst of the throttle while talking with apiserver on the hub cluster. - If it is set empty, use the default value: 100 - format: int32 - type: integer - hubKubeAPIQPS: - default: 50 - description: |- - HubKubeAPIQPS indicates the maximum QPS while talking with apiserver on the hub cluster. - If it is set empty, use the default value: 50 - format: int32 - type: integer - kubeAPIBurst: - default: 100 - description: |- - KubeAPIBurst indicates the maximum burst of the throttle while talking with apiserver on the spoke cluster. - If it is set empty, use the default value: 100 - format: int32 - type: integer - kubeAPIQPS: - default: 50 - description: |- - KubeAPIQPS indicates the maximum QPS while talking with apiserver on the spoke cluster. - If it is set empty, use the default value: 50 - format: int32 - type: integer - statusSyncInterval: - description: |- - StatusSyncInterval is the interval for the work agent to check the status of ManifestWorks. - Larger value means less frequent status sync and less api calls to the managed cluster, vice versa. - The value(x) should be: 5s <= x <= 1h. - pattern: ^([0-9]+(s|m|h))+$ - type: string - type: object - type: object - multiHubBootstrapHubKubeConfigs: - description: when MultipleHubs feature gate in klusterlet.registrationConfiguration - is enabled, need to set multiple bootstrap hub kubeConfigs - here. - items: - properties: - kubeConfig: - description: the kubeConfig file of the hub cluster - type: string - name: - description: the boostStrap secret name - type: string - type: object - type: array - noOperator: - description: NoOperator is to only deploy the klusterlet - CR if set true. - type: boolean - nodeSelector: - additionalProperties: - type: string - description: NodeSelector is the nodeSelector of the - operator deployment - type: object - podSecurityContext: - description: PodSecurityContext is the pod SecurityContext - in the operator deployment - properties: - appArmorProfile: - description: |- - appArmorProfile is the AppArmor options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile loaded on the node that should be used. - The profile must be preconfigured on the node to work. - Must match the loaded name of the profile. - Must be set if and only if type is "Localhost". - type: string - type: - description: |- - type indicates which kind of AppArmor profile will be applied. - Valid options are: - Localhost - a profile pre-loaded on the node. - RuntimeDefault - the container runtime's default profile. - Unconfined - no AppArmor enforcement. - type: string - required: - - type - type: object - fsGroup: - description: |- - A special supplemental group that applies to all containers in a pod. - Some volume types allow the Kubelet to change the ownership of that volume - to be owned by the pod: - - 1. The owning GID will be the FSGroup - 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- - - If unset, the Kubelet will not modify the ownership and permissions of any volume. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - fsGroupChangePolicy: - description: |- - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume - before being exposed inside Pod. This field will only apply to - volume types which support fsGroup based ownership(and permissions). - It will have no effect on ephemeral volume types such as: secret, configmaps - and emptydir. - Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. - Note that this field cannot be set when spec.os.name is windows. - type: string - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence - for that container. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxChangePolicy: - description: |- - seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod. - It has no effect on nodes that do not support SELinux or to volumes does not support SELinux. - Valid values are "MountOption" and "Recursive". - - "Recursive" means relabeling of all files on all Pod volumes by the container runtime. - This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node. - - "MountOption" mounts all eligible Pod volumes with `-o context` mount option. - This requires all Pods that share the same volume to use the same SELinux label. - It is not possible to share the same volume among privileged and unprivileged Pods. - Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes - whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their - CSIDriver instance. Other volumes are always re-labelled recursively. - "MountOption" value is allowed only when SELinuxMount feature gate is enabled. - - If not specified and SELinuxMount feature gate is enabled, "MountOption" is used. - If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes - and "Recursive" for all other volumes. - - This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers. - - All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state. - Note that this field cannot be set when spec.os.name is windows. - type: string - seLinuxOptions: - description: |- - The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in SecurityContext. If set in - both SecurityContext and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that - applies to the container. - type: string - role: - description: Role is a SELinux role label that - applies to the container. - type: string - type: - description: Type is a SELinux type label that - applies to the container. - type: string - user: - description: User is a SELinux user label that - applies to the container. - type: string - type: object - seccompProfile: - description: |- - The seccomp options to use by the containers in this pod. - Note that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - required: - - type - type: object - supplementalGroups: - description: |- - A list of groups applied to the first process run in each container, in - addition to the container's primary GID and fsGroup (if specified). If - the SupplementalGroupsPolicy feature is enabled, the - supplementalGroupsPolicy field determines whether these are in addition - to or instead of any group memberships defined in the container image. - If unspecified, no additional groups are added, though group memberships - defined in the container image may still be used, depending on the - supplementalGroupsPolicy field. - Note that this field cannot be set when spec.os.name is windows. - items: - format: int64 - type: integer - type: array - x-kubernetes-list-type: atomic - supplementalGroupsPolicy: - description: |- - Defines how supplemental groups of the first container processes are calculated. - Valid values are "Merge" and "Strict". If not specified, "Merge" is used. - (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled - and the container runtime must implement support for this feature. - Note that this field cannot be set when spec.os.name is windows. - type: string - sysctls: - description: |- - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported - sysctls (by the container runtime) might fail to launch. - Note that this field cannot be set when spec.os.name is windows. - items: - description: Sysctl defines a kernel parameter - to be set - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value - type: object - type: array - x-kubernetes-list-type: atomic - windowsOptions: - description: |- - The Windows specific settings applied to all containers. - If unspecified, the options within a container's SecurityContext will be used. - If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: |- - GMSACredentialSpec is where the GMSA admission webhook - (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the - GMSA credential spec named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name - of the GMSA credential spec to use. - type: string - hostProcess: - description: |- - HostProcess determines if a container should be run as a 'Host Process' container. - All of a Pod's containers must have the same effective HostProcess value - (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). - In addition, if HostProcess is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: |- - The UserName in Windows to run the entrypoint of the container process. - Defaults to the user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - priorityClassName: - description: PriorityClassName is the name of the PriorityClass - that will be used by the deployed klusterlet agent - and operator. - type: string - replicaCount: - description: ReplicaCount is the replicas for the klusterlet - operator deployment. - type: integer - resources: - description: Resources is the resource requirements - of the operator deployment - properties: - claims: - description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. - - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. - - This field is immutable. It can only be set for containers. - items: - description: ResourceClaim references one entry - in PodSpec.ResourceClaims. - properties: - name: - description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. - type: string - request: - description: |- - Request is the name chosen for a request in the referenced claim. - If empty, everything from the claim is made available, otherwise - only the result of this request. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Limits describes the maximum amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Requests describes the minimum amount of compute resources required. - If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - type: object - securityContext: - description: SecurityContext is the container SecurityContext - in operator deployment - properties: - allowPrivilegeEscalation: - description: |- - AllowPrivilegeEscalation controls whether a process can gain more - privileges than its parent process. This bool directly controls if - the no_new_privs flag will be set on the container process. - AllowPrivilegeEscalation is true always when the container is: - 1) run as Privileged - 2) has CAP_SYS_ADMIN - Note that this field cannot be set when spec.os.name is windows. - type: boolean - appArmorProfile: - description: |- - appArmorProfile is the AppArmor options to use by this container. If set, this profile - overrides the pod's appArmorProfile. - Note that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile loaded on the node that should be used. - The profile must be preconfigured on the node to work. - Must match the loaded name of the profile. - Must be set if and only if type is "Localhost". - type: string - type: - description: |- - type indicates which kind of AppArmor profile will be applied. - Valid options are: - Localhost - a profile pre-loaded on the node. - RuntimeDefault - the container runtime's default profile. - Unconfined - no AppArmor enforcement. - type: string - required: - - type - type: object - capabilities: - description: |- - The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the container runtime. - Note that this field cannot be set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - x-kubernetes-list-type: atomic - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - x-kubernetes-list-type: atomic - type: object - privileged: - description: |- - Run container in privileged mode. - Processes in privileged containers are essentially equivalent to root on the host. - Defaults to false. - Note that this field cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: |- - procMount denotes the type of proc mount to use for the containers. - The default value is Default which uses the container runtime defaults for - readonly paths and masked paths. - This requires the ProcMountType feature flag to be enabled. - Note that this field cannot be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: |- - Whether this container has a read-only root filesystem. - Default is false. - Note that this field cannot be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: |- - The GID to run the entrypoint of the container process. - Uses runtime default if unset. - May also be set in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: |- - Indicates that the container must run as a non-root user. - If true, the Kubelet will validate the image at runtime to ensure that it - does not run as UID 0 (root) and fail to start the container if it does. - If unset or false, no such validation will be performed. - May also be set in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: |- - The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: |- - The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random SELinux context for each - container. May also be set in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that - applies to the container. - type: string - role: - description: Role is a SELinux role label that - applies to the container. - type: string - type: - description: Type is a SELinux type label that - applies to the container. - type: string - user: - description: User is a SELinux user label that - applies to the container. - type: string - type: object - seccompProfile: - description: |- - The seccomp options to use by this container. If seccomp options are - provided at both the pod & container level, the container options - override the pod options. - Note that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: |- - localhostProfile indicates a profile defined in a file on the node should be used. - The profile must be preconfigured on the node to work. - Must be a descending path, relative to the kubelet's configured seccomp profile location. - Must be set if type is "Localhost". Must NOT be set for any other type. - type: string - type: - description: |- - type indicates which kind of seccomp profile will be applied. - Valid options are: - - Localhost - a profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile should be used. - Unconfined - no profile should be applied. - type: string - required: - - type - type: object - windowsOptions: - description: |- - The Windows specific settings applied to all containers. - If unspecified, the options from the PodSecurityContext will be used. - If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: |- - GMSACredentialSpec is where the GMSA admission webhook - (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the - GMSA credential spec named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name - of the GMSA credential spec to use. - type: string - hostProcess: - description: |- - HostProcess determines if a container should be run as a 'Host Process' container. - All of a Pod's containers must have the same effective HostProcess value - (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). - In addition, if HostProcess is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: |- - The UserName in Windows to run the entrypoint of the container process. - Defaults to the user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - tolerations: - description: Tolerations is the tolerations of the operator - deployment - items: - description: |- - The pod this Toleration is attached to tolerates any taint that matches - the triple using the matching operator . - properties: - effect: - description: |- - Effect indicates the taint effect to match. Empty means match all taint effects. - When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: |- - Key is the taint key that the toleration applies to. Empty means match all taint keys. - If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: |- - Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod can - tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: |- - TolerationSeconds represents the period of time the toleration (which must be - of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, - it is not set, which means tolerate the taint forever (do not evict). Zero and - negative values will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: |- - Value is the taint value the toleration matches to. - If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - type: object - type: array - type: object - type: object - kubeconfig: - description: Kubeconfig details for the Spoke cluster. - properties: - context: - description: The context to use in the kubeconfig file. - type: string - inCluster: - description: |- - If set, the kubeconfig will be read from the cluster. - Only applicable for same-cluster operations. - Defaults to false. - type: boolean - secretReference: - description: |- - A reference to an existing secret containing a kubeconfig. - Must be provided for remote clusters. - For same-cluster, must be provided unless InCluster is set to true. - properties: - kubeconfigKey: - default: kubeconfig - description: The map key to access the kubeconfig. Defaults - to 'kubeconfig'. - type: string - name: - description: The name of the secret. - type: string - namespace: - description: The namespace the secret is in. - type: string - required: - - name - - namespace - type: object - type: object - name: - description: The name of the spoke cluster. - maxLength: 63 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - proxyCa: - description: Proxy CA certificate, optional - type: string - proxyUrl: - description: URL of a forward proxy server used by agents to - connect to the Hub cluster. - type: string - syncLabels: - description: If true, sync the labels from klusterlet to all - agent resources. - type: boolean - required: - - kubeconfig - - name - type: object - type: array - timeout: - default: 300 - description: Timeout is the timeout in seconds for all clusteradm - operations, including init, accept, join, upgrade, etc. - type: integer - required: - - hub - - spokes - type: object - status: - description: FleetConfigStatus defines the observed state of FleetConfig. - properties: - conditions: - items: - description: Condition describes the state of a FleetConfig. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - wantStatus: - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - - wantStatus - type: object - type: array - installedHubAddOns: - items: - description: InstalledHubAddOn tracks metadata for each hubAddon - that is successfully installed on the hub. - properties: - bundleVersion: - description: BundleVersion is the bundle version used when installing - the addon. - type: string - name: - description: Name is the name of the addon. - type: string - namespace: - description: Namespace is the namespace that the addon was installed - into. - type: string - required: - - bundleVersion - - name - type: object - type: array - joinedSpokes: - items: - description: JoinedSpoke represents a spoke that has been joined - to a hub. - properties: - enabledAddons: - default: [] - description: EnabledAddons is the list of addons that are currently - enabled for the cluster. - items: - type: string - type: array - kubeconfig: - description: Kubeconfig details for the Spoke cluster. - properties: - context: - description: The context to use in the kubeconfig file. - type: string - inCluster: - description: |- - If set, the kubeconfig will be read from the cluster. - Only applicable for same-cluster operations. - Defaults to false. - type: boolean - secretReference: - description: |- - A reference to an existing secret containing a kubeconfig. - Must be provided for remote clusters. - For same-cluster, must be provided unless InCluster is set to true. - properties: - kubeconfigKey: - default: kubeconfig - description: The map key to access the kubeconfig. Defaults - to 'kubeconfig'. - type: string - name: - description: The name of the secret. - type: string - namespace: - description: The namespace the secret is in. - type: string - required: - - name - - namespace - type: object - type: object - name: - description: The name of the spoke cluster. - type: string - purgeKlusterletOperator: - default: true - description: |- - If set, the klusterlet operator will be purged and all open-cluster-management namespaces deleted - when the klusterlet is unjoined from its Hub cluster. - type: boolean - required: - - kubeconfig - - name - type: object - type: array - phase: - type: string - type: object - required: - - metadata - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/fleetconfig-controller/config/crd/bases/fleetconfig.open-cluster-management.io_fleetconfigs.yaml b/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_fleetconfigs.yaml similarity index 100% rename from fleetconfig-controller/config/crd/bases/fleetconfig.open-cluster-management.io_fleetconfigs.yaml rename to fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_fleetconfigs.yaml diff --git a/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_hubs.yaml b/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_hubs.yaml new file mode 100644 index 00000000..2b9548a4 --- /dev/null +++ b/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_hubs.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.0 + name: hubs.fleetconfig.open-cluster-management.io +spec: + group: fleetconfig.open-cluster-management.io + names: + kind: Hub + listKind: HubList + plural: hubs + singular: hub + scope: Cluster + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: Hub is the Schema for the hubs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec defines the desired state of Hub + properties: + foo: + description: foo is an example field of Hub. Edit hub_types.go to + remove/update + type: string + type: object + status: + description: status defines the observed state of Hub + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_spokes.yaml b/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_spokes.yaml new file mode 100644 index 00000000..25b75c85 --- /dev/null +++ b/fleetconfig-controller/charts/fleetconfig-controller/crds/fleetconfig.open-cluster-management.io_spokes.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.17.0 + name: spokes.fleetconfig.open-cluster-management.io +spec: + group: fleetconfig.open-cluster-management.io + names: + kind: Spoke + listKind: SpokeList + plural: spokes + singular: spoke + scope: Cluster + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: Spoke is the Schema for the spokes API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec defines the desired state of Spoke + properties: + foo: + description: foo is an example field of Spoke. Edit spoke_types.go + to remove/update + type: string + type: object + status: + description: status defines the observed state of Spoke + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/fleetconfig-controller/charts/fleetconfig-controller/templates/rbac/manager-rbac.yaml b/fleetconfig-controller/charts/fleetconfig-controller/templates/rbac/manager-rbac.yaml index ce0f3ccc..b3bf5686 100644 --- a/fleetconfig-controller/charts/fleetconfig-controller/templates/rbac/manager-rbac.yaml +++ b/fleetconfig-controller/charts/fleetconfig-controller/templates/rbac/manager-rbac.yaml @@ -37,10 +37,10 @@ rules: # Required for managing fleetconfig resources - apiGroups: ["fleetconfig.open-cluster-management.io"] - resources: ["fleetconfigs"] + resources: ["fleetconfigs", "hubs", "spokes"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["fleetconfig.open-cluster-management.io"] - resources: ["fleetconfigs/status"] + resources: ["fleetconfigs/status", "hubs/status", "spokes/status"] verbs: ["update", "patch"] # Required for handling OCM cluster API resources (ManagedClusters) diff --git a/fleetconfig-controller/cmd/main.go b/fleetconfig-controller/cmd/main.go index db52eac1..4c362db0 100644 --- a/fleetconfig-controller/cmd/main.go +++ b/fleetconfig-controller/cmd/main.go @@ -35,8 +35,11 @@ import ( metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" - v1alpha1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1alpha1" - "github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/controller" + apiv1alpha1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1alpha1" + apiv1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" + controllerv1alpha1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/controller/v1alpha1" + controllerv1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/controller/v1beta1" + webhookv1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/webhook/v1beta1" // +kubebuilder:scaffold:imports ) @@ -48,7 +51,8 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(apiv1alpha1.AddToScheme(scheme)) + utilruntime.Must(apiv1beta1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -132,7 +136,7 @@ func main() { os.Exit(1) } - if err = (&controller.FleetConfigReconciler{ + if err = (&controllerv1alpha1.FleetConfigReconciler{ Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("FleetConfig"), Scheme: mgr.GetScheme(), @@ -140,12 +144,38 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "FleetConfig") os.Exit(1) } - if useWebhook { - if err = v1alpha1.SetupFleetConfigWebhookWithManager(mgr); err != nil { + + if err := (&controllerv1beta1.HubReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Hub") + os.Exit(1) + } + if err := (&controllerv1beta1.SpokeReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Spoke") + os.Exit(1) + } + + // nolint:goconst + if useWebhook || os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err = apiv1alpha1.SetupFleetConfigWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "FleetConfig") os.Exit(1) } + if err := webhookv1beta1.SetupSpokeWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Spoke") + os.Exit(1) + } + if err := webhookv1beta1.SetupHubWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Hub") + os.Exit(1) + } } + // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/fleetconfig-controller/config/crd/kustomization.yaml b/fleetconfig-controller/config/crd/kustomization.yaml deleted file mode 100644 index b5261229..00000000 --- a/fleetconfig-controller/config/crd/kustomization.yaml +++ /dev/null @@ -1,9 +0,0 @@ -resources: -- bases/fleetconfig.open-cluster-management.io_fleetconfigs.yaml - -patches: -- path: patches/webhook_in_fleetconfigs.yaml -- path: patches/cainjection_in_fleetconfigs.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/fleetconfig-controller/config/crd/kustomizeconfig.yaml b/fleetconfig-controller/config/crd/kustomizeconfig.yaml deleted file mode 100644 index ec5c150a..00000000 --- a/fleetconfig-controller/config/crd/kustomizeconfig.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# This file is for teaching kustomize how to substitute name and namespace reference in CRD -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: CustomResourceDefinition - version: v1 - group: apiextensions.k8s.io - path: spec/conversion/webhook/clientConfig/service/name - -namespace: -- kind: CustomResourceDefinition - version: v1 - group: apiextensions.k8s.io - path: spec/conversion/webhook/clientConfig/service/namespace - create: false - -varReference: -- path: metadata/annotations diff --git a/fleetconfig-controller/config/crd/patches/cainjection_in_fleetconfigs.yaml b/fleetconfig-controller/config/crd/patches/cainjection_in_fleetconfigs.yaml deleted file mode 100644 index dd39e63f..00000000 --- a/fleetconfig-controller/config/crd/patches/cainjection_in_fleetconfigs.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME - name: fleetconfigs.fleetconfig.open-cluster-management.io diff --git a/fleetconfig-controller/config/crd/patches/webhook_in_fleetconfigs.yaml b/fleetconfig-controller/config/crd/patches/webhook_in_fleetconfigs.yaml deleted file mode 100644 index fff06c1f..00000000 --- a/fleetconfig-controller/config/crd/patches/webhook_in_fleetconfigs.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: fleetconfigs.fleetconfig.open-cluster-management.io -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/fleetconfig-controller/config/samples/fleetconfig_invalid_v1alpha1.yaml b/fleetconfig-controller/config/samples/fleetconfig_invalid_v1alpha1.yaml deleted file mode 100644 index c583fa34..00000000 --- a/fleetconfig-controller/config/samples/fleetconfig_invalid_v1alpha1.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: fleetconfig.open-cluster-management.io/v1alpha1 -kind: FleetConfig -metadata: - labels: - app.kubernetes.io/name: fleetconfig-controller - app.kubernetes.io/managed-by: kustomize - name: fleetconfig-sample -spec: - hub: - kubeconfig: - inCluster: true - spokes: - - name: hub-as-spoke - kubeconfig: - inCluster: true diff --git a/fleetconfig-controller/config/samples/fleetconfig_singleton_v1alpha1.yaml b/fleetconfig-controller/config/samples/fleetconfig_singleton_v1alpha1.yaml deleted file mode 100644 index dc53989c..00000000 --- a/fleetconfig-controller/config/samples/fleetconfig_singleton_v1alpha1.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: fleetconfig.open-cluster-management.io/v1alpha1 -kind: FleetConfig -metadata: - labels: - app.kubernetes.io/name: fleetconfig-controller - app.kubernetes.io/managed-by: kustomize - name: fleetconfig-sample -spec: - hub: - singleton: {} - kubeconfig: - inCluster: true - spokes: - - name: hub-as-spoke - klusterlet: - forceInternalEndpointLookup: true - kubeconfig: - inCluster: true diff --git a/fleetconfig-controller/config/samples/fleetconfig_v1alpha1.yaml b/fleetconfig-controller/config/samples/fleetconfig_v1alpha1.yaml deleted file mode 100644 index 47952466..00000000 --- a/fleetconfig-controller/config/samples/fleetconfig_v1alpha1.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: fleetconfig.open-cluster-management.io/v1alpha1 -kind: FleetConfig -metadata: - labels: - app.kubernetes.io/name: fleetconfig-controller - app.kubernetes.io/managed-by: kustomize - name: fleetconfig-sample -spec: - hub: - clusterManager: {} - kubeconfig: - inCluster: true - spokes: - - name: hub-as-spoke - klusterlet: - forceInternalEndpointLookup: true - kubeconfig: - inCluster: true diff --git a/fleetconfig-controller/config/webhook/manifests.yaml b/fleetconfig-controller/config/webhook/manifests.yaml index 8581ad9f..c9715870 100644 --- a/fleetconfig-controller/config/webhook/manifests.yaml +++ b/fleetconfig-controller/config/webhook/manifests.yaml @@ -24,6 +24,46 @@ webhooks: resources: - fleetconfigs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-fleetconfig-open-cluster-management-io-v1beta1-hub + failurePolicy: Fail + name: mhub-v1beta1.kb.io + rules: + - apiGroups: + - fleetconfig.open-cluster-management.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - hubs + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-fleetconfig-open-cluster-management-io-v1beta1-spoke + failurePolicy: Fail + name: mspoke-v1beta1.kb.io + rules: + - apiGroups: + - fleetconfig.open-cluster-management.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - spokes + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -51,3 +91,43 @@ webhooks: resources: - fleetconfigs sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-fleetconfig-open-cluster-management-io-v1beta1-hub + failurePolicy: Fail + name: vhub-v1beta1.kb.io + rules: + - apiGroups: + - fleetconfig.open-cluster-management.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - hubs + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-fleetconfig-open-cluster-management-io-v1beta1-spoke + failurePolicy: Fail + name: vspoke-v1beta1.kb.io + rules: + - apiGroups: + - fleetconfig.open-cluster-management.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - spokes + sideEffects: None diff --git a/fleetconfig-controller/internal/controller/addon.go b/fleetconfig-controller/internal/controller/v1alpha1/addon.go similarity index 99% rename from fleetconfig-controller/internal/controller/addon.go rename to fleetconfig-controller/internal/controller/v1alpha1/addon.go index a23d2a1f..b743e112 100644 --- a/fleetconfig-controller/internal/controller/addon.go +++ b/fleetconfig-controller/internal/controller/v1alpha1/addon.go @@ -1,4 +1,4 @@ -package controller +package v1alpha1 import ( "context" diff --git a/fleetconfig-controller/internal/controller/fleetconfig_controller.go b/fleetconfig-controller/internal/controller/v1alpha1/fleetconfig_controller.go similarity index 99% rename from fleetconfig-controller/internal/controller/fleetconfig_controller.go rename to fleetconfig-controller/internal/controller/v1alpha1/fleetconfig_controller.go index 61dd5dda..357a7c7a 100644 --- a/fleetconfig-controller/internal/controller/fleetconfig_controller.go +++ b/fleetconfig-controller/internal/controller/v1alpha1/fleetconfig_controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controller +package v1alpha1 import ( "context" diff --git a/fleetconfig-controller/internal/controller/fleetconfig_controller_test.go b/fleetconfig-controller/internal/controller/v1alpha1/fleetconfig_controller_test.go similarity index 99% rename from fleetconfig-controller/internal/controller/fleetconfig_controller_test.go rename to fleetconfig-controller/internal/controller/v1alpha1/fleetconfig_controller_test.go index a766c06e..9eec55d3 100644 --- a/fleetconfig-controller/internal/controller/fleetconfig_controller_test.go +++ b/fleetconfig-controller/internal/controller/v1alpha1/fleetconfig_controller_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controller +package v1alpha1 import ( "context" diff --git a/fleetconfig-controller/internal/controller/hub.go b/fleetconfig-controller/internal/controller/v1alpha1/hub.go similarity index 99% rename from fleetconfig-controller/internal/controller/hub.go rename to fleetconfig-controller/internal/controller/v1alpha1/hub.go index 37a87d90..32bcfc3a 100644 --- a/fleetconfig-controller/internal/controller/hub.go +++ b/fleetconfig-controller/internal/controller/v1alpha1/hub.go @@ -1,5 +1,5 @@ -// Package controller contains the main reconciliation logic of fleetconfig-controller -package controller +// Package v1alpha1 contains the main reconciliation logic for fleetconfig-controller's v1alpha1 resources. +package v1alpha1 import ( "context" diff --git a/fleetconfig-controller/internal/controller/spoke.go b/fleetconfig-controller/internal/controller/v1alpha1/spoke.go similarity index 99% rename from fleetconfig-controller/internal/controller/spoke.go rename to fleetconfig-controller/internal/controller/v1alpha1/spoke.go index ef4c1499..4d59bc00 100644 --- a/fleetconfig-controller/internal/controller/spoke.go +++ b/fleetconfig-controller/internal/controller/v1alpha1/spoke.go @@ -1,4 +1,4 @@ -package controller +package v1alpha1 import ( "context" diff --git a/fleetconfig-controller/internal/controller/suite_test.go b/fleetconfig-controller/internal/controller/v1alpha1/suite_test.go similarity index 94% rename from fleetconfig-controller/internal/controller/suite_test.go rename to fleetconfig-controller/internal/controller/v1alpha1/suite_test.go index 3b93cfdf..f1607a8b 100644 --- a/fleetconfig-controller/internal/controller/suite_test.go +++ b/fleetconfig-controller/internal/controller/v1alpha1/suite_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controller +package v1alpha1 import ( "os" @@ -68,10 +68,14 @@ func TestControllers(t *testing.T) { var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + var err error + + root, err := test.GetProjectDir() + Expect(err).NotTo(HaveOccurred()) By("bootstrapping test environment") testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{filepath.Join(root, "charts", "fleetconfig-controller", "crds")}, ErrorIfCRDPathMissing: true, } @@ -81,7 +85,6 @@ var _ = BeforeSuite(func() { testEnv.BinaryAssetsDirectory = kubebuilderAssets } - var err error cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) diff --git a/fleetconfig-controller/internal/controller/v1beta1/hub_controller.go b/fleetconfig-controller/internal/controller/v1beta1/hub_controller.go new file mode 100644 index 00000000..aade9e39 --- /dev/null +++ b/fleetconfig-controller/internal/controller/v1beta1/hub_controller.go @@ -0,0 +1,64 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains the main reconciliation logic for fleetconfig-controller's v1alpha1 resources. +package v1beta1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + v1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" +) + +// HubReconciler reconciles a Hub object +type HubReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=fleetconfig.open-cluster-management.io,resources=hubs,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=fleetconfig.open-cluster-management.io,resources=hubs/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=fleetconfig.open-cluster-management.io,resources=hubs/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Hub object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile +func (r *HubReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) { + _ = logf.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *HubReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1beta1.Hub{}). + Named("hub"). + Complete(r) +} diff --git a/fleetconfig-controller/internal/controller/v1beta1/hub_controller_test.go b/fleetconfig-controller/internal/controller/v1beta1/hub_controller_test.go new file mode 100644 index 00000000..c14d8615 --- /dev/null +++ b/fleetconfig-controller/internal/controller/v1beta1/hub_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" +) + +var _ = Describe("Hub Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + hub := &v1beta1.Hub{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Hub") + err := k8sClient.Get(ctx, typeNamespacedName, hub) + if err != nil && errors.IsNotFound(err) { + resource := &v1beta1.Hub{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &v1beta1.Hub{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Hub") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &HubReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/fleetconfig-controller/internal/controller/v1beta1/spoke_controller.go b/fleetconfig-controller/internal/controller/v1beta1/spoke_controller.go new file mode 100644 index 00000000..43054799 --- /dev/null +++ b/fleetconfig-controller/internal/controller/v1beta1/spoke_controller.go @@ -0,0 +1,63 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + v1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" +) + +// SpokeReconciler reconciles a Spoke object +type SpokeReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=fleetconfig.open-cluster-management.io,resources=spokes,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=fleetconfig.open-cluster-management.io,resources=spokes/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=fleetconfig.open-cluster-management.io,resources=spokes/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Spoke object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile +func (r *SpokeReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) { + _ = logf.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *SpokeReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1beta1.Spoke{}). + Named("spoke"). + Complete(r) +} diff --git a/fleetconfig-controller/internal/controller/v1beta1/spoke_controller_test.go b/fleetconfig-controller/internal/controller/v1beta1/spoke_controller_test.go new file mode 100644 index 00000000..c5ca6675 --- /dev/null +++ b/fleetconfig-controller/internal/controller/v1beta1/spoke_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" +) + +var _ = Describe("Spoke Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + spoke := &v1beta1.Spoke{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Spoke") + err := k8sClient.Get(ctx, typeNamespacedName, spoke) + if err != nil && errors.IsNotFound(err) { + resource := &v1beta1.Spoke{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &v1beta1.Spoke{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Spoke") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &SpokeReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/fleetconfig-controller/internal/controller/v1beta1/suite_test.go b/fleetconfig-controller/internal/controller/v1beta1/suite_test.go new file mode 100644 index 00000000..176976c3 --- /dev/null +++ b/fleetconfig-controller/internal/controller/v1beta1/suite_test.go @@ -0,0 +1,120 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "context" + "os" + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + v1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" + "github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/test" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + ctx context.Context + cancel context.CancelFunc + testEnv *envtest.Environment + cfg *rest.Config + k8sClient client.Client +) + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + var err error + err = v1beta1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + root, err := test.GetProjectDir() + Expect(err).NotTo(HaveOccurred()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join(root, "charts", "fleetconfig-controller", "crds")}, + ErrorIfCRDPathMissing: true, + } + + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/fleetconfig-controller/internal/test/test.go b/fleetconfig-controller/internal/test/test.go index 5c5b6c90..fe64b176 100644 --- a/fleetconfig-controller/internal/test/test.go +++ b/fleetconfig-controller/internal/test/test.go @@ -22,7 +22,11 @@ type Config struct { // LoadConfig centralized test config from disk. func LoadConfig() (*Config, error) { - data, err := os.ReadFile(filepath.Join("..", "..", "hack", "test-config.json")) + root, err := GetProjectDir() + if err != nil { + return nil, fmt.Errorf("failed to load test config: %w", err) + } + data, err := os.ReadFile(filepath.Join(root, "hack", "test-config.json")) if err != nil { return nil, fmt.Errorf("failed to load test config: %w", err) } @@ -66,3 +70,27 @@ func FindEnvTestBinaryDir(c *Config) string { } return "" } + +// GetProjectDir returns the fleetconfig-controller project root directory. +// It works by finding "fleetconfig-controller" in the current working directory path. +func GetProjectDir() (string, error) { + wd, err := os.Getwd() + if err != nil { + return "", err + } + + // Find the fleetconfig-controller directory in the path + parts := strings.Split(wd, string(os.PathSeparator)) + for i, part := range parts { + if part == "fleetconfig-controller" { + // Reconstruct path up to and including fleetconfig-controller + projectPath := strings.Join(parts[:i+1], string(os.PathSeparator)) + if projectPath == "" { + projectPath = string(os.PathSeparator) // Handle root case + } + return projectPath, nil + } + } + + return "", fmt.Errorf("fleetconfig-controller directory not found in path: %s", wd) +} diff --git a/fleetconfig-controller/internal/webhook/v1beta1/hub_webhook.go b/fleetconfig-controller/internal/webhook/v1beta1/hub_webhook.go new file mode 100644 index 00000000..c2df38cf --- /dev/null +++ b/fleetconfig-controller/internal/webhook/v1beta1/hub_webhook.go @@ -0,0 +1,129 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// TODO - remove once hub webhooks are implemented. +// +//nolint:all // Required because of `dupl` between this file and spoke_webhook.go +package v1beta1 + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + fleetconfigopenclustermanagementiov1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" +) + +// nolint:unused +// log is for logging in this package. +var hublog = logf.Log.WithName("hub-resource") + +// SetupHubWebhookWithManager registers the webhook for Hub in the manager. +func SetupHubWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&fleetconfigopenclustermanagementiov1beta1.Hub{}). + WithValidator(&HubCustomValidator{}). + WithDefaulter(&HubCustomDefaulter{}). + Complete() +} + +// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// +kubebuilder:webhook:path=/mutate-fleetconfig-open-cluster-management-io-v1beta1-hub,mutating=true,failurePolicy=fail,sideEffects=None,groups=fleetconfig.open-cluster-management.io,resources=hubs,verbs=create;update,versions=v1beta1,name=mhub-v1beta1.kb.io,admissionReviewVersions=v1 + +// HubCustomDefaulter struct is responsible for setting default values on the custom resource of the +// Kind Hub when those are created or updated. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as it is used only for temporary operations and does not need to be deeply copied. +type HubCustomDefaulter struct { + // TODO(user): Add more fields as needed for defaulting +} + +var _ webhook.CustomDefaulter = &HubCustomDefaulter{} + +// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind Hub. +func (d *HubCustomDefaulter) Default(_ context.Context, obj runtime.Object) error { + hub, ok := obj.(*fleetconfigopenclustermanagementiov1beta1.Hub) + + if !ok { + return fmt.Errorf("expected an Hub object but got %T", obj) + } + hublog.Info("Defaulting for Hub", "name", hub.GetName()) + + // TODO(user): fill in your defaulting logic. + + return nil +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. +// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. +// +kubebuilder:webhook:path=/validate-fleetconfig-open-cluster-management-io-v1beta1-hub,mutating=false,failurePolicy=fail,sideEffects=None,groups=fleetconfig.open-cluster-management.io,resources=hubs,verbs=create;update,versions=v1beta1,name=vhub-v1beta1.kb.io,admissionReviewVersions=v1 + +// HubCustomValidator struct is responsible for validating the Hub resource +// when it is created, updated, or deleted. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as this struct is used only for temporary operations and does not need to be deeply copied. +type HubCustomValidator struct { + // TODO(user): Add more fields as needed for validation +} + +var _ webhook.CustomValidator = &HubCustomValidator{} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type Hub. +func (v *HubCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + hub, ok := obj.(*fleetconfigopenclustermanagementiov1beta1.Hub) + if !ok { + return nil, fmt.Errorf("expected a Hub object but got %T", obj) + } + hublog.Info("Validation for Hub upon creation", "name", hub.GetName()) + + // TODO(user): fill in your validation logic upon object creation. + + return nil, nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type Hub. +func (v *HubCustomValidator) ValidateUpdate(_ context.Context, _, newObj runtime.Object) (admission.Warnings, error) { + hub, ok := newObj.(*fleetconfigopenclustermanagementiov1beta1.Hub) + if !ok { + return nil, fmt.Errorf("expected a Hub object for the newObj but got %T", newObj) + } + hublog.Info("Validation for Hub upon update", "name", hub.GetName()) + + // TODO(user): fill in your validation logic upon object update. + + return nil, nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type Hub. +func (v *HubCustomValidator) ValidateDelete(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + hub, ok := obj.(*fleetconfigopenclustermanagementiov1beta1.Hub) + if !ok { + return nil, fmt.Errorf("expected a Hub object but got %T", obj) + } + hublog.Info("Validation for Hub upon deletion", "name", hub.GetName()) + + // TODO(user): fill in your validation logic upon object deletion. + + return nil, nil +} diff --git a/fleetconfig-controller/internal/webhook/v1beta1/hub_webhook_test.go b/fleetconfig-controller/internal/webhook/v1beta1/hub_webhook_test.go new file mode 100644 index 00000000..62999319 --- /dev/null +++ b/fleetconfig-controller/internal/webhook/v1beta1/hub_webhook_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains webhooks for v1beta1 resources +package v1beta1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + fleetconfigopenclustermanagementiov1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" + // TODO (user): Add any additional imports if needed +) + +var _ = Describe("Hub Webhook", func() { + var ( + obj *fleetconfigopenclustermanagementiov1beta1.Hub + oldObj *fleetconfigopenclustermanagementiov1beta1.Hub + validator HubCustomValidator + defaulter HubCustomDefaulter + ) + + BeforeEach(func() { + obj = &fleetconfigopenclustermanagementiov1beta1.Hub{} + oldObj = &fleetconfigopenclustermanagementiov1beta1.Hub{} + validator = HubCustomValidator{} + Expect(validator).NotTo(BeNil(), "Expected validator to be initialized") + defaulter = HubCustomDefaulter{} + Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized") + Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized") + Expect(obj).NotTo(BeNil(), "Expected obj to be initialized") + // TODO (user): Add any setup logic common to all tests + }) + + AfterEach(func() { + // TODO (user): Add any teardown logic common to all tests + }) + + Context("When creating Hub under Defaulting Webhook", func() { + // TODO (user): Add logic for defaulting webhooks + // Example: + // It("Should apply defaults when a required field is empty", func() { + // By("simulating a scenario where defaults should be applied") + // obj.SomeFieldWithDefault = "" + // By("calling the Default method to apply defaults") + // defaulter.Default(ctx, obj) + // By("checking that the default values are set") + // Expect(obj.SomeFieldWithDefault).To(Equal("default_value")) + // }) + }) + + Context("When creating or updating Hub under Validating Webhook", func() { + // TODO (user): Add logic for validating webhooks + // Example: + // It("Should deny creation if a required field is missing", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "" + // Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred()) + // }) + // + // It("Should admit creation if all required fields are present", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "valid_value" + // Expect(validator.ValidateCreate(ctx, obj)).To(BeNil()) + // }) + // + // It("Should validate updates correctly", func() { + // By("simulating a valid update scenario") + // oldObj.SomeRequiredField = "updated_value" + // obj.SomeRequiredField = "updated_value" + // Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil()) + // }) + }) + +}) diff --git a/fleetconfig-controller/internal/webhook/v1beta1/spoke_webhook.go b/fleetconfig-controller/internal/webhook/v1beta1/spoke_webhook.go new file mode 100644 index 00000000..c5e4141b --- /dev/null +++ b/fleetconfig-controller/internal/webhook/v1beta1/spoke_webhook.go @@ -0,0 +1,129 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// TODO - remove once spoke webhooks are implemented. +// +//nolint:all // Required because of `dupl` between this file and spoke_webhook.go +package v1beta1 + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + fleetconfigopenclustermanagementiov1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" +) + +// nolint:unused +// log is for logging in this package. +var spokelog = logf.Log.WithName("spoke-resource") + +// SetupSpokeWebhookWithManager registers the webhook for Spoke in the manager. +func SetupSpokeWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&fleetconfigopenclustermanagementiov1beta1.Spoke{}). + WithValidator(&SpokeCustomValidator{}). + WithDefaulter(&SpokeCustomDefaulter{}). + Complete() +} + +// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +// +kubebuilder:webhook:path=/mutate-fleetconfig-open-cluster-management-io-v1beta1-spoke,mutating=true,failurePolicy=fail,sideEffects=None,groups=fleetconfig.open-cluster-management.io,resources=spokes,verbs=create;update,versions=v1beta1,name=mspoke-v1beta1.kb.io,admissionReviewVersions=v1 + +// SpokeCustomDefaulter struct is responsible for setting default values on the custom resource of the +// Kind Spoke when those are created or updated. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as it is used only for temporary operations and does not need to be deeply copied. +type SpokeCustomDefaulter struct { + // TODO(user): Add more fields as needed for defaulting +} + +var _ webhook.CustomDefaulter = &SpokeCustomDefaulter{} + +// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind Spoke. +func (d *SpokeCustomDefaulter) Default(_ context.Context, obj runtime.Object) error { + spoke, ok := obj.(*fleetconfigopenclustermanagementiov1beta1.Spoke) + + if !ok { + return fmt.Errorf("expected an Spoke object but got %T", obj) + } + spokelog.Info("Defaulting for Spoke", "name", spoke.GetName()) + + // TODO(user): fill in your defaulting logic. + + return nil +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here. +// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook. +// +kubebuilder:webhook:path=/validate-fleetconfig-open-cluster-management-io-v1beta1-spoke,mutating=false,failurePolicy=fail,sideEffects=None,groups=fleetconfig.open-cluster-management.io,resources=spokes,verbs=create;update,versions=v1beta1,name=vspoke-v1beta1.kb.io,admissionReviewVersions=v1 + +// SpokeCustomValidator struct is responsible for validating the Spoke resource +// when it is created, updated, or deleted. +// +// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, +// as this struct is used only for temporary operations and does not need to be deeply copied. +type SpokeCustomValidator struct { + // TODO(user): Add more fields as needed for validation +} + +var _ webhook.CustomValidator = &SpokeCustomValidator{} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type Spoke. +func (v *SpokeCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + spoke, ok := obj.(*fleetconfigopenclustermanagementiov1beta1.Spoke) + if !ok { + return nil, fmt.Errorf("expected a Spoke object but got %T", obj) + } + spokelog.Info("Validation for Spoke upon creation", "name", spoke.GetName()) + + // TODO(user): fill in your validation logic upon object creation. + + return nil, nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type Spoke. +func (v *SpokeCustomValidator) ValidateUpdate(_ context.Context, _, newObj runtime.Object) (admission.Warnings, error) { + spoke, ok := newObj.(*fleetconfigopenclustermanagementiov1beta1.Spoke) + if !ok { + return nil, fmt.Errorf("expected a Spoke object for the newObj but got %T", newObj) + } + spokelog.Info("Validation for Spoke upon update", "name", spoke.GetName()) + + // TODO(user): fill in your validation logic upon object update. + + return nil, nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type Spoke. +func (v *SpokeCustomValidator) ValidateDelete(_ context.Context, obj runtime.Object) (admission.Warnings, error) { + spoke, ok := obj.(*fleetconfigopenclustermanagementiov1beta1.Spoke) + if !ok { + return nil, fmt.Errorf("expected a Spoke object but got %T", obj) + } + spokelog.Info("Validation for Spoke upon deletion", "name", spoke.GetName()) + + // TODO(user): fill in your validation logic upon object deletion. + + return nil, nil +} diff --git a/fleetconfig-controller/internal/webhook/v1beta1/spoke_webhook_test.go b/fleetconfig-controller/internal/webhook/v1beta1/spoke_webhook_test.go new file mode 100644 index 00000000..4806b6dd --- /dev/null +++ b/fleetconfig-controller/internal/webhook/v1beta1/spoke_webhook_test.go @@ -0,0 +1,87 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + fleetconfigopenclustermanagementiov1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" + // TODO (user): Add any additional imports if needed +) + +var _ = Describe("Spoke Webhook", func() { + var ( + obj *fleetconfigopenclustermanagementiov1beta1.Spoke + oldObj *fleetconfigopenclustermanagementiov1beta1.Spoke + validator SpokeCustomValidator + defaulter SpokeCustomDefaulter + ) + + BeforeEach(func() { + obj = &fleetconfigopenclustermanagementiov1beta1.Spoke{} + oldObj = &fleetconfigopenclustermanagementiov1beta1.Spoke{} + validator = SpokeCustomValidator{} + Expect(validator).NotTo(BeNil(), "Expected validator to be initialized") + defaulter = SpokeCustomDefaulter{} + Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized") + Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized") + Expect(obj).NotTo(BeNil(), "Expected obj to be initialized") + // TODO (user): Add any setup logic common to all tests + }) + + AfterEach(func() { + // TODO (user): Add any teardown logic common to all tests + }) + + Context("When creating Spoke under Defaulting Webhook", func() { + // TODO (user): Add logic for defaulting webhooks + // Example: + // It("Should apply defaults when a required field is empty", func() { + // By("simulating a scenario where defaults should be applied") + // obj.SomeFieldWithDefault = "" + // By("calling the Default method to apply defaults") + // defaulter.Default(ctx, obj) + // By("checking that the default values are set") + // Expect(obj.SomeFieldWithDefault).To(Equal("default_value")) + // }) + }) + + Context("When creating or updating Spoke under Validating Webhook", func() { + // TODO (user): Add logic for validating webhooks + // Example: + // It("Should deny creation if a required field is missing", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "" + // Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred()) + // }) + // + // It("Should admit creation if all required fields are present", func() { + // By("simulating an invalid creation scenario") + // obj.SomeRequiredField = "valid_value" + // Expect(validator.ValidateCreate(ctx, obj)).To(BeNil()) + // }) + // + // It("Should validate updates correctly", func() { + // By("simulating a valid update scenario") + // oldObj.SomeRequiredField = "updated_value" + // obj.SomeRequiredField = "updated_value" + // Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil()) + // }) + }) + +}) diff --git a/fleetconfig-controller/internal/webhook/v1beta1/webhook_suite_test.go b/fleetconfig-controller/internal/webhook/v1beta1/webhook_suite_test.go new file mode 100644 index 00000000..5277e5df --- /dev/null +++ b/fleetconfig-controller/internal/webhook/v1beta1/webhook_suite_test.go @@ -0,0 +1,171 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "os" + "path/filepath" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + fleetconfigopenclustermanagementiov1beta1 "github.com/open-cluster-management-io/lab/fleetconfig-controller/api/v1beta1" + "github.com/open-cluster-management-io/lab/fleetconfig-controller/internal/test" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + ctx context.Context + cancel context.CancelFunc + k8sClient client.Client + cfg *rest.Config + testEnv *envtest.Environment +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Webhook Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + var err error + err = fleetconfigopenclustermanagementiov1beta1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + root, err := test.GetProjectDir() + Expect(err).NotTo(HaveOccurred()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join(root, "charts", "fleetconfig-controller", "crds")}, + ErrorIfCRDPathMissing: false, + + WebhookInstallOptions: envtest.WebhookInstallOptions{ + Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, + }, + } + + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + // start webhook server using Manager. + webhookInstallOptions := &testEnv.WebhookInstallOptions + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + LeaderElection: false, + Metrics: metricsserver.Options{BindAddress: "0"}, + }) + Expect(err).NotTo(HaveOccurred()) + + err = SetupSpokeWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + err = SetupHubWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:webhook + + go func() { + defer GinkgoRecover() + err = mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + + // wait for the webhook server to get ready. + dialer := &net.Dialer{Timeout: time.Second} + addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) + Eventually(func() error { + conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if err != nil { + return err + } + + return conn.Close() + }).Should(Succeed()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +}