From 1fcacd08ac1b8b9a645ac53b4d265dd253b9a2fe Mon Sep 17 00:00:00 2001 From: Gianluca Mardente Date: Sat, 27 Jun 2026 15:01:38 +0200 Subject: [PATCH] (feat) Introduce ManagementClusterClassifier CRD The existing Classifier evaluates rules against resources inside each managed cluster and applies labels based on what it finds there. This works well, but leaves a gap: there is no way to label managed clusters based on resources that live on the management cluster itself. A concrete example is an Internal Developer Platform backed by Crossplane. When a team orders an addon, a Crossplane Composite Resource is created on the management cluster. The platform needs to label the target cluster in response so that Sveltos can deploy the right addons. But sveltos-agent only sees the managed cluster, not the management cluster, so the existing Classifier cannot help here. This PR introduces two new CRDs to cover that gap. A **ManagementClusterClassifier** watches resources on the management cluster and runs a Lua function that receives the full set of matched resources and returns which managed clusters should be labeled. Because the evaluation happens entirely on the management cluster, no deployment to managed clusters is needed. A **ManagementClusterClassifierReport** tracks the label ownership state per classifier/cluster pair, enabling the same conflict detection the existing Classifier provides through the keymanager. --- .../managementclusterclassifier_types.go | 97 +++++++ ...managementclusterclassifierreport_types.go | 121 +++++++++ api/v1beta1/zz_generated.deepcopy.go | 207 ++++++++++++++ ...io_managementclusterclassifierreports.yaml | 106 ++++++++ ...eltos.io_managementclusterclassifiers.yaml | 238 ++++++++++++++++ config/crd/kustomization.yaml | 2 + generator.go | 6 + lib/crd/crd.go | 8 + lib/crd/crd_test.go | 30 ++ lib/crd/managementclusterclassifierreports.go | 125 +++++++++ lib/crd/managementclusterclassifiers.go | 257 ++++++++++++++++++ ...assifierreports.lib.projectsveltos.io.yaml | 105 +++++++ ...sterclassifiers.lib.projectsveltos.io.yaml | 237 ++++++++++++++++ 13 files changed, 1539 insertions(+) create mode 100644 api/v1beta1/managementclusterclassifier_types.go create mode 100644 api/v1beta1/managementclusterclassifierreport_types.go create mode 100644 config/crd/bases/lib.projectsveltos.io_managementclusterclassifierreports.yaml create mode 100644 config/crd/bases/lib.projectsveltos.io_managementclusterclassifiers.yaml create mode 100644 lib/crd/managementclusterclassifierreports.go create mode 100644 lib/crd/managementclusterclassifiers.go create mode 100644 manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifierreports.lib.projectsveltos.io.yaml create mode 100644 manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifiers.lib.projectsveltos.io.yaml diff --git a/api/v1beta1/managementclusterclassifier_types.go b/api/v1beta1/managementclusterclassifier_types.go new file mode 100644 index 00000000..626bdcdc --- /dev/null +++ b/api/v1beta1/managementclusterclassifier_types.go @@ -0,0 +1,97 @@ +/* +Copyright 2026. projectsveltos.io. All rights reserved. + +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" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +const ( + // ManagementClusterClassifierFinalizer allows ManagementClusterClassifierReconciler + // to clean up resources before removing the CR from the apiserver. + ManagementClusterClassifierFinalizer = "managementclusterclassifierfinalizer.projectsveltos.io" + + ManagementClusterClassifierKind = "ManagementClusterClassifier" +) + +// ManagementClusterClassifierSpec defines the desired state of ManagementClusterClassifier. +type ManagementClusterClassifierSpec struct { + // MatchResources lists the management-cluster resource types to watch. + // Reuses the existing ResourceSelector type. Label/name/namespace filters + // and per-resource Lua (ResourceSelector.Evaluate) all apply: only objects + // where Evaluate returns {matching: true} are included in the resources array + // passed to ClassificationLua. Same behavior as the existing Classifier. + MatchResources []ResourceSelector `json:"matchResources"` + + // ClassificationLua is a Lua function called once per reconcile with the full + // set of resources matched by MatchResources. It receives the array as resources + // and must return an array of cluster references. + // Each entry must have fields: namespace (string), name (string), kind (string). + // kind must be "SveltosCluster" or "Cluster". Entries with a missing or unrecognized + // kind are skipped and recorded in status.failureMessage. + // Passing all resources at once allows the function to make decisions that + // depend on the relationship between multiple resources. + ClassificationLua string `json:"classificationLua"` + + // ClassifierLabels is the set of labels to add to every cluster returned by + // ClassificationLua. Same type as Classifier.spec.classifierLabels. + ClassifierLabels []ClassifierLabel `json:"classifierLabels"` +} + +// ManagementClusterClassifierStatus defines the observed state of ManagementClusterClassifier. +type ManagementClusterClassifierStatus struct { + // FailureMessage reports the error from the last reconcile, if any. + // Set when resource collection or Lua evaluation fails; cleared on success. + // +optional + FailureMessage *string `json:"failureMessage,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:resource:path=managementclusterclassifiers,scope=Cluster +//+kubebuilder:subresource:status + +// ManagementClusterClassifier evaluates resources on the management cluster and +// labels managed clusters accordingly. Unlike Classifier, no deployment to managed +// clusters is required: the Lua function runs entirely on the management cluster and +// returns the list of clusters to label. +type ManagementClusterClassifier struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ManagementClusterClassifierSpec `json:"spec,omitempty"` + Status ManagementClusterClassifierStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ManagementClusterClassifierList contains a list of ManagementClusterClassifier. +type ManagementClusterClassifierList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ManagementClusterClassifier `json:"items"` +} + +func init() { + SchemeBuilder.Register(func(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, + &ManagementClusterClassifier{}, + &ManagementClusterClassifierList{}, + ) + return nil + }) +} diff --git a/api/v1beta1/managementclusterclassifierreport_types.go b/api/v1beta1/managementclusterclassifierreport_types.go new file mode 100644 index 00000000..c012ad4d --- /dev/null +++ b/api/v1beta1/managementclusterclassifierreport_types.go @@ -0,0 +1,121 @@ +/* +Copyright 2026. projectsveltos.io. All rights reserved. + +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 ( + "crypto/sha256" + "fmt" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +const ( + // ManagementClusterClassifierNameLabel is added to each ManagementClusterClassifierReport + // to identify the ManagementClusterClassifier that created it. + // `:` is not a valid character in Kubernetes resource names, so this prefix cannot + // collide with any Classifier name when both are registered in the keymanager. + ManagementClusterClassifierNameLabel = "projectsveltos.io/managementclusterclassifier-name" + + ManagementClusterClassifierReportKind = "ManagementClusterClassifierReport" +) + +// GetManagementClusterClassifierReportName returns a deterministic, fixed-length name +// for a ManagementClusterClassifierReport derived from a SHA-256 hash of the three +// identifying components. The name is opaque; labels carry the human-readable context. +func GetManagementClusterClassifierReportName(classifierName, clusterName string, clusterType *ClusterType) string { + h := sha256.New() + fmt.Fprintf(h, "%s:%s:%s", strings.ToLower(string(*clusterType)), classifierName, clusterName) + return fmt.Sprintf("%x", h.Sum(nil))[:32] +} + +// GetManagementClusterClassifierReportLabels returns the labels applied to a +// ManagementClusterClassifierReport. The three labels together allow efficient +// queries in both directions: all reports for a given classifier, and all reports +// targeting a given cluster. +func GetManagementClusterClassifierReportLabels(classifierName, clusterName string, clusterType *ClusterType) map[string]string { + return map[string]string{ + ManagementClusterClassifierNameLabel: classifierName, + ClassifierReportClusterNameLabel: clusterName, + ClassifierReportClusterTypeLabel: strings.ToLower(string(*clusterType)), + } +} + +// ManagementClusterClassifierReportSpec identifies the classifier and cluster this +// report belongs to. +type ManagementClusterClassifierReportSpec struct { + // ClassifierName is the name of the ManagementClusterClassifier this report is for. + ClassifierName string `json:"classifierName"` + + // ClusterNamespace is the namespace of the cluster this report is for. + ClusterNamespace string `json:"clusterNamespace"` + + // ClusterName is the name of the cluster this report is for. + ClusterName string `json:"clusterName"` + + // ClusterType is the type of the cluster this report is for. + ClusterType ClusterType `json:"clusterType"` +} + +// ManagementClusterClassifierReportStatus is written by the controller after processing. +type ManagementClusterClassifierReportStatus struct { + // ManagedLabels lists the label keys this ManagementClusterClassifier is + // actively managing on the cluster. + // +optional + ManagedLabels []string `json:"managedLabels,omitempty"` + + // UnManagedLabels lists label keys this ManagementClusterClassifier would like + // to manage but cannot because another Classifier or ManagementClusterClassifier + // already owns them. + // +optional + UnManagedLabels []UnManagedLabel `json:"unmanagedLabels,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:resource:path=managementclusterclassifierreports,scope=Namespaced +//+kubebuilder:subresource:status + +// ManagementClusterClassifierReport is created by the ManagementClusterClassifier +// controller — one per (ManagementClusterClassifier, cluster) pair — to track which +// labels are managed on each cluster and to enable conflict detection via the keymanager. +type ManagementClusterClassifierReport struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ManagementClusterClassifierReportSpec `json:"spec,omitempty"` + Status ManagementClusterClassifierReportStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ManagementClusterClassifierReportList contains a list of ManagementClusterClassifierReport. +type ManagementClusterClassifierReportList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ManagementClusterClassifierReport `json:"items"` +} + +func init() { + SchemeBuilder.Register(func(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, + &ManagementClusterClassifierReport{}, + &ManagementClusterClassifierReportList{}, + ) + return nil + }) +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 7a6c652f..9be406c1 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1801,6 +1801,213 @@ func (in *MachingClusterStatus) DeepCopy() *MachingClusterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagementClusterClassifier) DeepCopyInto(out *ManagementClusterClassifier) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifier. +func (in *ManagementClusterClassifier) DeepCopy() *ManagementClusterClassifier { + if in == nil { + return nil + } + out := new(ManagementClusterClassifier) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ManagementClusterClassifier) 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 *ManagementClusterClassifierList) DeepCopyInto(out *ManagementClusterClassifierList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ManagementClusterClassifier, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifierList. +func (in *ManagementClusterClassifierList) DeepCopy() *ManagementClusterClassifierList { + if in == nil { + return nil + } + out := new(ManagementClusterClassifierList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ManagementClusterClassifierList) 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 *ManagementClusterClassifierReport) DeepCopyInto(out *ManagementClusterClassifierReport) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifierReport. +func (in *ManagementClusterClassifierReport) DeepCopy() *ManagementClusterClassifierReport { + if in == nil { + return nil + } + out := new(ManagementClusterClassifierReport) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ManagementClusterClassifierReport) 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 *ManagementClusterClassifierReportList) DeepCopyInto(out *ManagementClusterClassifierReportList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ManagementClusterClassifierReport, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifierReportList. +func (in *ManagementClusterClassifierReportList) DeepCopy() *ManagementClusterClassifierReportList { + if in == nil { + return nil + } + out := new(ManagementClusterClassifierReportList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ManagementClusterClassifierReportList) 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 *ManagementClusterClassifierReportSpec) DeepCopyInto(out *ManagementClusterClassifierReportSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifierReportSpec. +func (in *ManagementClusterClassifierReportSpec) DeepCopy() *ManagementClusterClassifierReportSpec { + if in == nil { + return nil + } + out := new(ManagementClusterClassifierReportSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagementClusterClassifierReportStatus) DeepCopyInto(out *ManagementClusterClassifierReportStatus) { + *out = *in + if in.ManagedLabels != nil { + in, out := &in.ManagedLabels, &out.ManagedLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.UnManagedLabels != nil { + in, out := &in.UnManagedLabels, &out.UnManagedLabels + *out = make([]UnManagedLabel, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifierReportStatus. +func (in *ManagementClusterClassifierReportStatus) DeepCopy() *ManagementClusterClassifierReportStatus { + if in == nil { + return nil + } + out := new(ManagementClusterClassifierReportStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagementClusterClassifierSpec) DeepCopyInto(out *ManagementClusterClassifierSpec) { + *out = *in + if in.MatchResources != nil { + in, out := &in.MatchResources, &out.MatchResources + *out = make([]ResourceSelector, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ClassifierLabels != nil { + in, out := &in.ClassifierLabels, &out.ClassifierLabels + *out = make([]ClassifierLabel, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifierSpec. +func (in *ManagementClusterClassifierSpec) DeepCopy() *ManagementClusterClassifierSpec { + if in == nil { + return nil + } + out := new(ManagementClusterClassifierSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagementClusterClassifierStatus) DeepCopyInto(out *ManagementClusterClassifierStatus) { + *out = *in + if in.FailureMessage != nil { + in, out := &in.FailureMessage, &out.FailureMessage + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementClusterClassifierStatus. +func (in *ManagementClusterClassifierStatus) DeepCopy() *ManagementClusterClassifierStatus { + if in == nil { + return nil + } + out := new(ManagementClusterClassifierStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MessagingMatchCriteria) DeepCopyInto(out *MessagingMatchCriteria) { *out = *in diff --git a/config/crd/bases/lib.projectsveltos.io_managementclusterclassifierreports.yaml b/config/crd/bases/lib.projectsveltos.io_managementclusterclassifierreports.yaml new file mode 100644 index 00000000..c40afce5 --- /dev/null +++ b/config/crd/bases/lib.projectsveltos.io_managementclusterclassifierreports.yaml @@ -0,0 +1,106 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + name: managementclusterclassifierreports.lib.projectsveltos.io +spec: + group: lib.projectsveltos.io + names: + kind: ManagementClusterClassifierReport + listKind: ManagementClusterClassifierReportList + plural: managementclusterclassifierreports + singular: managementclusterclassifierreport + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + ManagementClusterClassifierReport is created by the ManagementClusterClassifier + controller — one per (ManagementClusterClassifier, cluster) pair — to track which + labels are managed on each cluster and to enable conflict detection via the keymanager. + 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: |- + ManagementClusterClassifierReportSpec identifies the classifier and cluster this + report belongs to. + properties: + classifierName: + description: ClassifierName is the name of the ManagementClusterClassifier + this report is for. + type: string + clusterName: + description: ClusterName is the name of the cluster this report is + for. + type: string + clusterNamespace: + description: ClusterNamespace is the namespace of the cluster this + report is for. + type: string + clusterType: + description: ClusterType is the type of the cluster this report is + for. + type: string + required: + - classifierName + - clusterName + - clusterNamespace + - clusterType + type: object + status: + description: ManagementClusterClassifierReportStatus is written by the + controller after processing. + properties: + managedLabels: + description: |- + ManagedLabels lists the label keys this ManagementClusterClassifier is + actively managing on the cluster. + items: + type: string + type: array + unmanagedLabels: + description: |- + UnManagedLabels lists label keys this ManagementClusterClassifier would like + to manage but cannot because another Classifier or ManagementClusterClassifier + already owns them. + items: + properties: + failureMessage: + description: |- + FailureMessage is a human consumable message explaining the + misconfiguration + type: string + key: + description: |- + Key represents a label Classifier would like to manage + but cannot because currently managed by different instance + type: string + required: + - key + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/lib.projectsveltos.io_managementclusterclassifiers.yaml b/config/crd/bases/lib.projectsveltos.io_managementclusterclassifiers.yaml new file mode 100644 index 00000000..bac65e34 --- /dev/null +++ b/config/crd/bases/lib.projectsveltos.io_managementclusterclassifiers.yaml @@ -0,0 +1,238 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + name: managementclusterclassifiers.lib.projectsveltos.io +spec: + group: lib.projectsveltos.io + names: + kind: ManagementClusterClassifier + listKind: ManagementClusterClassifierList + plural: managementclusterclassifiers + singular: managementclusterclassifier + scope: Cluster + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + ManagementClusterClassifier evaluates resources on the management cluster and + labels managed clusters accordingly. Unlike Classifier, no deployment to managed + clusters is required: the Lua function runs entirely on the management cluster and + returns the list of clusters to label. + 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: ManagementClusterClassifierSpec defines the desired state + of ManagementClusterClassifier. + properties: + classificationLua: + description: |- + ClassificationLua is a Lua function called once per reconcile with the full + set of resources matched by MatchResources. It receives the array as resources + and must return an array of cluster references. + Each entry must have fields: namespace (string), name (string), kind (string). + kind must be "SveltosCluster" or "Cluster". Entries with a missing or unrecognized + kind are skipped and recorded in status.failureMessage. + Passing all resources at once allows the function to make decisions that + depend on the relationship between multiple resources. + type: string + classifierLabels: + description: |- + ClassifierLabels is the set of labels to add to every cluster returned by + ClassificationLua. Same type as Classifier.spec.classifierLabels. + items: + properties: + key: + description: Key is the label key + type: string + value: + description: Value is the label value + type: string + required: + - key + - value + type: object + type: array + matchResources: + description: |- + MatchResources lists the management-cluster resource types to watch. + Reuses the existing ResourceSelector type. Label/name/namespace filters + and per-resource Lua (ResourceSelector.Evaluate) all apply: only objects + where Evaluate returns {matching: true} are included in the resources array + passed to ClassificationLua. Same behavior as the existing Classifier. + items: + description: ResourceSelector defines what resources are a match + properties: + evaluate: + description: |- + Evaluate contains a function "evaluate" in lua language. + The function will be passed one of the object selected based on + above criteria. + Must return struct with field "matching" representing whether + object is a match and an optional "message" field. + type: string + evaluateCEL: + description: |- + EvaluateCEL contains a list of named CEL (Common Expression Language) rules. + Each rule will be evaluated in order against each object selected based on + the criteria defined above. Each rule's expression must return a boolean value + indicating whether the object is a match. + + Evaluation stops at the first rule that returns true; subsequent + rules will not be evaluated. + items: + description: CELRule defines a named CEL rule used in EvaluateCEL. + properties: + name: + description: Name is a human-readable identifier for the + rule. + type: string + rule: + description: |- + Rule is the CEL (Common Expression Language) expression to evaluate. + It must return a bool + type: string + required: + - name + - rule + type: object + type: array + group: + description: Group of the resource deployed in the Cluster. + type: string + kind: + description: Kind of the resource deployed in the Cluster. + minLength: 1 + type: string + labelFilters: + description: LabelFilters allows to filter resources based on + current labels. + items: + properties: + key: + description: Key is the label key + type: string + operation: + description: Operation is the comparison operation + enum: + - Equal + - Different + - Has + - DoesNotHave + type: string + value: + description: Value is the label value + type: string + required: + - key + - operation + type: object + type: array + name: + description: Name of the resource deployed in the Cluster. + type: string + namespace: + description: |- + Namespace of the resource deployed in the Cluster. + Empty for resources scoped at cluster level. + For namespaced resources, an empty string "" indicates all namespaces. + type: string + selector: + description: |- + Selector is a standard Kubernetes label selector. Resources are selected + if their labels match the selector. This field uses the standard + Kubernetes label matching syntax (e.g., {"environment": "production"}). + If both LabelFilters and Selector are specified, the requirements from + both are logically ANDed. + 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 + version: + description: Version of the resource deployed in the Cluster. + type: string + required: + - group + - kind + - version + type: object + type: array + required: + - classificationLua + - classifierLabels + - matchResources + type: object + status: + description: ManagementClusterClassifierStatus defines the observed state + of ManagementClusterClassifier. + properties: + failureMessage: + description: |- + FailureMessage reports the error from the last reconcile, if any. + Set when resource collection or Lua evaluation fails; cleared on success. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 24c6539d..bc21f03a 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -22,6 +22,8 @@ resources: - bases/lib.projectsveltos.io_configurationbundles.yaml - bases/lib.projectsveltos.io_configurationgroups.yaml - bases/lib.projectsveltos.io_sveltoslicenses.yaml +- bases/lib.projectsveltos.io_managementclusterclassifiers.yaml +- bases/lib.projectsveltos.io_managementclusterclassifierreports.yaml patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. diff --git a/generator.go b/generator.go index 813fa2f8..4859c7a7 100644 --- a/generator.go +++ b/generator.go @@ -148,4 +148,10 @@ func main() { sveltosLicenseFile := "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_sveltoslicenses.lib.projectsveltos.io.yaml" generate(sveltosLicenseFile, "sveltoslicenses", "SveltosLicense") + + managementclusterclassifierFile := "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifiers.lib.projectsveltos.io.yaml" + generate(managementclusterclassifierFile, "managementclusterclassifiers", "ManagementClusterClassifier") + + managementclusterclassifierreportsFile := "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifierreports.lib.projectsveltos.io.yaml" + generate(managementclusterclassifierreportsFile, "managementclusterclassifierreports", "ManagementClusterClassifierReport") } diff --git a/lib/crd/crd.go b/lib/crd/crd.go index 60fe2b87..a9894023 100644 --- a/lib/crd/crd.go +++ b/lib/crd/crd.go @@ -97,3 +97,11 @@ func GetConfigurationGroupCRDYAML() []byte { func GetSveltosLicenseCRDYAML() []byte { return SveltosLicenseCRD } + +func GetManagementClusterClassifierCRDYAML() []byte { + return ManagementClusterClassifierCRD +} + +func GetManagementClusterClassifierReportCRDYAML() []byte { + return ManagementClusterClassifierReportCRD +} diff --git a/lib/crd/crd_test.go b/lib/crd/crd_test.go index b2a6e65e..bf2421cd 100644 --- a/lib/crd/crd_test.go +++ b/lib/crd/crd_test.go @@ -215,4 +215,34 @@ var _ = Describe("CRD", func() { Expect(string(yaml)).To(Equal(string(currentFile))) }) + + It("Gets the SveltosLicense CustomResourceDefinition", func() { + yaml := crd.GetSveltosLicenseCRDYAML() + + filename := "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_sveltoslicenses.lib.projectsveltos.io.yaml" + currentFile, err := os.ReadFile(filename) + Expect(err).To(BeNil()) + + Expect(string(yaml)).To(Equal(string(currentFile))) + }) + + It("Gets the ManagementClusterClassifier CustomResourceDefinition", func() { + yaml := crd.GetManagementClusterClassifierCRDYAML() + + filename := "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifiers.lib.projectsveltos.io.yaml" + currentFile, err := os.ReadFile(filename) + Expect(err).To(BeNil()) + + Expect(string(yaml)).To(Equal(string(currentFile))) + }) + + It("Gets the ManagementClusterClassifierReport CustomResourceDefinition", func() { + yaml := crd.GetManagementClusterClassifierReportCRDYAML() + + filename := "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifierreports.lib.projectsveltos.io.yaml" + currentFile, err := os.ReadFile(filename) + Expect(err).To(BeNil()) + + Expect(string(yaml)).To(Equal(string(currentFile))) + }) }) diff --git a/lib/crd/managementclusterclassifierreports.go b/lib/crd/managementclusterclassifierreports.go new file mode 100644 index 00000000..4c2be1e6 --- /dev/null +++ b/lib/crd/managementclusterclassifierreports.go @@ -0,0 +1,125 @@ +// Generated by *go generate* - DO NOT EDIT +/* +Copyright 2022-23. projectsveltos.io. All rights reserved. + +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 crd + +var ManagementClusterClassifierReportFile = "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifierreports.lib.projectsveltos.io.yaml" +var ManagementClusterClassifierReportCRD = []byte(`apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + name: managementclusterclassifierreports.lib.projectsveltos.io +spec: + group: lib.projectsveltos.io + names: + kind: ManagementClusterClassifierReport + listKind: ManagementClusterClassifierReportList + plural: managementclusterclassifierreports + singular: managementclusterclassifierreport + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + ManagementClusterClassifierReport is created by the ManagementClusterClassifier + controller — one per (ManagementClusterClassifier, cluster) pair — to track which + labels are managed on each cluster and to enable conflict detection via the keymanager. + 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: |- + ManagementClusterClassifierReportSpec identifies the classifier and cluster this + report belongs to. + properties: + classifierName: + description: ClassifierName is the name of the ManagementClusterClassifier + this report is for. + type: string + clusterName: + description: ClusterName is the name of the cluster this report is + for. + type: string + clusterNamespace: + description: ClusterNamespace is the namespace of the cluster this + report is for. + type: string + clusterType: + description: ClusterType is the type of the cluster this report is + for. + type: string + required: + - classifierName + - clusterName + - clusterNamespace + - clusterType + type: object + status: + description: ManagementClusterClassifierReportStatus is written by the + controller after processing. + properties: + managedLabels: + description: |- + ManagedLabels lists the label keys this ManagementClusterClassifier is + actively managing on the cluster. + items: + type: string + type: array + unmanagedLabels: + description: |- + UnManagedLabels lists label keys this ManagementClusterClassifier would like + to manage but cannot because another Classifier or ManagementClusterClassifier + already owns them. + items: + properties: + failureMessage: + description: |- + FailureMessage is a human consumable message explaining the + misconfiguration + type: string + key: + description: |- + Key represents a label Classifier would like to manage + but cannot because currently managed by different instance + type: string + required: + - key + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +`) diff --git a/lib/crd/managementclusterclassifiers.go b/lib/crd/managementclusterclassifiers.go new file mode 100644 index 00000000..d2f437dd --- /dev/null +++ b/lib/crd/managementclusterclassifiers.go @@ -0,0 +1,257 @@ +// Generated by *go generate* - DO NOT EDIT +/* +Copyright 2022-23. projectsveltos.io. All rights reserved. + +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 crd + +var ManagementClusterClassifierFile = "../../manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifiers.lib.projectsveltos.io.yaml" +var ManagementClusterClassifierCRD = []byte(`apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + name: managementclusterclassifiers.lib.projectsveltos.io +spec: + group: lib.projectsveltos.io + names: + kind: ManagementClusterClassifier + listKind: ManagementClusterClassifierList + plural: managementclusterclassifiers + singular: managementclusterclassifier + scope: Cluster + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + ManagementClusterClassifier evaluates resources on the management cluster and + labels managed clusters accordingly. Unlike Classifier, no deployment to managed + clusters is required: the Lua function runs entirely on the management cluster and + returns the list of clusters to label. + 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: ManagementClusterClassifierSpec defines the desired state + of ManagementClusterClassifier. + properties: + classificationLua: + description: |- + ClassificationLua is a Lua function called once per reconcile with the full + set of resources matched by MatchResources. It receives the array as resources + and must return an array of cluster references. + Each entry must have fields: namespace (string), name (string), kind (string). + kind must be "SveltosCluster" or "Cluster". Entries with a missing or unrecognized + kind are skipped and recorded in status.failureMessage. + Passing all resources at once allows the function to make decisions that + depend on the relationship between multiple resources. + type: string + classifierLabels: + description: |- + ClassifierLabels is the set of labels to add to every cluster returned by + ClassificationLua. Same type as Classifier.spec.classifierLabels. + items: + properties: + key: + description: Key is the label key + type: string + value: + description: Value is the label value + type: string + required: + - key + - value + type: object + type: array + matchResources: + description: |- + MatchResources lists the management-cluster resource types to watch. + Reuses the existing ResourceSelector type. Label/name/namespace filters + and per-resource Lua (ResourceSelector.Evaluate) all apply: only objects + where Evaluate returns {matching: true} are included in the resources array + passed to ClassificationLua. Same behavior as the existing Classifier. + items: + description: ResourceSelector defines what resources are a match + properties: + evaluate: + description: |- + Evaluate contains a function "evaluate" in lua language. + The function will be passed one of the object selected based on + above criteria. + Must return struct with field "matching" representing whether + object is a match and an optional "message" field. + type: string + evaluateCEL: + description: |- + EvaluateCEL contains a list of named CEL (Common Expression Language) rules. + Each rule will be evaluated in order against each object selected based on + the criteria defined above. Each rule's expression must return a boolean value + indicating whether the object is a match. + + Evaluation stops at the first rule that returns true; subsequent + rules will not be evaluated. + items: + description: CELRule defines a named CEL rule used in EvaluateCEL. + properties: + name: + description: Name is a human-readable identifier for the + rule. + type: string + rule: + description: |- + Rule is the CEL (Common Expression Language) expression to evaluate. + It must return a bool + type: string + required: + - name + - rule + type: object + type: array + group: + description: Group of the resource deployed in the Cluster. + type: string + kind: + description: Kind of the resource deployed in the Cluster. + minLength: 1 + type: string + labelFilters: + description: LabelFilters allows to filter resources based on + current labels. + items: + properties: + key: + description: Key is the label key + type: string + operation: + description: Operation is the comparison operation + enum: + - Equal + - Different + - Has + - DoesNotHave + type: string + value: + description: Value is the label value + type: string + required: + - key + - operation + type: object + type: array + name: + description: Name of the resource deployed in the Cluster. + type: string + namespace: + description: |- + Namespace of the resource deployed in the Cluster. + Empty for resources scoped at cluster level. + For namespaced resources, an empty string "" indicates all namespaces. + type: string + selector: + description: |- + Selector is a standard Kubernetes label selector. Resources are selected + if their labels match the selector. This field uses the standard + Kubernetes label matching syntax (e.g., {"environment": "production"}). + If both LabelFilters and Selector are specified, the requirements from + both are logically ANDed. + 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 + version: + description: Version of the resource deployed in the Cluster. + type: string + required: + - group + - kind + - version + type: object + type: array + required: + - classificationLua + - classifierLabels + - matchResources + type: object + status: + description: ManagementClusterClassifierStatus defines the observed state + of ManagementClusterClassifier. + properties: + failureMessage: + description: |- + FailureMessage reports the error from the last reconcile, if any. + Set when resource collection or Lua evaluation fails; cleared on success. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +`) diff --git a/manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifierreports.lib.projectsveltos.io.yaml b/manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifierreports.lib.projectsveltos.io.yaml new file mode 100644 index 00000000..05d2983f --- /dev/null +++ b/manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifierreports.lib.projectsveltos.io.yaml @@ -0,0 +1,105 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + name: managementclusterclassifierreports.lib.projectsveltos.io +spec: + group: lib.projectsveltos.io + names: + kind: ManagementClusterClassifierReport + listKind: ManagementClusterClassifierReportList + plural: managementclusterclassifierreports + singular: managementclusterclassifierreport + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + ManagementClusterClassifierReport is created by the ManagementClusterClassifier + controller — one per (ManagementClusterClassifier, cluster) pair — to track which + labels are managed on each cluster and to enable conflict detection via the keymanager. + 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: |- + ManagementClusterClassifierReportSpec identifies the classifier and cluster this + report belongs to. + properties: + classifierName: + description: ClassifierName is the name of the ManagementClusterClassifier + this report is for. + type: string + clusterName: + description: ClusterName is the name of the cluster this report is + for. + type: string + clusterNamespace: + description: ClusterNamespace is the namespace of the cluster this + report is for. + type: string + clusterType: + description: ClusterType is the type of the cluster this report is + for. + type: string + required: + - classifierName + - clusterName + - clusterNamespace + - clusterType + type: object + status: + description: ManagementClusterClassifierReportStatus is written by the + controller after processing. + properties: + managedLabels: + description: |- + ManagedLabels lists the label keys this ManagementClusterClassifier is + actively managing on the cluster. + items: + type: string + type: array + unmanagedLabels: + description: |- + UnManagedLabels lists label keys this ManagementClusterClassifier would like + to manage but cannot because another Classifier or ManagementClusterClassifier + already owns them. + items: + properties: + failureMessage: + description: |- + FailureMessage is a human consumable message explaining the + misconfiguration + type: string + key: + description: |- + Key represents a label Classifier would like to manage + but cannot because currently managed by different instance + type: string + required: + - key + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifiers.lib.projectsveltos.io.yaml b/manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifiers.lib.projectsveltos.io.yaml new file mode 100644 index 00000000..238a4eda --- /dev/null +++ b/manifests/apiextensions.k8s.io_v1_customresourcedefinition_managementclusterclassifiers.lib.projectsveltos.io.yaml @@ -0,0 +1,237 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.21.0 + name: managementclusterclassifiers.lib.projectsveltos.io +spec: + group: lib.projectsveltos.io + names: + kind: ManagementClusterClassifier + listKind: ManagementClusterClassifierList + plural: managementclusterclassifiers + singular: managementclusterclassifier + scope: Cluster + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: |- + ManagementClusterClassifier evaluates resources on the management cluster and + labels managed clusters accordingly. Unlike Classifier, no deployment to managed + clusters is required: the Lua function runs entirely on the management cluster and + returns the list of clusters to label. + 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: ManagementClusterClassifierSpec defines the desired state + of ManagementClusterClassifier. + properties: + classificationLua: + description: |- + ClassificationLua is a Lua function called once per reconcile with the full + set of resources matched by MatchResources. It receives the array as resources + and must return an array of cluster references. + Each entry must have fields: namespace (string), name (string), kind (string). + kind must be "SveltosCluster" or "Cluster". Entries with a missing or unrecognized + kind are skipped and recorded in status.failureMessage. + Passing all resources at once allows the function to make decisions that + depend on the relationship between multiple resources. + type: string + classifierLabels: + description: |- + ClassifierLabels is the set of labels to add to every cluster returned by + ClassificationLua. Same type as Classifier.spec.classifierLabels. + items: + properties: + key: + description: Key is the label key + type: string + value: + description: Value is the label value + type: string + required: + - key + - value + type: object + type: array + matchResources: + description: |- + MatchResources lists the management-cluster resource types to watch. + Reuses the existing ResourceSelector type. Label/name/namespace filters + and per-resource Lua (ResourceSelector.Evaluate) all apply: only objects + where Evaluate returns {matching: true} are included in the resources array + passed to ClassificationLua. Same behavior as the existing Classifier. + items: + description: ResourceSelector defines what resources are a match + properties: + evaluate: + description: |- + Evaluate contains a function "evaluate" in lua language. + The function will be passed one of the object selected based on + above criteria. + Must return struct with field "matching" representing whether + object is a match and an optional "message" field. + type: string + evaluateCEL: + description: |- + EvaluateCEL contains a list of named CEL (Common Expression Language) rules. + Each rule will be evaluated in order against each object selected based on + the criteria defined above. Each rule's expression must return a boolean value + indicating whether the object is a match. + + Evaluation stops at the first rule that returns true; subsequent + rules will not be evaluated. + items: + description: CELRule defines a named CEL rule used in EvaluateCEL. + properties: + name: + description: Name is a human-readable identifier for the + rule. + type: string + rule: + description: |- + Rule is the CEL (Common Expression Language) expression to evaluate. + It must return a bool + type: string + required: + - name + - rule + type: object + type: array + group: + description: Group of the resource deployed in the Cluster. + type: string + kind: + description: Kind of the resource deployed in the Cluster. + minLength: 1 + type: string + labelFilters: + description: LabelFilters allows to filter resources based on + current labels. + items: + properties: + key: + description: Key is the label key + type: string + operation: + description: Operation is the comparison operation + enum: + - Equal + - Different + - Has + - DoesNotHave + type: string + value: + description: Value is the label value + type: string + required: + - key + - operation + type: object + type: array + name: + description: Name of the resource deployed in the Cluster. + type: string + namespace: + description: |- + Namespace of the resource deployed in the Cluster. + Empty for resources scoped at cluster level. + For namespaced resources, an empty string "" indicates all namespaces. + type: string + selector: + description: |- + Selector is a standard Kubernetes label selector. Resources are selected + if their labels match the selector. This field uses the standard + Kubernetes label matching syntax (e.g., {"environment": "production"}). + If both LabelFilters and Selector are specified, the requirements from + both are logically ANDed. + 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 + version: + description: Version of the resource deployed in the Cluster. + type: string + required: + - group + - kind + - version + type: object + type: array + required: + - classificationLua + - classifierLabels + - matchResources + type: object + status: + description: ManagementClusterClassifierStatus defines the observed state + of ManagementClusterClassifier. + properties: + failureMessage: + description: |- + FailureMessage reports the error from the last reconcile, if any. + Set when resource collection or Lua evaluation fails; cleared on success. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {}