Skip to content
Closed
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
151 changes: 151 additions & 0 deletions apis/placement/v1beta1/metriccollector_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
Copyright 2025 The KubeFleet Authors.

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 (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/kubefleet-dev/kubefleet/apis"
)

// +genclient
// +genclient:nonNamespaced
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope="Cluster",shortName=mc,categories={fleet,fleet-metrics}
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:JSONPath=`.metadata.generation`,name="Gen",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="MetricCollectorReady")].status`,name="Ready",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.workloadsMonitored`,name="Workloads",type=integer
// +kubebuilder:printcolumn:JSONPath=`.status.lastCollectionTime`,name="Last-Collection",type=date
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date

// MetricCollector is used by member-agent to scrape and collect metrics from workloads
// running on the member cluster. It runs on each member cluster and collects metrics
// from Prometheus-compatible endpoints.
type MetricCollector struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// The desired state of MetricCollector.
// +required
Spec MetricCollectorSpec `json:"spec"`

// The observed status of MetricCollector.
// +optional
Status MetricCollectorStatus `json:"status,omitempty"`
}

// MetricCollectorSpec defines the desired state of MetricCollector.
type MetricCollectorSpec struct {
// PrometheusURL is the URL of the Prometheus server.
// Example: http://prometheus.test-ns.svc.cluster.local:9090
// +required
// +kubebuilder:validation:Pattern=`^https?://.*$`
PrometheusURL string `json:"prometheusUrl"`

// ReportNamespace is the namespace in the hub cluster where the MetricCollectorReport will be created.
// This should be the fleet-member-{clusterName} namespace.
// Example: fleet-member-cluster-1
// +required
ReportNamespace string `json:"reportNamespace"`
}

// MetricsEndpointSpec defines how to access the metrics endpoint.ctor.
type MetricCollectorStatus struct {
// Conditions is an array of current observed conditions.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`

// ObservedGeneration is the generation most recently observed.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// WorkloadsMonitored is the count of workloads being monitored.
// +optional
WorkloadsMonitored int32 `json:"workloadsMonitored,omitempty"`

// LastCollectionTime is when metrics were last collected.
// +optional
LastCollectionTime *metav1.Time `json:"lastCollectionTime,omitempty"`

// CollectedMetrics contains the most recent metrics from each workload.
// +optional
CollectedMetrics []WorkloadMetrics `json:"collectedMetrics,omitempty"`
}

// WorkloadMetrics represents metrics collected from a single workload pod.
type WorkloadMetrics struct {
// Namespace is the namespace of the pod.
// +required
Namespace string `json:"namespace"`

// ClusterName from the workload_health metric label.
// +required
ClusterName string `json:"clusterName"`

// WorkloadName from the workload_health metric label (typically the deployment name).
// +required
WorkloadName string `json:"workloadName"`

// Health indicates if the workload is healthy (true=healthy, false=unhealthy).
// +required
Health bool `json:"health"`
}

const (
// MetricCollectorConditionTypeReady indicates the collector is ready.
MetricCollectorConditionTypeReady string = "MetricCollectorReady"

// MetricCollectorConditionTypeCollecting indicates metrics are being collected.
MetricCollectorConditionTypeCollecting string = "MetricsCollecting"

// MetricCollectorConditionTypeReported indicates metrics were successfully reported to hub.
MetricCollectorConditionTypeReported string = "MetricsReported"
)

// +kubebuilder:object:root=true

// MetricCollectorList contains a list of MetricCollector.
type MetricCollectorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MetricCollector `json:"items"`
}

// GetConditions returns the conditions of the MetricCollector.
func (m *MetricCollector) GetConditions() []metav1.Condition {
return m.Status.Conditions
}

// SetConditions sets the conditions of the MetricCollector.
func (m *MetricCollector) SetConditions(conditions ...metav1.Condition) {
m.Status.Conditions = conditions
}

// GetCondition returns the condition of the given MetricCollector.
func (m *MetricCollector) GetCondition(conditionType string) *metav1.Condition {
return meta.FindStatusCondition(m.Status.Conditions, conditionType)
}

// Ensure MetricCollector implements the ConditionedObj interface.
var _ apis.ConditionedObj = &MetricCollector{}

func init() {
SchemeBuilder.Register(&MetricCollector{}, &MetricCollectorList{})
}
86 changes: 86 additions & 0 deletions apis/placement/v1beta1/metriccollectorreport_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
Copyright 2025 The KubeFleet Authors.

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"
)

// +genclient
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope="Namespaced",shortName=mcr,categories={fleet,fleet-metrics}
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:JSONPath=`.workloadsMonitored`,name="Workloads",type=integer
// +kubebuilder:printcolumn:JSONPath=`.lastCollectionTime`,name="Last-Collection",type=date
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date

// MetricCollectorReport is created by the MetricCollector controller on the hub cluster
// in the fleet-member-{clusterName} namespace to report collected metrics from a member cluster.
// The controller watches MetricCollector objects on the member cluster, collects metrics,
// and syncs the status to the hub as MetricCollectorReport objects.
//
// Controller workflow:
// 1. MetricCollector reconciles and collects metrics on member cluster
// 2. Metrics include clusterName from workload_health labels
// 3. Controller creates/updates MetricCollectorReport in fleet-member-{clusterName} namespace on hub
// 4. Report name matches MetricCollector name for easy lookup
//
// Namespace: fleet-member-{clusterName} (extracted from CollectedMetrics[0].ClusterName)
// Name: Same as MetricCollector name
// All metrics in CollectedMetrics are guaranteed to have the same ClusterName.
type MetricCollectorReport struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Conditions copied from the MetricCollector status.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`

// ObservedGeneration is the generation most recently observed from the MetricCollector.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// WorkloadsMonitored is the count of workloads being monitored.
// +optional
WorkloadsMonitored int32 `json:"workloadsMonitored,omitempty"`

// LastCollectionTime is when metrics were last collected on the member cluster.
// +optional
LastCollectionTime *metav1.Time `json:"lastCollectionTime,omitempty"`

// CollectedMetrics contains the most recent metrics from each workload.
// All metrics are guaranteed to have the same ClusterName since they're collected from one member cluster.
// +optional
CollectedMetrics []WorkloadMetrics `json:"collectedMetrics,omitempty"`

// LastReportTime is when this report was last synced to the hub.
// +optional
LastReportTime *metav1.Time `json:"lastReportTime,omitempty"`
}

// +kubebuilder:object:root=true

// MetricCollectorReportList contains a list of MetricCollectorReport.
type MetricCollectorReportList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MetricCollectorReport `json:"items"`
}

func init() {
SchemeBuilder.Register(&MetricCollectorReport{}, &MetricCollectorReportList{})
}
63 changes: 63 additions & 0 deletions apis/placement/v1beta1/workloadtracker_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Copyright 2025 The KubeFleet Authors.

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"
)

// WorkloadReference represents a workload to be tracked
type WorkloadReference struct {
// Name is the name of the workload
// +required
Name string `json:"name"`

// Namespace is the namespace of the workload
// +required
Namespace string `json:"namespace"`
}

// +genclient
// +genclient:nonNamespaced
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope="Cluster",categories={fleet,fleet-placement}
// +kubebuilder:storageversion
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// WorkloadTracker expresses user intent to track certain workloads
type WorkloadTracker struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Workloads is a list of workloads to track
// +optional
Workloads []WorkloadReference `json:"workloads,omitempty"`
}

// +kubebuilder:object:root=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// WorkloadTrackerList contains a list of WorkloadTracker
type WorkloadTrackerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []WorkloadTracker `json:"items"`
}

func init() {
SchemeBuilder.Register(&WorkloadTracker{}, &WorkloadTrackerList{})
}
Loading
Loading