Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions api/v1beta1/managementclusterclassifier_types.go
Original file line number Diff line number Diff line change
@@ -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
})
}
121 changes: 121 additions & 0 deletions api/v1beta1/managementclusterclassifierreport_types.go
Original file line number Diff line number Diff line change
@@ -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
})
}
Loading