From 9731ec487df6c3597810a95dd1a4f87967c46c1d Mon Sep 17 00:00:00 2001 From: Ryan Zhang Date: Wed, 31 May 2023 19:19:13 -0700 Subject: [PATCH] interface: Policy scheduling API (#367) policy scheduling api --- .github/workflows/ci.yml | 2 +- .github/workflows/code-lint.yml | 2 +- .golangci.yml | 2 +- Makefile | 8 +- apis/v1/binding_types.go | 102 ++ apis/v1/clusterresourceplacement_types.go | 367 ++++++ apis/v1/commons.go | 98 ++ apis/v1/doc.go | 11 + apis/v1/groupversion_info.go | 24 + apis/v1/internalmembercluster_types.go | 109 ++ apis/v1/membercluster_types.go | 128 +++ apis/v1/policySnapshot_types.go | 149 +++ apis/v1/resourceSnapshot_types.go | 106 ++ apis/v1/zz_generated.deepcopy.go | 1000 +++++++++++++++++ .../clusterresourceplacement_types.go | 1 + .../clusterresourceplacement_webhook.go | 2 +- apis/v1alpha1/internalmembercluster_types.go | 1 + apis/v1alpha1/membercluster_types.go | 1 + ...leet.azure.com_clusterpolicysnapshots.yaml | 439 ++++++++ ...eet.azure.com_clusterresourcebindings.yaml | 151 +++ ...t.azure.com_clusterresourceplacements.yaml | 612 +++++++++- ...et.azure.com_clusterresourcesnapshots.yaml | 161 +++ ...leet.azure.com_internalmemberclusters.yaml | 190 +++- .../bases/fleet.azure.com_memberclusters.yaml | 301 ++++- docker/hub-agent.Dockerfile | 2 +- docker/member-agent.Dockerfile | 2 +- docker/refresh-token.Dockerfile | 2 +- go.mod | 2 +- hack/loadtest/main.go | 3 +- hack/loadtest/util/help.go | 10 +- pkg/authtoken/token_refresher_test.go | 2 +- .../resource_selector.go | 4 +- .../membercluster_controller.go | 2 +- .../resourcechange_controller.go | 2 +- .../resourcechange_controller_test.go | 2 +- pkg/controllers/work/apply_controller.go | 2 +- pkg/controllers/work/apply_controller_test.go | 2 +- pkg/resourcewatcher/event_handlers_test.go | 4 +- pkg/utils/controller/controller.go | 1 + pkg/webhook/pod/pod_validating_webhook.go | 2 +- .../replicaset_validating_webhook.go | 2 +- test/integration/cluster_placement_test.go | 12 +- 42 files changed, 3953 insertions(+), 72 deletions(-) create mode 100644 apis/v1/binding_types.go create mode 100644 apis/v1/clusterresourceplacement_types.go create mode 100644 apis/v1/commons.go create mode 100644 apis/v1/doc.go create mode 100644 apis/v1/groupversion_info.go create mode 100644 apis/v1/internalmembercluster_types.go create mode 100644 apis/v1/membercluster_types.go create mode 100644 apis/v1/policySnapshot_types.go create mode 100644 apis/v1/resourceSnapshot_types.go create mode 100644 apis/v1/zz_generated.deepcopy.go create mode 100644 config/crd/bases/fleet.azure.com_clusterpolicysnapshots.yaml create mode 100644 config/crd/bases/fleet.azure.com_clusterresourcebindings.yaml create mode 100644 config/crd/bases/fleet.azure.com_clusterresourcesnapshots.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d73c05406..997a7746f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ on: paths-ignore: [docs/**, "**.md", "**.mdx", "**.png", "**.jpg"] env: - GO_VERSION: '1.18' + GO_VERSION: '1.20' jobs: detect-noop: diff --git a/.github/workflows/code-lint.yml b/.github/workflows/code-lint.yml index 13260f63d..8772b054e 100644 --- a/.github/workflows/code-lint.yml +++ b/.github/workflows/code-lint.yml @@ -14,7 +14,7 @@ on: env: # Common versions - GO_VERSION: '1.18' + GO_VERSION: '1.20' jobs: diff --git a/.golangci.yml b/.golangci.yml index cd629acbf..878484d12 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,6 @@ run: deadline: 10m - go: '1.18' + go: '1.20' linters: disable-all: true diff --git a/Makefile b/Makefile index 9d9ccad61..348302616 100644 --- a/Makefile +++ b/Makefile @@ -26,11 +26,11 @@ CLUSTER_CONFIG := $(abspath test/e2e/kind-config.yaml) # Binaries # Note: Need to use abspath so we can invoke these from subdirectories -CONTROLLER_GEN_VER := v0.8.0 +CONTROLLER_GEN_VER := v0.11.4 CONTROLLER_GEN_BIN := controller-gen CONTROLLER_GEN := $(abspath $(TOOLS_BIN_DIR)/$(CONTROLLER_GEN_BIN)-$(CONTROLLER_GEN_VER)) -STATICCHECK_VER := 2022.1.2 +STATICCHECK_VER := 2023.1.2 STATICCHECK_BIN := staticcheck STATICCHECK := $(abspath $(TOOLS_BIN_DIR)/$(STATICCHECK_BIN)-$(STATICCHECK_VER)) @@ -38,12 +38,12 @@ GOIMPORTS_VER := latest GOIMPORTS_BIN := goimports GOIMPORTS := $(abspath $(TOOLS_BIN_DIR)/$(GOIMPORTS_BIN)-$(GOIMPORTS_VER)) -GOLANGCI_LINT_VER := v1.47.2 +GOLANGCI_LINT_VER := v1.52.2 GOLANGCI_LINT_BIN := golangci-lint GOLANGCI_LINT := $(abspath $(TOOLS_BIN_DIR)/$(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VER)) # ENVTEST_K8S_VERSION refers to the version of k8s binary assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.23.x +ENVTEST_K8S_VERSION = 1.26.x # ENVTEST_VER is the version of the ENVTEST binary ENVTEST_VER = latest ENVTEST_BIN := setup-envtest diff --git a/apis/v1/binding_types.go b/apis/v1/binding_types.go new file mode 100644 index 000000000..5532711e0 --- /dev/null +++ b/apis/v1/binding_types.go @@ -0,0 +1,102 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// CRPTrackingLabel is the label that points to the cluster resource policy that creates a resource binding. +const CRPTrackingLabel = labelPrefix + "parentCRP" + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,categories={fleet},shortName=rb +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="ResourceBindingApplied")].status`,name="Applied",type=string +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date + +// ClusterResourceBinding represents a scheduling decision that binds a group of resources to a cluster. +// It must have CRPTrackingLabel that points to the cluster resource policy that creates it. +type ClusterResourceBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // The desired state of ClusterResourceBinding. + // +required + Spec ResourceBindingSpec `json:"spec"` + + // The observed status of ClusterResourceBinding. + // +optional + Status ResourceBindingStatus `json:"status,omitempty"` +} + +// ResourceBindingSpec defines the desired state of ClusterResourceBinding. +type ResourceBindingSpec struct { + // ResourceSnapshotName is the name of the resource snapshot that this resource binding points to. If the resources are divided into multiple snapshots because of the resource size limit, it will point to the name of the parent snapshot. + ResourceSnapshotName string `json:"resourceSnapshotName"` + + // TargetCluster is the name of the cluster that the scheduler assigns the resources to. + TargetCluster string `json:"targetCluster"` +} + +// ResourceBindingStatus represents the current status of a ClusterResourceBinding. +type ResourceBindingStatus struct { + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + + // Conditions is an array of current observed conditions for ClusterResourceBinding. + // +optional + Conditions []metav1.Condition `json:"conditions"` +} + +// ResourceBindingConditionType identifies a specific condition of the ClusterResourceBinding. +type ResourceBindingConditionType string + +const ( + // ResourceBindingBound indicates the bound condition of the given resources. + // Its condition status can be one of the following: + // - "True" means the corresponding work CR is created in the target cluster's namespace. + // - "False" means the corresponding work CR is not created yet. + // - "Unknown" means it is unknown. + ResourceBindingBound ResourceBindingConditionType = "Bound" + + // ResourceBindingApplied indicates the applied condition of the given resources. + // Its condition status can be one of the following: + // - "True" means all the resources are created in the target cluster. + // - "False" means not all the resources are created in the target cluster yet. + // - "Unknown" means it is unknown. + ResourceBindingApplied ResourceBindingConditionType = "Applied" +) + +// ClusterResourceBindingList is a collection of ClusterResourceBinding. +// +kubebuilder:resource:scope="Cluster" +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type ClusterResourceBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + // items is the list of ClusterResourceBindings. + Items []ClusterResourceBinding `json:"items"` +} + +// SetConditions set the given conditions on the ClusterResourceBinding. +func (m *ClusterResourceBinding) SetConditions(conditions ...metav1.Condition) { + for _, c := range conditions { + meta.SetStatusCondition(&m.Status.Conditions, c) + } +} + +// GetCondition returns the condition of the given ClusterResourceBinding. +func (m *ClusterResourceBinding) GetCondition(conditionType string) *metav1.Condition { + return meta.FindStatusCondition(m.Status.Conditions, conditionType) +} + +func init() { + SchemeBuilder.Register(&ClusterResourceBinding{}, &ClusterResourceBindingList{}) +} diff --git a/apis/v1/clusterresourceplacement_types.go b/apis/v1/clusterresourceplacement_types.go new file mode 100644 index 000000000..d20b4e725 --- /dev/null +++ b/apis/v1/clusterresourceplacement_types.go @@ -0,0 +1,367 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +genclient:nonNamespaced +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope="Cluster",shortName=crp,categories={fleet-workload} +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=`.metadata.generation`,name="Gen",type=string +// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Scheduled")].status`,name="Scheduled",type=string +// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Scheduled")].observedGeneration`,name="ScheduledGen",type=string +// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Applied")].status`,name="Applied",type=string +// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Applied")].observedGeneration`,name="AppliedGen",type=string +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterResourcePlacement is used to select cluster scoped resources, including built-in resources and custom resources, and placement them onto selected member clusters in a fleet. +// If a namespace is selected, ALL the resources under the namespace are placed to the target clusters. +// Note that you can't select the following resources: +// - reserved namespaces including: default, kube-* (reserved for Kubernetes system namespaces), fleet-* (reserved for fleet system namespaces). +// - reserved fleet resource types including: MemberCluster, InternalMemberCluster, ClusterResourcePlacement, MultiClusterService, ServiceImport, etc. +type ClusterResourcePlacement struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // The desired state of ClusterResourcePlacement. + // +required + Spec ClusterResourcePlacementSpec `json:"spec"` + + // The observed status of ClusterResourcePlacement. + // +optional + Status ClusterResourcePlacementStatus `json:"status,omitempty"` +} + +// ClusterResourcePlacementSpec defines the desired state of ClusterResourcePlacement. +type ClusterResourcePlacementSpec struct { + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=100 + + // ResourceSelectors is an array of selectors used to select cluster scoped resources. The selectors are `ORed`. + // You can have 1-100 selectors. + // +required + ResourceSelectors []ClusterResourceSelector `json:"resourceSelectors"` + + // Policy defines how to select member clusters to place the selected resources. + // If unspecified, all the joined member clusters are selected. + // +optional + Policy *PlacementPolicy `json:"policy,omitempty"` +} + +// ClusterResourceSelector is used to select cluster scoped resources as the target resources to be placed. +// If a namespace is selected, ALL the resources under the namespace are selected automatically. +// All the fields are `ANDed`. In other words, a resource must match all the fields to be selected. +type ClusterResourceSelector struct { + // Group name of the cluster-scoped resource. + // Use an empty string to select resources under the core API group (e.g., namespaces). + // +required + Group string `json:"group"` + + // Version of the cluster-scoped resource. + // +required + Version string `json:"version"` + + // Kind of the cluster-scoped resource. + // Note: When `Kind` is `namespace`, ALL the resources under the selected namespaces are selected. + // +required + Kind string `json:"kind"` + + // You can only specify at most one of the following two fields: Name and LabelSelector. + // If none is specified, all the cluster-scoped resources with the given group, version and kind are selected. + + // Name of the cluster-scoped resource. + // +optional + Name string `json:"name,omitempty"` + + // A label query over all the cluster-scoped resources. Resources matching the query are selected. + // Note that namespace-scoped resources can't be selected even if they match the query. + // +optional + LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` +} + +// PlacementPolicy contains the rules to select target member clusters to place the selected resources. +// Note that only clusters that are both joined and satisfying the rules will be selected. +// +// You can only specify at most one of the two fields: ClusterNames and Affinity. +// If none is specified, all the joined clusters are selected. +type PlacementPolicy struct { + // +kubebuilder:validation:MaxItems=100 + // ClusterNames contains a list of names of MemberCluster to place the selected resources. + // If the list is not empty, `PlacementType`, `NumberOfClusters`, `Affinity`, and `TopologySpreadConstraints` + // are ignored. + // +optional + ClusterNames []string `json:"clusterNames,omitempty"` + + // Type of placement. Can be "PickAll" or "PickN". Default is PickAll. + // +optional + PlacementType PlacementType `json:"placementType,omitempty"` + + // NumberOfClusters of placement. Only valid if the placement type is "PickN". + // +optional + NumberOfClusters *int32 `json:"numberOfClusters,omitempty"` + + // The rollout strategy to use to replace existing applications with new ones. + // +optional + // +patchStrategy=retainKeys + Strategy *RolloutStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" patchMergeKey:"type"` + + // Affinity contains cluster affinity scheduling rules. Defines which member clusters to place the selected resources. + // +optional + Affinity *Affinity `json:"affinity,omitempty"` + + // TopologySpreadConstraints describes how a group of resources ought to spread across multiple topology + // domains. Scheduler will schedule resources in a way which abides by the constraints. + // All topologySpreadConstraints are ANDed. + // +optional + // +patchMergeKey=topologyKey + // +patchStrategy=merge + TopologySpreadConstraints []TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty" patchStrategy:"merge" patchMergeKey:"topologyKey"` +} + +// Affinity is a group of cluster affinity scheduling rules. More to be added. +type Affinity struct { + // ClusterAffinity contains cluster affinity scheduling rules for the selected resources. + // +optional + ClusterAffinity *ClusterAffinity `json:"clusterAffinity,omitempty"` +} + +// ClusterAffinity contains cluster affinity scheduling rules for the selected resources. +type ClusterAffinity struct { + // If the affinity requirements specified by this field are not met at + // scheduling time, the resource will not be scheduled onto the cluster. + // If the affinity requirements specified by this field cease to be met + // at some point after the placement (e.g. due to an update), the system + // may or may not try to eventually remove the resource from the cluster. + // +optional + RequiredDuringSchedulingIgnoredDuringExecution *ClusterSelector `json:"requiredDuringSchedulingIgnoredDuringExecution,omitempty"` + + // The scheduler computes a score for each cluster at schedule time by iterating + // through the elements of this field and adding "weight" to the sum if the cluster + // matches the corresponding matchExpression. The scheduler then chooses the first + // `N` clusters with the highest sum to satisfy the placement. + // This field is ignored if the placement type is "PickAll". + // If the cluster score changes at some point after the placement (e.g. due to an update), + // the system may or may not try to eventually move the resource from a cluster with a lower score + // to a cluster with higher score. + // +optional + PreferredDuringSchedulingIgnoredDuringExecution []PreferredClusterSelector `json:"preferredDuringSchedulingIgnoredDuringExecution,omitempty"` +} + +type ClusterSelector struct { + // +kubebuilder:validation:MaxItems=10 + // ClusterSelectorTerms is a list of cluster selector terms. The terms are `ORed`. + // +required + ClusterSelectorTerms []ClusterSelectorTerm `json:"clusterSelectorTerms"` +} + +type PreferredClusterSelector struct { + // Weight associated with matching the corresponding clusterSelectorTerm, in the range [-100, 100]. + // +required + Weight int32 `json:"weight"` + + // A cluster selector term, associated with the corresponding weight. + // +required + Preference ClusterSelectorTerm `json:"preference"` +} + +// ClusterSelectorTerm contains the requirements to select clusters. +type ClusterSelectorTerm struct { + // LabelSelector is a label query over all the joined member clusters. Clusters matching the query are selected. + // +required + LabelSelector metav1.LabelSelector `json:"labelSelector"` +} + +// TopologySpreadConstraint specifies how to spread resources among the given cluster topology. +type TopologySpreadConstraint struct { + // MaxSkew describes the degree to which resources may be unevenly distributed. + // When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + // between the number of resource copies in the target topology and the global minimum. + // The global minimum is the minimum number of resource copies in a domain. + // When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + // to topologies that satisfy it. + // It's an optional field. Default value is 1 and 0 is not allowed. + // +optional + MaxSkew *int32 `json:"maxSkew,omitempty"` + + // TopologyKey is the key of cluster labels. Clusters that have a label with this key + // and identical values are considered to be in the same topology. + // We consider each as a "bucket", and try to put balanced number + // of replicas of the resource into each bucket honor the `MaxSkew` value. + // It's a required field. + // +required + TopologyKey string `json:"topologyKey"` + + // WhenUnsatisfiable indicates how to deal with the resource if it doesn't satisfy + // the spread constraint. + // - DoNotSchedule (default) tells the scheduler not to schedule it. + // - ScheduleAnyway tells the scheduler to schedule the resource in any cluster, + // but giving higher precedence to topologies that would help reduce the skew. + // It's an optional field. + // +optional + WhenUnsatisfiable UnsatisfiableConstraintAction `json:"whenUnsatisfiable,omitempty"` +} + +// UnsatisfiableConstraintAction defines the type of actions that can be taken if a constraint is not satisfied. +// +enum +type UnsatisfiableConstraintAction string + +const ( + // DoNotSchedule instructs the scheduler not to schedule the resource + // onto the cluster when constraints are not satisfied. + DoNotSchedule UnsatisfiableConstraintAction = "DoNotSchedule" + + // ScheduleAnyway instructs the scheduler to schedule the resource + // even if constraints are not satisfied. + ScheduleAnyway UnsatisfiableConstraintAction = "ScheduleAnyway" +) + +// ClusterResourcePlacementStatus defines the observed state of resource. +type ClusterResourcePlacementStatus struct { + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + + // Conditions is an array of current observed conditions for ClusterResourcePlacement. + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // SelectedResources contains a list of resources selected by ResourceSelectors. + // +optional + SelectedResources []ResourceIdentifier `json:"selectedResources,omitempty"` + + // TargetClusters contains a list of names of member clusters selected by PlacementPolicy. + // Note that the clusters must be both joined and meeting PlacementPolicy. + // +optional + TargetClusters []string `json:"targetClusters,omitempty"` + + // +kubebuilder:validation:MaxItems=1000 + + // FailedResourcePlacements is a list of all the resources failed to be placed to the given clusters. + // Note that we only include 1000 failed resource placements even if there are more than 1000. + // +optional + FailedResourcePlacements []FailedResourcePlacement `json:"failedPlacements,omitempty"` +} + +// ResourceIdentifier identifies one Kubernetes resource. +type ResourceIdentifier struct { + // Group is the group name of the selected resource. + // +optional + Group string `json:"group,omitempty"` + + // Version is the version of the selected resource. + // +required + Version string `json:"version"` + + // Kind represents the Kind of the selected resources. + // +required + Kind string `json:"kind"` + + // Name of the target resource. + // +required + Name string `json:"name"` + + // Namespace is the namespace of the resource. Empty if the resource is cluster scoped. + // +optional + Namespace string `json:"namespace,omitempty"` +} + +// FailedResourcePlacement contains the failure details of a failed resource placement. +type FailedResourcePlacement struct { + // The resource failed to be placed. + // +required + ResourceIdentifier `json:",inline"` + + // Name of the member cluster that the resource is placed to. + // +required + ClusterName string `json:"clusterName"` + + // The failed condition status. + // +required + Condition metav1.Condition `json:"condition"` +} + +// ResourcePlacementConditionType defines a specific condition of a resource placement. +// +enum +type ResourcePlacementConditionType string + +const ( + // ResourcePlacementConditionTypeScheduled indicates whether we have selected at least one resource to be placed to at least one member cluster and created work CRs under the corresponding per-cluster namespaces (i.e., fleet-member-). + // Its condition status can be one of the following: + // - "True" means we have selected at least one resource, targeted at least one member cluster and created the work CRs. + // - "False" means we have selected zero resources, zero target clusters, or failed to create the work CRs. + // - "Unknown" otherwise. + ResourcePlacementConditionTypeScheduled ResourcePlacementConditionType = "Scheduled" + + // ResourcePlacementStatusConditionTypeApplied indicates whether the selected member clusters have received the work CRs and applied the selected resources locally. + // Its condition status can be one of the following: + // - "True" means all the selected resources are successfully applied to all the target clusters. + // - "False" means some of them have failed. + // - "Unknown" otherwise. + ResourcePlacementStatusConditionTypeApplied ResourcePlacementConditionType = "Applied" +) + +// PlacementType identifies the type of placement. +// +enum +type PlacementType string + +const ( + // PickAllPlacementType picks all clusters that satisfy the rules. + PickAllPlacementType PlacementType = "PickAll" + + // PickNPlacementType picks N clusters that satisfy the rules. + PickNPlacementType PlacementType = "PickN" +) + +// RolloutStrategy describes how to replace existing application with new ones. +type RolloutStrategy struct { + // Type of rollout strategy. Can be "Recreate" or "OnDelete". Default is "Recreate". + // +optional + Type RolloutStrategyType `json:"type,omitempty"` +} + +// RolloutStrategyType identifies the type of strategy we use to roll out new resources. +// +enum +type RolloutStrategyType string + +const ( + // RecreateRolloutStrategyType removes all existing resources from the clusters before creating new ones. + RecreateRolloutStrategyType RolloutStrategyType = "Recreate" + + // OnDeleteRolloutStrategy schedules a new resource binding only after an old binding is deleted. + OnDeleteRolloutStrategy RolloutStrategyType = "OnDelete" +) + +// ClusterResourcePlacementList contains a list of ClusterResourcePlacement. +// +kubebuilder:resource:scope="Cluster" +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type ClusterResourcePlacementList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterResourcePlacement `json:"items"` +} + +// SetConditions sets the conditions of the ClusterResourcePlacement. +func (m *ClusterResourcePlacement) SetConditions(conditions ...metav1.Condition) { + for _, c := range conditions { + meta.SetStatusCondition(&m.Status.Conditions, c) + } +} + +// GetCondition returns the condition of the ClusterResourcePlacement objects. +func (m *ClusterResourcePlacement) GetCondition(conditionType string) *metav1.Condition { + return meta.FindStatusCondition(m.Status.Conditions, conditionType) +} + +func init() { + SchemeBuilder.Register(&ClusterResourcePlacement{}, &ClusterResourcePlacementList{}) +} diff --git a/apis/v1/commons.go b/apis/v1/commons.go new file mode 100644 index 000000000..3b44a647f --- /dev/null +++ b/apis/v1/commons.go @@ -0,0 +1,98 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ClusterState string + +const ( + // Unprefixed labels are reserved for end-users + // we will add a fleet.azure.com to designate these labels as official fleet labels. + // See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#label-selector-and-annotation-conventions + labelPrefix = "fleet.azure.com/" +) + +const ( + ClusterStateJoin ClusterState = "Join" + ClusterStateLeave ClusterState = "Leave" +) + +const ( + MemberClusterKind = "MemberCluster" + MemberClusterResource = "memberclusters" + InternalMemberClusterKind = "InternalMemberCluster" + ClusterResourcePlacementResource = "clusterresourceplacements" +) + +// ResourceUsage contains the observed resource usage of a member cluster. +type ResourceUsage struct { + // Capacity represents the total resource capacity of all the nodes on a member cluster. + // +optional + Capacity v1.ResourceList `json:"capacity,omitempty"` + + // Allocatable represents the total resources of all the nodes on a member cluster that are available for scheduling. + // +optional + Allocatable v1.ResourceList `json:"allocatable,omitempty"` + + // When the resource usage is observed. + // +optional + ObservationTime metav1.Time `json:"observationTime,omitempty"` +} + +// AgentType defines a type of agent/binary running in a member cluster. +type AgentType string + +const ( + // MemberAgent (core) handles member cluster joining/leaving as well as k8s object placement from hub to member clusters. + MemberAgent AgentType = "MemberAgent" + // MultiClusterServiceAgent (networking) is responsible for exposing multi-cluster services via L4 load + // balancer. + MultiClusterServiceAgent AgentType = "MultiClusterServiceAgent" + // ServiceExportImportAgent (networking) is responsible for export or import services across multi-clusters. + ServiceExportImportAgent AgentType = "ServiceExportImportAgent" +) + +// AgentStatus defines the observed status of the member agent of the given type. +type AgentStatus struct { + // Type of the member agent. + // +required + Type AgentType `json:"type"` + + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + + // Conditions is an array of current observed conditions for the member agent. + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // Last time we received a heartbeat from the member agent. + // +optional + LastReceivedHeartbeat metav1.Time `json:"lastReceivedHeartbeat,omitempty"` +} + +// AgentConditionType identifies a specific condition on the Agent. +type AgentConditionType string + +const ( + // AgentJoined indicates the join condition of the given member agent. + // Its condition status can be one of the following: + // - "True" means the member agent has joined. + // - "False" means the member agent has left. + // - "Unknown" means the member agent is joining or leaving or in an unknown status. + AgentJoined AgentConditionType = "Joined" + // AgentHealthy indicates the health condition of the given member agent. + // Its condition status can be one of the following: + // - "True" means the member agent is healthy. + // - "False" means the member agent is unhealthy. + // - "Unknown" means the member agent has an unknown health status. + AgentHealthy AgentConditionType = "Healthy" +) diff --git a/apis/v1/doc.go b/apis/v1/doc.go new file mode 100644 index 000000000..a958c08f2 --- /dev/null +++ b/apis/v1/doc.go @@ -0,0 +1,11 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +// Package v1alpha1 contains API Schema definitions for the fleet v1alpha1 API group + +// +kubebuilder:object:generate=true +// +k8s:deepcopy-gen=package,register +// +groupName=fleet.azure.com +package v1 diff --git a/apis/v1/groupversion_info.go b/apis/v1/groupversion_info.go new file mode 100644 index 000000000..ce15d4374 --- /dev/null +++ b/apis/v1/groupversion_info.go @@ -0,0 +1,24 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +// +kubebuilder:object:generate=true +// +groupName=fleet.azure.com +package v1 + +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: "fleet.azure.com", Version: "v1"} + + // 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/apis/v1/internalmembercluster_types.go b/apis/v1/internalmembercluster_types.go new file mode 100644 index 000000000..c96d0e110 --- /dev/null +++ b/apis/v1/internalmembercluster_types.go @@ -0,0 +1,109 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Namespaced,categories={fleet},shortName=imc +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date + +// InternalMemberCluster is used by hub agent to notify the member agents about the member cluster state changes, and is used by the member agents to report their status. +type InternalMemberCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // The desired state of InternalMemberCluster. + // +required + Spec InternalMemberClusterSpec `json:"spec"` + + // The observed status of InternalMemberCluster. + // +optional + Status InternalMemberClusterStatus `json:"status,omitempty"` +} + +// InternalMemberClusterSpec defines the desired state of InternalMemberCluster. Set by the hub agent. +type InternalMemberClusterSpec struct { + // +kubebuilder:validation:Required,Enum=Join;Leave + + // The desired state of the member cluster. Possible values: Join, Leave. + // +required + State ClusterState `json:"state"` + + // +kubebuilder:default=60 + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=600 + + // How often (in seconds) for the member cluster to send a heartbeat to the hub cluster. Default: 60 seconds. Min: 1 second. Max: 10 minutes. + // +optional + HeartbeatPeriodSeconds int32 `json:"heartbeatPeriodSeconds,omitempty"` +} + +// InternalMemberClusterStatus defines the observed state of InternalMemberCluster. +type InternalMemberClusterStatus struct { + // The current observed resource usage of the member cluster. It is populated by the member agent. + // +optional + ResourceUsage ResourceUsage `json:"resourceUsage,omitempty"` + + // AgentStatus is an array of current observed status, each corresponding to one member agent running in the member cluster. + // +optional + AgentStatus []AgentStatus `json:"agentStatus,omitempty"` +} + +//+kubebuilder:object:root=true + +// InternalMemberClusterList contains a list of InternalMemberCluster. +type InternalMemberClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []InternalMemberCluster `json:"items"` +} + +// SetConditionsWithType is used to add condition to AgentStatus for a given agentType. +func (m *InternalMemberCluster) SetConditionsWithType(agentType AgentType, conditions ...metav1.Condition) { + desiredAgentStatus := m.GetAgentStatus(agentType) + for _, c := range conditions { + meta.SetStatusCondition(&desiredAgentStatus.Conditions, c) + } +} + +// GetConditionWithType is used to retrieve the desired condition from AgentStatus for given agentType +func (m *InternalMemberCluster) GetConditionWithType(agentType AgentType, conditionType string) *metav1.Condition { + var desiredAgentStatus AgentStatus + for _, agentStatus := range m.Status.AgentStatus { + if agentType == agentStatus.Type { + desiredAgentStatus = agentStatus + } + } + if desiredAgentStatus.Type == agentType { + return meta.FindStatusCondition(desiredAgentStatus.Conditions, conditionType) + } + return nil +} + +// GetAgentStatus is used to retrieve agent status from internal member cluster, +// if it doesn't exist it creates the expected agent status and returns it. +func (m *InternalMemberCluster) GetAgentStatus(agentType AgentType) *AgentStatus { + for i := range m.Status.AgentStatus { + if m.Status.AgentStatus[i].Type == agentType { + return &m.Status.AgentStatus[i] + } + } + agentStatus := AgentStatus{ + Type: agentType, + Conditions: []metav1.Condition{}, + } + m.Status.AgentStatus = append(m.Status.AgentStatus, agentStatus) + return &m.Status.AgentStatus[len(m.Status.AgentStatus)-1] +} + +func init() { + SchemeBuilder.Register(&InternalMemberCluster{}, &InternalMemberClusterList{}) +} diff --git a/apis/v1/membercluster_types.go b/apis/v1/membercluster_types.go new file mode 100644 index 000000000..46b60e9e5 --- /dev/null +++ b/apis/v1/membercluster_types.go @@ -0,0 +1,128 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,categories={fleet},shortName=mc +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Joined")].status`,name="Joined",type=string +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date + +// MemberCluster is a resource created in the hub cluster to represent a member cluster within a fleet. +type MemberCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // The desired state of MemberCluster. + // +required + Spec MemberClusterSpec `json:"spec"` + + // The observed status of MemberCluster. + // +optional + Status MemberClusterStatus `json:"status,omitempty"` +} + +// MemberClusterSpec defines the desired state of MemberCluster. +type MemberClusterSpec struct { + // +kubebuilder:validation:Required,Enum=Join;Leave + + // The desired state of the member cluster. Possible values: Join, Leave. + // +required + State ClusterState `json:"state"` + + // The identity used by the member cluster to access the hub cluster. + // The hub agents deployed on the hub cluster will automatically grant the minimal required permissions to this identity for the member agents deployed on the member cluster to access the hub cluster. + // +required + Identity rbacv1.Subject `json:"identity"` + + // +kubebuilder:default=60 + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=600 + + // How often (in seconds) for the member cluster to send a heartbeat to the hub cluster. Default: 60 seconds. Min: 1 second. Max: 10 minutes. + // +optional + HeartbeatPeriodSeconds int32 `json:"heartbeatPeriodSeconds,omitempty"` +} + +// MemberClusterStatus defines the observed status of MemberCluster. +type MemberClusterStatus struct { + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + + // Conditions is an array of current observed conditions for the member cluster. + // +optional + Conditions []metav1.Condition `json:"conditions"` + + // The current observed resource usage of the member cluster. It is copied from the corresponding InternalMemberCluster object. + // +optional + ResourceUsage ResourceUsage `json:"resourceUsage,omitempty"` + + // AgentStatus is an array of current observed status, each corresponding to one member agent running in the member cluster. + // +optional + AgentStatus []AgentStatus `json:"agentStatus,omitempty"` +} + +// MemberClusterConditionType defines a specific condition of a member cluster. +type MemberClusterConditionType string + +const ( + // ConditionTypeMemberClusterReadyToJoin indicates the readiness condition of the given member cluster for joining the hub cluster. + // Its condition status can be one of the following: + // - "True" means the hub cluster is ready for the member cluster to join. + // - "False" means the hub cluster is not ready for the member cluster to join. + // - "Unknown" means it is unknown whether the hub cluster is ready for the member cluster to join. + ConditionTypeMemberClusterReadyToJoin MemberClusterConditionType = "ReadyToJoin" + + // ConditionTypeMemberClusterJoined indicates the join condition of the given member cluster. + // Its condition status can be one of the following: + // - "True" means all the agents on the member cluster have joined. + // - "False" means all the agents on the member cluster have left. + // - "Unknown" means not all the agents have joined or left. + ConditionTypeMemberClusterJoined MemberClusterConditionType = "Joined" + + // ConditionTypeMemberClusterHealthy indicates the health condition of the given member cluster. + // Its condition status can be one of the following: + // - "True" means the member cluster is healthy. + // - "False" means the member cluster is unhealthy. + // - "Unknown" means the member cluster has an unknown health status. + // NOTE: This condition type is currently unused. + ConditionTypeMemberClusterHealthy MemberClusterConditionType = "Healthy" +) + +//+kubebuilder:object:root=true + +// MemberClusterList contains a list of MemberCluster. +type MemberClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MemberCluster `json:"items"` +} + +func (m *MemberCluster) SetConditions(conditions ...metav1.Condition) { + for _, c := range conditions { + meta.SetStatusCondition(&m.Status.Conditions, c) + } +} + +func (m *MemberCluster) GetCondition(conditionType string) *metav1.Condition { + return meta.FindStatusCondition(m.Status.Conditions, conditionType) +} + +func (m *MemberCluster) RemoveCondition(conditionType string) { + meta.RemoveStatusCondition(&m.Status.Conditions, conditionType) +} + +func init() { + SchemeBuilder.Register(&MemberCluster{}, &MemberClusterList{}) +} diff --git a/apis/v1/policySnapshot_types.go b/apis/v1/policySnapshot_types.go new file mode 100644 index 000000000..5f7d1237d --- /dev/null +++ b/apis/v1/policySnapshot_types.go @@ -0,0 +1,149 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // PolicyIndexLabel is the label that indicate the policy snapshot index of a cluster policy. + PolicyIndexLabel = labelPrefix + "policyIndex" + + // IsLatestSnapshotLabel tells if the policy snapshot is the latest one. + IsLatestSnapshotLabel = labelPrefix + "isLatestSnapshot" +) + +// +genclient +// +genclient:nonNamespaced +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope="Cluster",shortName=pss,categories={fleet-workload} +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=`.metadata.generation`,name="Gen",type=string +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterPolicySnapshot is used to store a snapshot of cluster placement policy. +// It is immutable. +// It must have `CRPTrackingLabel`, `PolicyIndexLabel` and `IsLatestSnapshotLabel` labels. +type ClusterPolicySnapshot struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // The desired state of PolicySnapShot. + // +required + Spec PolicySnapShotSpec `json:"spec"` + + // The observed status of PolicySnapShot. + // +optional + Status PolicySnapShotStatus `json:"status,omitempty"` +} + +// PolicySnapShotSpec defines the desired state of PolicySnapShot. +type PolicySnapShotSpec struct { + // Policy defines how to select member clusters to place the selected resources. + // If unspecified, all the joined member clusters are selected. + // +optional + Policy *PlacementPolicy `json:"policy,omitempty"` + + // PolicyHash is the sha-256 hash value of the Policy field. + // +required + PolicyHash []byte `json:"policyHash"` +} + +// PolicySnapShotStatus defines the observed state of PolicySnapShot. +type PolicySnapShotStatus struct { + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + + // Conditions is an array of current observed conditions for PolicySnapShot. + // +optional + Conditions []metav1.Condition `json:"conditions"` + + // +kubebuilder:validation:MaxItems=100 + // ClusterDecisions contains a list of names of member clusters considered by the scheduler. + // Note that all the selected clusters must present in the list while not all the + // member clusters are guaranteed to be listed. + // +optional + ClusterDecisions []ClusterDecision `json:"targetClusters,omitempty"` +} + +// PolicySnapShotConditionType identifies a specific condition of the PolicySnapShot. +type PolicySnapShotConditionType string + +const ( + // Scheduled indicates the scheduled condition of the given policySnapShot. + // Its condition status can be one of the following: + // - "True" means the corresponding policySnapShot is fully scheduled. + // - "False" means the corresponding policySnapShot is not scheduled yet. + // - "Unknown" means this policy does not have a full schedule yet. + PolicySnapshotScheduled PolicySnapShotConditionType = "Scheduled" +) + +// ClusterDecision represents a decision from a placement +// An empty ClusterDecision indicates it is not scheduled yet. +type ClusterDecision struct { + // ClusterName is the name of the ManagedCluster. If it is not empty, its value should be unique cross all + // placement decisions for the Placement. + // +kubebuilder:validation:Required + // +required + ClusterName string `json:"clusterName"` + + // Selected indicates if this cluster is selected by the scheduler. + // +required + Selected bool `json:"selected"` + + // ClusterScore represents the score of the cluster calculated by the scheduler. + // +optional + ClusterScore *ClusterScore `json:"clusterScore"` + + // Reason represents the reason why the cluster is selected or not. + // +required + Reason string `json:"reason"` +} + +// ClusterScore represents the score of the cluster calculated by the scheduler. +type ClusterScore struct { + // AffinityScore represents the affinity score of the cluster calculated by the last + // scheduling decision based on the preferred affinity selector. + // An affinity score may not present if the cluster does not meet the required affinity. + // +optional + AffinityScore *int32 `json:"affinityScore,omitempty"` + + // TopologySpreadScore represents the priority score of the cluster calculated by the last + // scheduling decision based on the topology spread applied to the cluster. + // A priority score may not present if the cluster does not meet the topology spread. + // +optional + TopologySpreadScore *int32 `json:"priorityScore,omitempty"` +} + +// ClusterPolicySnapShotList contains a list of ClusterPolicySnapShot. +// +kubebuilder:resource:scope="Cluster" +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type ClusterPolicySnapShotList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterPolicySnapshot `json:"items"` +} + +// SetConditions sets the given conditions on the ClusterPolicySnapshot. +func (m *ClusterPolicySnapshot) SetConditions(conditions ...metav1.Condition) { + for _, c := range conditions { + meta.SetStatusCondition(&m.Status.Conditions, c) + } +} + +// GetCondition returns the condition of the given type if exists. +func (m *ClusterPolicySnapshot) GetCondition(conditionType string) *metav1.Condition { + return meta.FindStatusCondition(m.Status.Conditions, conditionType) +} + +func init() { + SchemeBuilder.Register(&ClusterPolicySnapshot{}, &ClusterPolicySnapShotList{}) +} diff --git a/apis/v1/resourceSnapshot_types.go b/apis/v1/resourceSnapshot_types.go new file mode 100644 index 000000000..f12b56a3b --- /dev/null +++ b/apis/v1/resourceSnapshot_types.go @@ -0,0 +1,106 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package v1 + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +const ( + // ResourceIndexLabel is the label that indicate the resource snapshot index of a cluster policy. + ResourceIndexLabel = labelPrefix + "resourceIndex" + + // ResourceGroupHashAnnotation is the label that contains the value of the sha-256 hash + // value of all the snapshots belong to the same snapshot index. + ResourceGroupHashAnnotation = labelPrefix + "resourceHash" +) + +// +genclient +// +genclient:nonNamespaced +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope="Cluster",shortName=rss,categories={fleet-workload} +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:JSONPath=`.metadata.generation`,name="Gen",type=string +// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterResourceSnapshot is used to store a snapshot of selected resources by a resource placement policy. +// It is immutable. We may need to produce more than one resourceSnapshot for one ResourcePlacement to +// get around the 1MB size limit of k8s objects. +// Each snapshot must have `CRPTrackingLabel`, `ResourceIndexLabel` and `IsLatestSnapshotLabel`. If there are multiple resource snapshots for a resourcePlacement, the parent one whose name is {CRPName}-{resourceIndex} will have a label "NumberOfResourceSnapshots" to store the total number of resource snapshots. +// Each snapshot must have an annotation "fleet.azure.com/resourceHash" with value as the the sha-256 hash value of all the snapshots belong to the same snapshot index. +type ClusterResourceSnapshot struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // The desired state of ResourceSnapShot. + // +required + Spec ResourceSnapShotSpec `json:"spec"` + + // The observed status of ResourceSnapShot. + // +optional + Status ResourceSnapShotStatus `json:"status,omitempty"` +} + +// ResourceSnapShotSpec defines the desired state of ResourceSnapShot. +type ResourceSnapShotSpec struct { + // Index is the parent index of this resource snapshot. Each index can have multiple snapshots. + // All the snapshots with the same index have the same label "fleet.azure.com/snapshotGroup" that + // points to the index. + // +required + + // SelectedResources contains a list of resources selected by ResourceSelectors. + // +required + SelectedResources []ResourceContent `json:"selectedResources"` + + // PolicySnapShotName is the name of the policy snapshot that this resource snapshot is pointing to. + PolicySnapShotName string `json:"policySnapShotName"` +} + +// ResourceContent contains the content of a resource +type ResourceContent struct { + // +kubebuilder:validation:EmbeddedResource + // +kubebuilder:pruning:PreserveUnknownFields + runtime.RawExtension `json:"-,inline"` +} + +type ResourceSnapShotStatus struct { + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + + // Conditions is an array of current observed conditions for ResourceSnapShot. + // +optional + Conditions []metav1.Condition `json:"conditions"` +} + +// ClusterResourceSnapShotList contains a list of ClusterResourceSnapshot. +// +kubebuilder:resource:scope="Cluster" +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type ClusterResourceSnapShotList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterResourceSnapshot `json:"items"` +} + +// SetConditions sets the conditions for a ClusterResourceSnapshot. +func (m *ClusterResourceSnapshot) SetConditions(conditions ...metav1.Condition) { + for _, c := range conditions { + meta.SetStatusCondition(&m.Status.Conditions, c) + } +} + +// GetCondition gets the condition for a ClusterResourceSnapshot. +func (m *ClusterResourceSnapshot) GetCondition(conditionType string) *metav1.Condition { + return meta.FindStatusCondition(m.Status.Conditions, conditionType) +} + +func init() { + SchemeBuilder.Register(&ClusterResourceSnapshot{}, &ClusterResourceSnapShotList{}) +} diff --git a/apis/v1/zz_generated.deepcopy.go b/apis/v1/zz_generated.deepcopy.go new file mode 100644 index 000000000..f82d82513 --- /dev/null +++ b/apis/v1/zz_generated.deepcopy.go @@ -0,0 +1,1000 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Affinity) DeepCopyInto(out *Affinity) { + *out = *in + if in.ClusterAffinity != nil { + in, out := &in.ClusterAffinity, &out.ClusterAffinity + *out = new(ClusterAffinity) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Affinity. +func (in *Affinity) DeepCopy() *Affinity { + if in == nil { + return nil + } + out := new(Affinity) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgentStatus) DeepCopyInto(out *AgentStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.LastReceivedHeartbeat.DeepCopyInto(&out.LastReceivedHeartbeat) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentStatus. +func (in *AgentStatus) DeepCopy() *AgentStatus { + if in == nil { + return nil + } + out := new(AgentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterAffinity) DeepCopyInto(out *ClusterAffinity) { + *out = *in + if in.RequiredDuringSchedulingIgnoredDuringExecution != nil { + in, out := &in.RequiredDuringSchedulingIgnoredDuringExecution, &out.RequiredDuringSchedulingIgnoredDuringExecution + *out = new(ClusterSelector) + (*in).DeepCopyInto(*out) + } + if in.PreferredDuringSchedulingIgnoredDuringExecution != nil { + in, out := &in.PreferredDuringSchedulingIgnoredDuringExecution, &out.PreferredDuringSchedulingIgnoredDuringExecution + *out = make([]PreferredClusterSelector, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterAffinity. +func (in *ClusterAffinity) DeepCopy() *ClusterAffinity { + if in == nil { + return nil + } + out := new(ClusterAffinity) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterDecision) DeepCopyInto(out *ClusterDecision) { + *out = *in + if in.ClusterScore != nil { + in, out := &in.ClusterScore, &out.ClusterScore + *out = new(ClusterScore) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDecision. +func (in *ClusterDecision) DeepCopy() *ClusterDecision { + if in == nil { + return nil + } + out := new(ClusterDecision) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterPolicySnapShotList) DeepCopyInto(out *ClusterPolicySnapShotList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterPolicySnapshot, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPolicySnapShotList. +func (in *ClusterPolicySnapShotList) DeepCopy() *ClusterPolicySnapShotList { + if in == nil { + return nil + } + out := new(ClusterPolicySnapShotList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterPolicySnapShotList) 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 *ClusterPolicySnapshot) DeepCopyInto(out *ClusterPolicySnapshot) { + *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 ClusterPolicySnapshot. +func (in *ClusterPolicySnapshot) DeepCopy() *ClusterPolicySnapshot { + if in == nil { + return nil + } + out := new(ClusterPolicySnapshot) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterPolicySnapshot) 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 *ClusterResourceBinding) DeepCopyInto(out *ClusterResourceBinding) { + *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 ClusterResourceBinding. +func (in *ClusterResourceBinding) DeepCopy() *ClusterResourceBinding { + if in == nil { + return nil + } + out := new(ClusterResourceBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterResourceBinding) 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 *ClusterResourceBindingList) DeepCopyInto(out *ClusterResourceBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterResourceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceBindingList. +func (in *ClusterResourceBindingList) DeepCopy() *ClusterResourceBindingList { + if in == nil { + return nil + } + out := new(ClusterResourceBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterResourceBindingList) 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 *ClusterResourcePlacement) DeepCopyInto(out *ClusterResourcePlacement) { + *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 ClusterResourcePlacement. +func (in *ClusterResourcePlacement) DeepCopy() *ClusterResourcePlacement { + if in == nil { + return nil + } + out := new(ClusterResourcePlacement) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterResourcePlacement) 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 *ClusterResourcePlacementList) DeepCopyInto(out *ClusterResourcePlacementList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterResourcePlacement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourcePlacementList. +func (in *ClusterResourcePlacementList) DeepCopy() *ClusterResourcePlacementList { + if in == nil { + return nil + } + out := new(ClusterResourcePlacementList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterResourcePlacementList) 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 *ClusterResourcePlacementSpec) DeepCopyInto(out *ClusterResourcePlacementSpec) { + *out = *in + if in.ResourceSelectors != nil { + in, out := &in.ResourceSelectors, &out.ResourceSelectors + *out = make([]ClusterResourceSelector, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Policy != nil { + in, out := &in.Policy, &out.Policy + *out = new(PlacementPolicy) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourcePlacementSpec. +func (in *ClusterResourcePlacementSpec) DeepCopy() *ClusterResourcePlacementSpec { + if in == nil { + return nil + } + out := new(ClusterResourcePlacementSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterResourcePlacementStatus) DeepCopyInto(out *ClusterResourcePlacementStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SelectedResources != nil { + in, out := &in.SelectedResources, &out.SelectedResources + *out = make([]ResourceIdentifier, len(*in)) + copy(*out, *in) + } + if in.TargetClusters != nil { + in, out := &in.TargetClusters, &out.TargetClusters + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.FailedResourcePlacements != nil { + in, out := &in.FailedResourcePlacements, &out.FailedResourcePlacements + *out = make([]FailedResourcePlacement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourcePlacementStatus. +func (in *ClusterResourcePlacementStatus) DeepCopy() *ClusterResourcePlacementStatus { + if in == nil { + return nil + } + out := new(ClusterResourcePlacementStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterResourceSelector) DeepCopyInto(out *ClusterResourceSelector) { + *out = *in + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceSelector. +func (in *ClusterResourceSelector) DeepCopy() *ClusterResourceSelector { + if in == nil { + return nil + } + out := new(ClusterResourceSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterResourceSnapShotList) DeepCopyInto(out *ClusterResourceSnapShotList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterResourceSnapshot, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourceSnapShotList. +func (in *ClusterResourceSnapShotList) DeepCopy() *ClusterResourceSnapShotList { + if in == nil { + return nil + } + out := new(ClusterResourceSnapShotList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterResourceSnapShotList) 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 *ClusterResourceSnapshot) DeepCopyInto(out *ClusterResourceSnapshot) { + *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 ClusterResourceSnapshot. +func (in *ClusterResourceSnapshot) DeepCopy() *ClusterResourceSnapshot { + if in == nil { + return nil + } + out := new(ClusterResourceSnapshot) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterResourceSnapshot) 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 *ClusterScore) DeepCopyInto(out *ClusterScore) { + *out = *in + if in.AffinityScore != nil { + in, out := &in.AffinityScore, &out.AffinityScore + *out = new(int32) + **out = **in + } + if in.TopologySpreadScore != nil { + in, out := &in.TopologySpreadScore, &out.TopologySpreadScore + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterScore. +func (in *ClusterScore) DeepCopy() *ClusterScore { + if in == nil { + return nil + } + out := new(ClusterScore) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterSelector) DeepCopyInto(out *ClusterSelector) { + *out = *in + if in.ClusterSelectorTerms != nil { + in, out := &in.ClusterSelectorTerms, &out.ClusterSelectorTerms + *out = make([]ClusterSelectorTerm, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSelector. +func (in *ClusterSelector) DeepCopy() *ClusterSelector { + if in == nil { + return nil + } + out := new(ClusterSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterSelectorTerm) DeepCopyInto(out *ClusterSelectorTerm) { + *out = *in + in.LabelSelector.DeepCopyInto(&out.LabelSelector) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSelectorTerm. +func (in *ClusterSelectorTerm) DeepCopy() *ClusterSelectorTerm { + if in == nil { + return nil + } + out := new(ClusterSelectorTerm) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FailedResourcePlacement) DeepCopyInto(out *FailedResourcePlacement) { + *out = *in + out.ResourceIdentifier = in.ResourceIdentifier + in.Condition.DeepCopyInto(&out.Condition) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailedResourcePlacement. +func (in *FailedResourcePlacement) DeepCopy() *FailedResourcePlacement { + if in == nil { + return nil + } + out := new(FailedResourcePlacement) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InternalMemberCluster) DeepCopyInto(out *InternalMemberCluster) { + *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 InternalMemberCluster. +func (in *InternalMemberCluster) DeepCopy() *InternalMemberCluster { + if in == nil { + return nil + } + out := new(InternalMemberCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InternalMemberCluster) 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 *InternalMemberClusterList) DeepCopyInto(out *InternalMemberClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]InternalMemberCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InternalMemberClusterList. +func (in *InternalMemberClusterList) DeepCopy() *InternalMemberClusterList { + if in == nil { + return nil + } + out := new(InternalMemberClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InternalMemberClusterList) 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 *InternalMemberClusterSpec) DeepCopyInto(out *InternalMemberClusterSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InternalMemberClusterSpec. +func (in *InternalMemberClusterSpec) DeepCopy() *InternalMemberClusterSpec { + if in == nil { + return nil + } + out := new(InternalMemberClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InternalMemberClusterStatus) DeepCopyInto(out *InternalMemberClusterStatus) { + *out = *in + in.ResourceUsage.DeepCopyInto(&out.ResourceUsage) + if in.AgentStatus != nil { + in, out := &in.AgentStatus, &out.AgentStatus + *out = make([]AgentStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InternalMemberClusterStatus. +func (in *InternalMemberClusterStatus) DeepCopy() *InternalMemberClusterStatus { + if in == nil { + return nil + } + out := new(InternalMemberClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MemberCluster) DeepCopyInto(out *MemberCluster) { + *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 MemberCluster. +func (in *MemberCluster) DeepCopy() *MemberCluster { + if in == nil { + return nil + } + out := new(MemberCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MemberCluster) 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 *MemberClusterList) DeepCopyInto(out *MemberClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MemberCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemberClusterList. +func (in *MemberClusterList) DeepCopy() *MemberClusterList { + if in == nil { + return nil + } + out := new(MemberClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MemberClusterList) 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 *MemberClusterSpec) DeepCopyInto(out *MemberClusterSpec) { + *out = *in + out.Identity = in.Identity +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemberClusterSpec. +func (in *MemberClusterSpec) DeepCopy() *MemberClusterSpec { + if in == nil { + return nil + } + out := new(MemberClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MemberClusterStatus) DeepCopyInto(out *MemberClusterStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.ResourceUsage.DeepCopyInto(&out.ResourceUsage) + if in.AgentStatus != nil { + in, out := &in.AgentStatus, &out.AgentStatus + *out = make([]AgentStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemberClusterStatus. +func (in *MemberClusterStatus) DeepCopy() *MemberClusterStatus { + if in == nil { + return nil + } + out := new(MemberClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementPolicy) DeepCopyInto(out *PlacementPolicy) { + *out = *in + if in.ClusterNames != nil { + in, out := &in.ClusterNames, &out.ClusterNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NumberOfClusters != nil { + in, out := &in.NumberOfClusters, &out.NumberOfClusters + *out = new(int32) + **out = **in + } + if in.Strategy != nil { + in, out := &in.Strategy, &out.Strategy + *out = new(RolloutStrategy) + **out = **in + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(Affinity) + (*in).DeepCopyInto(*out) + } + if in.TopologySpreadConstraints != nil { + in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints + *out = make([]TopologySpreadConstraint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementPolicy. +func (in *PlacementPolicy) DeepCopy() *PlacementPolicy { + if in == nil { + return nil + } + out := new(PlacementPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicySnapShotSpec) DeepCopyInto(out *PolicySnapShotSpec) { + *out = *in + if in.Policy != nil { + in, out := &in.Policy, &out.Policy + *out = new(PlacementPolicy) + (*in).DeepCopyInto(*out) + } + if in.PolicyHash != nil { + in, out := &in.PolicyHash, &out.PolicyHash + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicySnapShotSpec. +func (in *PolicySnapShotSpec) DeepCopy() *PolicySnapShotSpec { + if in == nil { + return nil + } + out := new(PolicySnapShotSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicySnapShotStatus) DeepCopyInto(out *PolicySnapShotStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ClusterDecisions != nil { + in, out := &in.ClusterDecisions, &out.ClusterDecisions + *out = make([]ClusterDecision, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicySnapShotStatus. +func (in *PolicySnapShotStatus) DeepCopy() *PolicySnapShotStatus { + if in == nil { + return nil + } + out := new(PolicySnapShotStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PreferredClusterSelector) DeepCopyInto(out *PreferredClusterSelector) { + *out = *in + in.Preference.DeepCopyInto(&out.Preference) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreferredClusterSelector. +func (in *PreferredClusterSelector) DeepCopy() *PreferredClusterSelector { + if in == nil { + return nil + } + out := new(PreferredClusterSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceBindingSpec) DeepCopyInto(out *ResourceBindingSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceBindingSpec. +func (in *ResourceBindingSpec) DeepCopy() *ResourceBindingSpec { + if in == nil { + return nil + } + out := new(ResourceBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceBindingStatus) DeepCopyInto(out *ResourceBindingStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceBindingStatus. +func (in *ResourceBindingStatus) DeepCopy() *ResourceBindingStatus { + if in == nil { + return nil + } + out := new(ResourceBindingStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceContent) DeepCopyInto(out *ResourceContent) { + *out = *in + in.RawExtension.DeepCopyInto(&out.RawExtension) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceContent. +func (in *ResourceContent) DeepCopy() *ResourceContent { + if in == nil { + return nil + } + out := new(ResourceContent) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceIdentifier) DeepCopyInto(out *ResourceIdentifier) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceIdentifier. +func (in *ResourceIdentifier) DeepCopy() *ResourceIdentifier { + if in == nil { + return nil + } + out := new(ResourceIdentifier) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceSnapShotSpec) DeepCopyInto(out *ResourceSnapShotSpec) { + *out = *in + if in.SelectedResources != nil { + in, out := &in.SelectedResources, &out.SelectedResources + *out = make([]ResourceContent, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceSnapShotSpec. +func (in *ResourceSnapShotSpec) DeepCopy() *ResourceSnapShotSpec { + if in == nil { + return nil + } + out := new(ResourceSnapShotSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceSnapShotStatus) DeepCopyInto(out *ResourceSnapShotStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceSnapShotStatus. +func (in *ResourceSnapShotStatus) DeepCopy() *ResourceSnapShotStatus { + if in == nil { + return nil + } + out := new(ResourceSnapShotStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceUsage) DeepCopyInto(out *ResourceUsage) { + *out = *in + if in.Capacity != nil { + in, out := &in.Capacity, &out.Capacity + *out = make(corev1.ResourceList, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + if in.Allocatable != nil { + in, out := &in.Allocatable, &out.Allocatable + *out = make(corev1.ResourceList, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + in.ObservationTime.DeepCopyInto(&out.ObservationTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceUsage. +func (in *ResourceUsage) DeepCopy() *ResourceUsage { + if in == nil { + return nil + } + out := new(ResourceUsage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RolloutStrategy) DeepCopyInto(out *RolloutStrategy) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStrategy. +func (in *RolloutStrategy) DeepCopy() *RolloutStrategy { + if in == nil { + return nil + } + out := new(RolloutStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TopologySpreadConstraint) DeepCopyInto(out *TopologySpreadConstraint) { + *out = *in + if in.MaxSkew != nil { + in, out := &in.MaxSkew, &out.MaxSkew + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologySpreadConstraint. +func (in *TopologySpreadConstraint) DeepCopy() *TopologySpreadConstraint { + if in == nil { + return nil + } + out := new(TopologySpreadConstraint) + in.DeepCopyInto(out) + return out +} diff --git a/apis/v1alpha1/clusterresourceplacement_types.go b/apis/v1alpha1/clusterresourceplacement_types.go index 53fe714cc..8551f8a07 100644 --- a/apis/v1alpha1/clusterresourceplacement_types.go +++ b/apis/v1alpha1/clusterresourceplacement_types.go @@ -14,6 +14,7 @@ import ( // +genclient:nonNamespaced // +kubebuilder:object:root=true // +kubebuilder:resource:scope="Cluster",shortName=crp,categories={fleet-workload} +// +kubebuilder:storageversion // +kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=`.metadata.generation`,name="Gen",type=string // +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Scheduled")].status`,name="Scheduled",type=string diff --git a/apis/v1alpha1/clusterresourceplacement_webhook.go b/apis/v1alpha1/clusterresourceplacement_webhook.go index f95ddc11e..dcff1e0c6 100644 --- a/apis/v1alpha1/clusterresourceplacement_webhook.go +++ b/apis/v1alpha1/clusterresourceplacement_webhook.go @@ -32,7 +32,7 @@ func (c *ClusterResourcePlacement) ValidateCreate() error { return ValidateClusterResourcePlacement(c) } -func (c *ClusterResourcePlacement) ValidateUpdate(old runtime.Object) error { +func (c *ClusterResourcePlacement) ValidateUpdate(_ runtime.Object) error { // TODO: validate changes against old if needed return ValidateClusterResourcePlacement(c) } diff --git a/apis/v1alpha1/internalmembercluster_types.go b/apis/v1alpha1/internalmembercluster_types.go index 6ba336176..433be9f0f 100644 --- a/apis/v1alpha1/internalmembercluster_types.go +++ b/apis/v1alpha1/internalmembercluster_types.go @@ -11,6 +11,7 @@ import ( ) // +kubebuilder:object:root=true +// +kubebuilder:storageversion // +kubebuilder:resource:scope=Namespaced,categories={fleet},shortName=imc // +kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date diff --git a/apis/v1alpha1/membercluster_types.go b/apis/v1alpha1/membercluster_types.go index 4bbf81288..f121699c4 100644 --- a/apis/v1alpha1/membercluster_types.go +++ b/apis/v1alpha1/membercluster_types.go @@ -12,6 +12,7 @@ import ( ) // +kubebuilder:object:root=true +// +kubebuilder:storageversion // +kubebuilder:resource:scope=Cluster,categories={fleet},shortName=mc // +kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Joined")].status`,name="Joined",type=string diff --git a/config/crd/bases/fleet.azure.com_clusterpolicysnapshots.yaml b/config/crd/bases/fleet.azure.com_clusterpolicysnapshots.yaml new file mode 100644 index 000000000..557119182 --- /dev/null +++ b/config/crd/bases/fleet.azure.com_clusterpolicysnapshots.yaml @@ -0,0 +1,439 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.4 + name: clusterpolicysnapshots.fleet.azure.com +spec: + group: fleet.azure.com + names: + categories: + - fleet-workload + kind: ClusterPolicySnapshot + listKind: ClusterPolicySnapshotList + plural: clusterpolicysnapshots + shortNames: + - pss + singular: clusterpolicysnapshot + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.generation + name: Gen + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ClusterPolicySnapshot is used to store a snapshot of cluster + placement policy. It is immutable. It must have `CRPTrackingLabel`, `PolicyIndexLabel` + and `IsLatestSnapshotLabel` labels. + 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: The desired state of PolicySnapShot. + properties: + policy: + description: Policy defines how to select member clusters to place + the selected resources. If unspecified, all the joined member clusters + are selected. + properties: + affinity: + description: Affinity contains cluster affinity scheduling rules. + Defines which member clusters to place the selected resources. + properties: + clusterAffinity: + description: ClusterAffinity contains cluster affinity scheduling + rules for the selected resources. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler computes a score for each cluster + at schedule time by iterating through the elements of + this field and adding "weight" to the sum if the cluster + matches the corresponding matchExpression. The scheduler + then chooses the first `N` clusters with the highest + sum to satisfy the placement. This field is ignored + if the placement type is "PickAll". If the cluster score + changes at some point after the placement (e.g. due + to an update), the system may or may not try to eventually + move the resource from a cluster with a lower score + to a cluster with higher score. + items: + properties: + preference: + description: A cluster selector term, associated + with the corresponding weight. + properties: + labelSelector: + description: LabelSelector is a label query + over all the joined member clusters. Clusters + matching the query are selected. + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - labelSelector + type: object + weight: + description: Weight associated with matching the + corresponding clusterSelectorTerm, in the range + [-100, 100]. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the resource + will not be scheduled onto the cluster. If the affinity + requirements specified by this field cease to be met + at some point after the placement (e.g. due to an update), + the system may or may not try to eventually remove the + resource from the cluster. + properties: + clusterSelectorTerms: + description: ClusterSelectorTerms is a list of cluster + selector terms. The terms are `ORed`. + items: + description: ClusterSelectorTerm contains the requirements + to select clusters. + properties: + labelSelector: + description: LabelSelector is a label query + over all the joined member clusters. Clusters + matching the query are selected. + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - labelSelector + type: object + maxItems: 10 + type: array + required: + - clusterSelectorTerms + type: object + type: object + type: object + clusterNames: + description: ClusterNames contains a list of names of MemberCluster + to place the selected resources. If the list is not empty, `PlacementType`, + `NumberOfClusters`, `Affinity`, and `TopologySpreadConstraints` + are ignored. + items: + type: string + maxItems: 100 + type: array + numberOfClusters: + description: NumberOfClusters of placement. Only valid if the + placement type is "PickN". + format: int32 + type: integer + placementType: + description: Type of placement. Can be "PickAll" or "PickN". Default + is PickAll. + type: string + strategy: + description: The rollout strategy to use to replace existing applications + with new ones. + properties: + type: + description: Type of rollout strategy. Can be "Recreate" or + "OnDelete". Default is "Recreate". + type: string + type: object + topologySpreadConstraints: + description: TopologySpreadConstraints describes how a group of + resources ought to spread across multiple topology domains. + Scheduler will schedule resources in a way which abides by the + constraints. All topologySpreadConstraints are ANDed. + items: + description: TopologySpreadConstraint specifies how to spread + resources among the given cluster topology. + properties: + maxSkew: + description: MaxSkew describes the degree to which resources + may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, + it is the maximum permitted difference between the number + of resource copies in the target topology and the global + minimum. The global minimum is the minimum number of resource + copies in a domain. When `whenUnsatisfiable=ScheduleAnyway`, + it is used to give higher precedence to topologies that + satisfy it. It's an optional field. Default value is 1 + and 0 is not allowed. + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of cluster labels. Clusters + that have a label with this key and identical values are + considered to be in the same topology. We consider each + as a "bucket", and try to put balanced number + of replicas of the resource into each bucket honor the + `MaxSkew` value. It's a required field. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with + the resource if it doesn't satisfy the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule + it. - ScheduleAnyway tells the scheduler to schedule the + resource in any cluster, but giving higher precedence + to topologies that would help reduce the skew. It's an + optional field. + type: string + required: + - topologyKey + type: object + type: array + type: object + policyHash: + description: PolicyHash is the sha-256 hash value of the Policy field + format: byte + type: string + required: + - policyHash + type: object + status: + description: The observed status of PolicySnapShot. + properties: + conditions: + description: Conditions is an array of current observed conditions + for PolicySnapShot. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + 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. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + targetClusters: + description: ClusterDecisions contains a list of names of member clusters + considered by the scheduler. Note that all the selected clusters + must present in the list while not all the member clusters are guaranteed + to be listed. + items: + description: ClusterDecision represents a decision from a placement + An empty ClusterDecision indicates it is not scheduled yet. + properties: + clusterName: + description: ClusterName is the name of the ManagedCluster. + If it is not empty, its value should be unique cross all placement + decisions for the Placement. + type: string + clusterScore: + description: ClusterScore represents the score of the cluster + calculated by the scheduler. + properties: + affinityScore: + description: AffinityScore represents the affinity score + of the cluster calculated by the last scheduling decision + based on the preferred affinity selector. An affinity + score may not present if the cluster does not meet the + required affinity. + format: int32 + type: integer + priorityScore: + description: TopologySpreadScore represents the priority + score of the cluster calculated by the last scheduling + decision based on the topology spread applied to the cluster. + A priority score may not present if the cluster does not + meet the topology spread. + format: int32 + type: integer + type: object + reason: + description: Reason represents the reason why the cluster is + selected or not. + type: string + selected: + description: Selected indicates if this cluster is selected + by the scheduler. + type: boolean + required: + - clusterName + - reason + - selected + type: object + maxItems: 100 + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/fleet.azure.com_clusterresourcebindings.yaml b/config/crd/bases/fleet.azure.com_clusterresourcebindings.yaml new file mode 100644 index 000000000..1aa7ccdd8 --- /dev/null +++ b/config/crd/bases/fleet.azure.com_clusterresourcebindings.yaml @@ -0,0 +1,151 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.4 + name: clusterresourcebindings.fleet.azure.com +spec: + group: fleet.azure.com + names: + categories: + - fleet + kind: ClusterResourceBinding + listKind: ClusterResourceBindingList + plural: clusterresourcebindings + shortNames: + - rb + singular: clusterresourcebinding + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="ResourceBindingApplied")].status + name: Applied + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ClusterResourceBinding is represents a scheduling decision that + binds a group of resources to a cluster. it must have CRPTrackingLabel that + points to the cluster resource policy that creates it. + 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: The desired state of ClusterResourceBinding. + properties: + resourcePolicyName: + description: ResourcePolicyName is the name of the resource policy + that this resource binding points to. + type: string + resourceSnapshotIndex: + description: ResourceSnapshotIndex is the index of the resource snapshot + to which that this resource binding points to. + format: int32 + type: integer + targetCluster: + description: TargetCluster is the name of the cluster that the scheduler + assigns the resources to. + type: string + required: + - resourcePolicyName + - resourceSnapshotIndex + - targetCluster + type: object + status: + description: The observed status of ClusterResourceBinding. + properties: + conditions: + description: Conditions is an array of current observed conditions + for ClusterResourceBinding. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + 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. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/fleet.azure.com_clusterresourceplacements.yaml b/config/crd/bases/fleet.azure.com_clusterresourceplacements.yaml index 33cee1548..d7bacf7ee 100644 --- a/config/crd/bases/fleet.azure.com_clusterresourceplacements.yaml +++ b/config/crd/bases/fleet.azure.com_clusterresourceplacements.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: clusterresourceplacements.fleet.azure.com spec: group: fleet.azure.com @@ -19,6 +18,603 @@ spec: singular: clusterresourceplacement scope: Cluster versions: + - additionalPrinterColumns: + - jsonPath: .metadata.generation + name: Gen + type: string + - jsonPath: .status.conditions[?(@.type=="Scheduled")].status + name: Scheduled + type: string + - jsonPath: .status.conditions[?(@.type=="Scheduled")].observedGeneration + name: ScheduledGen + type: string + - jsonPath: .status.conditions[?(@.type=="Applied")].status + name: Applied + type: string + - jsonPath: .status.conditions[?(@.type=="Applied")].observedGeneration + name: AppliedGen + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: 'ClusterResourcePlacement is used to select cluster scoped resources, + including built-in resources and custom resources, and placement them onto + selected member clusters in a fleet. If a namespace is selected, ALL the + resources under the namespace are placed to the target clusters. Note that + you can''t select the following resources: - reserved namespaces including: + default, kube-* (reserved for Kubernetes system namespaces), fleet-* (reserved + for fleet system namespaces). - reserved fleet resource types including: + MemberCluster, InternalMemberCluster, ClusterResourcePlacement, MultiClusterService, + ServiceImport, etc.' + 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: The desired state of ClusterResourcePlacement. + properties: + policy: + description: Policy defines how to select member clusters to place + the selected resources. If unspecified, all the joined member clusters + are selected. + properties: + affinity: + description: Affinity contains cluster affinity scheduling rules. + Defines which member clusters to place the selected resources. + properties: + clusterAffinity: + description: ClusterAffinity contains cluster affinity scheduling + rules for the selected resources. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler computes a score for each cluster + at schedule time by iterating through the elements of + this field and adding "weight" to the sum if the cluster + matches the corresponding matchExpression. The scheduler + then chooses the first `N` clusters with the highest + sum to satisfy the placement. This field is ignored + if the placement type is "PickAll". If the cluster score + changes at some point after the placement (e.g. due + to an update), the system may or may not try to eventually + move the resource from a cluster with a lower score + to a cluster with higher score. + items: + properties: + preference: + description: A cluster selector term, associated + with the corresponding weight. + properties: + labelSelector: + description: LabelSelector is a label query + over all the joined member clusters. Clusters + matching the query are selected. + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - labelSelector + type: object + weight: + description: Weight associated with matching the + corresponding clusterSelectorTerm, in the range + [-100, 100]. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the resource + will not be scheduled onto the cluster. If the affinity + requirements specified by this field cease to be met + at some point after the placement (e.g. due to an update), + the system may or may not try to eventually remove the + resource from the cluster. + properties: + clusterSelectorTerms: + description: ClusterSelectorTerms is a list of cluster + selector terms. The terms are `ORed`. + items: + description: ClusterSelectorTerm contains the requirements + to select clusters. + properties: + labelSelector: + description: LabelSelector is a label query + over all the joined member clusters. Clusters + matching the query are selected. + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - labelSelector + type: object + maxItems: 10 + type: array + required: + - clusterSelectorTerms + type: object + type: object + type: object + clusterNames: + description: ClusterNames contains a list of names of MemberCluster + to place the selected resources. If the list is not empty, `PlacementType`, + `NumberOfClusters`, `Affinity`, and `TopologySpreadConstraints` + are ignored. + items: + type: string + maxItems: 100 + type: array + numberOfClusters: + description: NumberOfClusters of placement. Only valid if the + placement type is "PickN". + format: int32 + type: integer + placementType: + description: Type of placement. Can be "PickAll" or "PickN". Default + is PickAll. + type: string + strategy: + description: The rollout strategy to use to replace existing applications + with new ones. + properties: + type: + description: Type of rollout strategy. Can be "Recreate" or + "OnDelete". Default is "Recreate". + type: string + type: object + topologySpreadConstraints: + description: TopologySpreadConstraints describes how a group of + resources ought to spread across multiple topology domains. + Scheduler will schedule resources in a way which abides by the + constraints. All topologySpreadConstraints are ANDed. + items: + description: TopologySpreadConstraint specifies how to spread + resources among the given cluster topology. + properties: + maxSkew: + description: MaxSkew describes the degree to which resources + may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, + it is the maximum permitted difference between the number + of resource copies in the target topology and the global + minimum. The global minimum is the minimum number of resource + copies in a domain. When `whenUnsatisfiable=ScheduleAnyway`, + it is used to give higher precedence to topologies that + satisfy it. It's an optional field. Default value is 1 + and 0 is not allowed. + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of cluster labels. Clusters + that have a label with this key and identical values are + considered to be in the same topology. We consider each + as a "bucket", and try to put balanced number + of replicas of the resource into each bucket honor the + `MaxSkew` value. It's a required field. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with + the resource if it doesn't satisfy the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule + it. - ScheduleAnyway tells the scheduler to schedule the + resource in any cluster, but giving higher precedence + to topologies that would help reduce the skew. It's an + optional field. + type: string + required: + - topologyKey + type: object + type: array + type: object + resourceSelectors: + description: ResourceSelectors is an array of selectors used to select + cluster scoped resources. The selectors are `ORed`. You can have + 1-100 selectors. + items: + description: ClusterResourceSelector is used to select cluster scoped + resources as the target resources to be placed. If a namespace + is selected, ALL the resources under the namespace are selected + automatically. All the fields are `ANDed`. In other words, a resource + must match all the fields to be selected. + properties: + group: + description: Group name of the cluster-scoped resource. Use + an empty string to select resources under the core API group + (e.g., namespaces). + type: string + kind: + description: 'Kind of the cluster-scoped resource. Note: When + `Kind` is `namespace`, ALL the resources under the selected + namespaces are selected.' + type: string + labelSelector: + description: A label query over all the cluster-scoped resources. + Resources matching the query are selected. Note that namespace-scoped + resources can't be selected even if they match the query. + 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 + required: + - key + - operator + type: object + type: array + 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 + name: + description: Name of the cluster-scoped resource. + type: string + version: + description: Version of the cluster-scoped resource. + type: string + required: + - group + - kind + - version + type: object + maxItems: 100 + minItems: 1 + type: array + required: + - resourceSelectors + type: object + status: + description: The observed status of ClusterResourcePlacement. + properties: + conditions: + description: Conditions is an array of current observed conditions + for ClusterResourcePlacement. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + 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. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + failedPlacements: + description: FailedResourcePlacements is a list of all the resources + failed to be placed to the given clusters. Note that we only include + 1000 failed resource placements even if there are more than 1000. + items: + description: FailedResourcePlacement contains the failure details + of a failed resource placement. + properties: + clusterName: + description: Name of the member cluster that the resource is + placed to. + type: string + condition: + description: The failed condition status. + 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. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + group: + description: Group is the group name of the selected resource. + type: string + kind: + description: Kind represents the Kind of the selected resources. + type: string + name: + description: Name of the target resource. + type: string + namespace: + description: Namespace is the namespace of the resource. Empty + if the resource is cluster scoped. + type: string + version: + description: Version is the version of the selected resource. + type: string + required: + - clusterName + - condition + - kind + - name + - version + type: object + maxItems: 1000 + type: array + selectedResources: + description: SelectedResources contains a list of resources selected + by ResourceSelectors. + items: + description: ResourceIdentifier identifies one Kubernetes resource. + properties: + group: + description: Group is the group name of the selected resource. + type: string + kind: + description: Kind represents the Kind of the selected resources. + type: string + name: + description: Name of the target resource. + type: string + namespace: + description: Namespace is the namespace of the resource. Empty + if the resource is cluster scoped. + type: string + version: + description: Version is the version of the selected resource. + type: string + required: + - kind + - name + - version + type: object + type: array + targetClusters: + description: TargetClusters contains a list of names of member clusters + selected by PlacementPolicy. Note that the clusters must be both + joined and meeting PlacementPolicy. + items: + type: string + type: array + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} - additionalPrinterColumns: - jsonPath: .metadata.generation name: Gen @@ -138,6 +734,7 @@ spec: The requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic required: - labelSelector type: object @@ -221,6 +818,7 @@ spec: only "value". The requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic name: description: Name of the cluster-scoped resource. type: string @@ -248,8 +846,8 @@ spec: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a foo's - current state. // Known .status.conditions.type are: \"Available\", + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" @@ -452,9 +1050,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/fleet.azure.com_clusterresourcesnapshots.yaml b/config/crd/bases/fleet.azure.com_clusterresourcesnapshots.yaml new file mode 100644 index 000000000..5e9b325ff --- /dev/null +++ b/config/crd/bases/fleet.azure.com_clusterresourcesnapshots.yaml @@ -0,0 +1,161 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.4 + name: clusterresourcesnapshots.fleet.azure.com +spec: + group: fleet.azure.com + names: + categories: + - fleet-workload + kind: ClusterResourceSnapshot + listKind: ClusterResourceSnapshotList + plural: clusterresourcesnapshots + shortNames: + - rss + singular: clusterresourcesnapshot + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.generation + name: Gen + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ClusterResourceSnapshot is used to store a snapshot of selected + resources by a resource placement policy. It is immutable. We may need to + produce more than one resourceSnapshot for one ResourcePlacement to get + around the 1MB size limit of k8s objects. Each snapshot must have `CRPTrackingLabel`, + `ResourceIndexLabel` and `IsLatestSnapshotLabel` Each snapshot must have + an annotation "fleet.azure.com/" with value as the s + 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: The desired state of ResourceSnapShot. + properties: + index: + description: Index is the parent index of this resource snapshot. + Each index can have multiple snapshots. All the snapshots with the + same index have the same label "fleet.azure.com/snapshotGroup" that + points to the index. + format: int32 + type: integer + policySnapShotName: + description: PolicySnapShotName is the name of the policy snapshot + that this resource snapshot is pointing to. + type: string + selectedResources: + description: SelectedResources contains a list of resources selected + by ResourceSelectors. + items: + description: ResourceContent contains the content of a resource + type: object + x-kubernetes-embedded-resource: true + x-kubernetes-preserve-unknown-fields: true + type: array + required: + - index + - policySnapShotName + - selectedResources + type: object + status: + description: The observed status of ResourceSnapShot. + properties: + conditions: + description: Conditions is an array of current observed conditions + for ResourceSnapShot. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + 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. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/fleet.azure.com_internalmemberclusters.yaml b/config/crd/bases/fleet.azure.com_internalmemberclusters.yaml index 92daafdaa..b18ab094a 100644 --- a/config/crd/bases/fleet.azure.com_internalmemberclusters.yaml +++ b/config/crd/bases/fleet.azure.com_internalmemberclusters.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: internalmemberclusters.fleet.azure.com spec: group: fleet.azure.com @@ -19,6 +18,185 @@ spec: singular: internalmembercluster scope: Namespaced versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: InternalMemberCluster is used by hub agent to notify the member + agents about the member cluster state changes, and is used by the member + agents to report their status. + 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: The desired state of InternalMemberCluster. + properties: + heartbeatPeriodSeconds: + default: 60 + description: 'How often (in seconds) for the member cluster to send + a heartbeat to the hub cluster. Default: 60 seconds. Min: 1 second. + Max: 10 minutes.' + format: int32 + maximum: 600 + minimum: 1 + type: integer + state: + description: 'The desired state of the member cluster. Possible values: + Join, Leave.' + type: string + required: + - state + type: object + status: + description: The observed status of InternalMemberCluster. + properties: + agentStatus: + description: AgentStatus is an array of current observed status, each + corresponding to one member agent running in the member cluster. + items: + description: AgentStatus defines the observed status of the member + agent of the given type. + properties: + conditions: + description: Conditions is an array of current observed conditions + for the member agent. + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, \n type FooStatus struct{ + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: \"Available\", \"Progressing\", + and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields + }" + 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. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lastReceivedHeartbeat: + description: Last time we received a heartbeat from the member + agent. + format: date-time + type: string + type: + description: Type of the member agent. + type: string + required: + - type + type: object + type: array + resourceUsage: + description: The current observed resource usage of the member cluster. + It is populated by the member agent. + properties: + allocatable: + 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: Allocatable represents the total resources of all + the nodes on a member cluster that are available for scheduling. + type: object + capacity: + 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: Capacity represents the total resource capacity of + all the nodes on a member cluster. + type: object + observationTime: + description: When the resource usage is observed. + format: date-time + type: string + type: object + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age @@ -78,7 +256,7 @@ spec: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path - .status.conditions. For example, type FooStatus struct{ + .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge @@ -198,9 +376,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crd/bases/fleet.azure.com_memberclusters.yaml b/config/crd/bases/fleet.azure.com_memberclusters.yaml index 200d723cf..de46eac9a 100644 --- a/config/crd/bases/fleet.azure.com_memberclusters.yaml +++ b/config/crd/bases/fleet.azure.com_memberclusters.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.11.4 name: memberclusters.fleet.azure.com spec: group: fleet.azure.com @@ -19,6 +18,291 @@ spec: singular: membercluster scope: Cluster versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Joined")].status + name: Joined + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: MemberCluster is a resource created in the hub cluster to represent + a member cluster within a fleet. + 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: The desired state of MemberCluster. + properties: + heartbeatPeriodSeconds: + default: 60 + description: 'How often (in seconds) for the member cluster to send + a heartbeat to the hub cluster. Default: 60 seconds. Min: 1 second. + Max: 10 minutes.' + format: int32 + maximum: 600 + minimum: 1 + type: integer + identity: + description: The identity used by the member cluster to access the + hub cluster. The hub agents deployed on the hub cluster will automatically + grant the minimal required permissions to this identity for the + member agents deployed on the member cluster to access the hub cluster. + properties: + apiGroup: + description: APIGroup holds the API group of the referenced subject. + Defaults to "" for ServiceAccount subjects. Defaults to "rbac.authorization.k8s.io" + for User and Group subjects. + type: string + kind: + description: Kind of object being referenced. Values defined by + this API group are "User", "Group", and "ServiceAccount". If + the Authorizer does not recognized the kind value, the Authorizer + should report an error. + type: string + name: + description: Name of the object being referenced. + type: string + namespace: + description: Namespace of the referenced object. If the object + kind is non-namespace, such as "User" or "Group", and this value + is not empty the Authorizer should report an error. + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + state: + description: 'The desired state of the member cluster. Possible values: + Join, Leave.' + type: string + required: + - identity + - state + type: object + status: + description: The observed status of MemberCluster. + properties: + agentStatus: + description: AgentStatus is an array of current observed status, each + corresponding to one member agent running in the member cluster. + items: + description: AgentStatus defines the observed status of the member + agent of the given type. + properties: + conditions: + description: Conditions is an array of current observed conditions + for the member agent. + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, \n type FooStatus struct{ + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: \"Available\", \"Progressing\", + and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields + }" + 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. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lastReceivedHeartbeat: + description: Last time we received a heartbeat from the member + agent. + format: date-time + type: string + type: + description: Type of the member agent. + type: string + required: + - type + type: object + type: array + conditions: + description: Conditions is an array of current observed conditions + for the member cluster. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + 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. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + resourceUsage: + description: The current observed resource usage of the member cluster. + It is copied from the corresponding InternalMemberCluster object. + properties: + allocatable: + 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: Allocatable represents the total resources of all + the nodes on a member cluster that are available for scheduling. + type: object + capacity: + 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: Capacity represents the total resource capacity of + all the nodes on a member cluster. + type: object + observationTime: + description: When the resource usage is observed. + format: date-time + type: string + type: object + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} - additionalPrinterColumns: - jsonPath: .status.conditions[?(@.type=="Joined")].status name: Joined @@ -85,6 +369,7 @@ spec: - kind - name type: object + x-kubernetes-map-type: atomic state: description: 'The desired state of the member cluster. Possible values: Join, Leave.' @@ -110,7 +395,7 @@ spec: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path - .status.conditions. For example, type FooStatus struct{ + .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge @@ -200,8 +485,8 @@ spec: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a foo's - current state. // Known .status.conditions.type are: \"Available\", + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" @@ -303,9 +588,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/docker/hub-agent.Dockerfile b/docker/hub-agent.Dockerfile index b8dc72207..0c217fb5c 100644 --- a/docker/hub-agent.Dockerfile +++ b/docker/hub-agent.Dockerfile @@ -1,5 +1,5 @@ # Build the hubagent binary -FROM golang:1.18 as builder +FROM golang:1.20 as builder WORKDIR /workspace # Copy the Go Modules manifests diff --git a/docker/member-agent.Dockerfile b/docker/member-agent.Dockerfile index f882be8c0..f1a11f621 100644 --- a/docker/member-agent.Dockerfile +++ b/docker/member-agent.Dockerfile @@ -1,5 +1,5 @@ # Build the memberagent binary -FROM golang:1.18 as builder +FROM golang:1.20 as builder WORKDIR /workspace # Copy the Go Modules manifests diff --git a/docker/refresh-token.Dockerfile b/docker/refresh-token.Dockerfile index 9d2c63a51..abf49428b 100644 --- a/docker/refresh-token.Dockerfile +++ b/docker/refresh-token.Dockerfile @@ -1,5 +1,5 @@ # Build the hubagent binary -FROM golang:1.18 as builder +FROM golang:1.20 as builder WORKDIR /workspace # Copy the Go Modules manifests diff --git a/go.mod b/go.mod index 7a64d4e3c..c972ebd59 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.goms.io/fleet -go 1.18 +go 1.20 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.0 diff --git a/hack/loadtest/main.go b/hack/loadtest/main.go index 4b8040c9a..74894611d 100644 --- a/hack/loadtest/main.go +++ b/hack/loadtest/main.go @@ -74,7 +74,8 @@ func main() { go runLoadTest(loadTestCtx, config) // setup prometheus server http.Handle("/metrics", promhttp.Handler()) - if err = http.ListenAndServe(":4848", nil); err != nil { + /* #nosec */ + if err = http.ListenAndServe(":4848", http.TimeoutHandler(nil, 10*time.Second, "request time out")); err != nil { panic(err) } } diff --git a/hack/loadtest/util/help.go b/hack/loadtest/util/help.go index 670724551..80b72e4b8 100644 --- a/hack/loadtest/util/help.go +++ b/hack/loadtest/util/help.go @@ -115,10 +115,7 @@ func applyTestManifests(ctx context.Context, hubClient client.Client, namespaceN if err := applyObjectFromManifest(ctx, hubClient, namespaceName, "hack/loadtest/manifests/test-role.yaml"); err != nil { return err } - if err := applyObjectFromManifest(ctx, hubClient, namespaceName, "hack/loadtest/manifests/test-rolebinding.yaml"); err != nil { - return err - } - return nil + return applyObjectFromManifest(ctx, hubClient, namespaceName, "hack/loadtest/manifests/test-rolebinding.yaml") } // deleteTestManifests deletes the test manifests in the hub cluster under a namespace @@ -144,10 +141,7 @@ func deleteTestManifests(ctx context.Context, hubClient client.Client, namespace if err := deleteObjectFromManifest(ctx, hubClient, namespaceName, "hack/loadtest/manifests/test-role.yaml"); err != nil { return err } - if err := deleteObjectFromManifest(ctx, hubClient, namespaceName, "hack/loadtest/manifests/test-rolebinding.yaml"); err != nil { - return err - } - return nil + return deleteObjectFromManifest(ctx, hubClient, namespaceName, "hack/loadtest/manifests/test-rolebinding.yaml") } func deleteNamespace(ctx context.Context, hubClient client.Client, namespaceName string) error { diff --git a/pkg/authtoken/token_refresher_test.go b/pkg/authtoken/token_refresher_test.go index 5e564daf0..5cc403af6 100644 --- a/pkg/authtoken/token_refresher_test.go +++ b/pkg/authtoken/token_refresher_test.go @@ -20,7 +20,7 @@ type MockAuthTokenProvider struct { Token interfaces.AuthToken } -func (m MockAuthTokenProvider) FetchToken(ctx context.Context) (interfaces.AuthToken, error) { +func (m MockAuthTokenProvider) FetchToken(_ context.Context) (interfaces.AuthToken, error) { return m.Token, nil } diff --git a/pkg/controllers/clusterresourceplacement/resource_selector.go b/pkg/controllers/clusterresourceplacement/resource_selector.go index 9a3ada500..4061e431f 100644 --- a/pkg/controllers/clusterresourceplacement/resource_selector.go +++ b/pkg/controllers/clusterresourceplacement/resource_selector.go @@ -103,7 +103,7 @@ func (r *Reconciler) gatherSelectedResource(ctx context.Context, placement *flee } // fetchClusterScopedResources retrieve the objects based on the selector. -func (r *Reconciler) fetchClusterScopedResources(ctx context.Context, selector fleetv1alpha1.ClusterResourceSelector, placeName string) ([]runtime.Object, error) { +func (r *Reconciler) fetchClusterScopedResources(_ context.Context, selector fleetv1alpha1.ClusterResourceSelector, placeName string) ([]runtime.Object, error) { klog.V(2).InfoS("start to fetch the cluster scoped resources by the selector", "selector", selector) gk := schema.GroupKind{ Group: selector.Group, @@ -221,7 +221,7 @@ func (r *Reconciler) fetchNamespaceResources(ctx context.Context, selector fleet } // fetchAllResourcesInOneNamespace retrieve all the objects inside a single namespace which includes the namespace itself. -func (r *Reconciler) fetchAllResourcesInOneNamespace(ctx context.Context, namespaceName string, placeName string) ([]runtime.Object, error) { +func (r *Reconciler) fetchAllResourcesInOneNamespace(_ context.Context, namespaceName string, placeName string) ([]runtime.Object, error) { var resources []runtime.Object if !utils.ShouldPropagateNamespace(namespaceName, r.SkippedNamespaces) { diff --git a/pkg/controllers/memberclusterplacement/membercluster_controller.go b/pkg/controllers/memberclusterplacement/membercluster_controller.go index a08d8d6bd..f927a1b0f 100644 --- a/pkg/controllers/memberclusterplacement/membercluster_controller.go +++ b/pkg/controllers/memberclusterplacement/membercluster_controller.go @@ -34,7 +34,7 @@ type Reconciler struct { PlacementController controller.Controller } -func (r *Reconciler) Reconcile(ctx context.Context, key controller.QueueKey) (ctrl.Result, error) { +func (r *Reconciler) Reconcile(_ context.Context, key controller.QueueKey) (ctrl.Result, error) { startTime := time.Now() memberClusterName, ok := key.(string) if !ok { diff --git a/pkg/controllers/resourcechange/resourcechange_controller.go b/pkg/controllers/resourcechange/resourcechange_controller.go index 06129e649..be7f86f2e 100644 --- a/pkg/controllers/resourcechange/resourcechange_controller.go +++ b/pkg/controllers/resourcechange/resourcechange_controller.go @@ -49,7 +49,7 @@ type Reconciler struct { Recorder record.EventRecorder } -func (r *Reconciler) Reconcile(ctx context.Context, key controller.QueueKey) (ctrl.Result, error) { +func (r *Reconciler) Reconcile(_ context.Context, key controller.QueueKey) (ctrl.Result, error) { startTime := time.Now() clusterWideKey, ok := key.(keys.ClusterWideKey) if !ok { diff --git a/pkg/controllers/resourcechange/resourcechange_controller_test.go b/pkg/controllers/resourcechange/resourcechange_controller_test.go index 4b08cf1f1..aba8f878b 100644 --- a/pkg/controllers/resourcechange/resourcechange_controller_test.go +++ b/pkg/controllers/resourcechange/resourcechange_controller_test.go @@ -30,7 +30,7 @@ type fakeController struct { QueueObj []string } -func (w *fakeController) Run(ctx context.Context, workerNumber int) error { +func (w *fakeController) Run(_ context.Context, _ int) error { //TODO implement me panic("implement me") } diff --git a/pkg/controllers/work/apply_controller.go b/pkg/controllers/work/apply_controller.go index 2f033f5e3..4ab75bc6d 100644 --- a/pkg/controllers/work/apply_controller.go +++ b/pkg/controllers/work/apply_controller.go @@ -459,7 +459,7 @@ func (r *ApplyWorkReconciler) generateWorkCondition(results []applyResult, work } // Join starts to reconcile -func (r *ApplyWorkReconciler) Join(ctx context.Context) error { +func (r *ApplyWorkReconciler) Join(_ context.Context) error { if !r.joined.Load() { klog.InfoS("mark the apply work reconciler joined") } diff --git a/pkg/controllers/work/apply_controller_test.go b/pkg/controllers/work/apply_controller_test.go index 290278d0b..f91976cd6 100644 --- a/pkg/controllers/work/apply_controller_test.go +++ b/pkg/controllers/work/apply_controller_test.go @@ -87,7 +87,7 @@ type testMapper struct { meta.RESTMapper } -func (m testMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) { +func (m testMapper) RESTMapping(gk schema.GroupKind, _ ...string) (*meta.RESTMapping, error) { if gk.Kind == "Deployment" { return &meta.RESTMapping{ Resource: testGvr, diff --git a/pkg/resourcewatcher/event_handlers_test.go b/pkg/resourcewatcher/event_handlers_test.go index a63e03283..792e73ee9 100644 --- a/pkg/resourcewatcher/event_handlers_test.go +++ b/pkg/resourcewatcher/event_handlers_test.go @@ -310,11 +310,11 @@ type fakeController struct { Enqueued bool } -func (t *fakeController) Enqueue(obj interface{}) { +func (t *fakeController) Enqueue(_ interface{}) { t.Enqueued = true } -func (t *fakeController) Run(ctx context.Context, workerNumber int) error { +func (t *fakeController) Run(_ context.Context, _ int) error { //TODO implement me panic("implement me") } diff --git a/pkg/utils/controller/controller.go b/pkg/utils/controller/controller.go index acae41612..49cef22b5 100644 --- a/pkg/utils/controller/controller.go +++ b/pkg/utils/controller/controller.go @@ -115,6 +115,7 @@ func (w *controller) Run(ctx context.Context, workerNumber int) error { defer wg.Done() defer utilruntime.HandleCrash() // Run a worker thread that just dequeues items, processes them, and marks them done. + //revive:disable:empty-block for w.processNextWorkItem(ctx) { } }() diff --git a/pkg/webhook/pod/pod_validating_webhook.go b/pkg/webhook/pod/pod_validating_webhook.go index b878bc43f..347cf92aa 100644 --- a/pkg/webhook/pod/pod_validating_webhook.go +++ b/pkg/webhook/pod/pod_validating_webhook.go @@ -38,7 +38,7 @@ type podValidator struct { } // Handle podValidator denies a pod if it is not created in the system namespaces. -func (v *podValidator) Handle(ctx context.Context, req admission.Request) admission.Response { +func (v *podValidator) Handle(_ context.Context, req admission.Request) admission.Response { if req.Operation == admissionv1.Create { pod := &corev1.Pod{} err := v.decoder.Decode(req, pod) diff --git a/pkg/webhook/replicaset/replicaset_validating_webhook.go b/pkg/webhook/replicaset/replicaset_validating_webhook.go index f94f4ab90..c5b92110f 100644 --- a/pkg/webhook/replicaset/replicaset_validating_webhook.go +++ b/pkg/webhook/replicaset/replicaset_validating_webhook.go @@ -38,7 +38,7 @@ func Add(mgr manager.Manager) error { } // Handle replicaSetValidator denies all creation requests. -func (v *replicaSetValidator) Handle(ctx context.Context, req admission.Request) admission.Response { +func (v *replicaSetValidator) Handle(_ context.Context, req admission.Request) admission.Response { if req.Operation == admissionv1.Create { rs := &v1.ReplicaSet{} if err := v.decoder.Decode(req, rs); err != nil { diff --git a/test/integration/cluster_placement_test.go b/test/integration/cluster_placement_test.go index 426776839..ef490a445 100644 --- a/test/integration/cluster_placement_test.go +++ b/test/integration/cluster_placement_test.go @@ -1068,11 +1068,8 @@ var _ = Describe("Test Cluster Resource Placement Controller", func() { // verify that we have created the work object var clusterWork workv1alpha1.Work Eventually(func() error { - if err := k8sClient.Get(ctx, types.NamespacedName{ - Name: crp.Name, Namespace: fmt.Sprintf(utils.NamespaceNameFormat, clusterA.Name)}, &clusterWork); err != nil { - return err - } - return nil + return k8sClient.Get(ctx, types.NamespacedName{ + Name: crp.Name, Namespace: fmt.Sprintf(utils.NamespaceNameFormat, clusterA.Name)}, &clusterWork) }, timeout, interval).Should(Succeed(), "Failed to retrieve %s work", crp.Name) // Apply is Pending because work api controller is not being run for this test suite @@ -1673,10 +1670,7 @@ var _ = Describe("Test Cluster Resource Placement Controller", func() { } Eventually(func() error { - if err := k8sClient.Get(ctx, types.NamespacedName{Name: crp.Name, Namespace: fmt.Sprintf(utils.NamespaceNameFormat, clusterA.Name)}, &clusterWork); err != nil { - return err - } - return nil + return k8sClient.Get(ctx, types.NamespacedName{Name: crp.Name, Namespace: fmt.Sprintf(utils.NamespaceNameFormat, clusterA.Name)}, &clusterWork) }, timeout, interval).Should(Succeed(), "Failed to retrieve %s work", crp.Name) // update work for clusterA to have applied condition as false, and have one manifest condition as false