Skip to content

Commit ca9b195

Browse files
authored
feat: Loosening webhook to allow for kubernetes-fleet.io/ labels to be modified (#210)
1 parent f6245da commit ca9b195

File tree

9 files changed

+171
-50
lines changed

9 files changed

+171
-50
lines changed

apis/placement/v1beta1/binding_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const (
2828
// SchedulerBindingCleanupFinalizer is a finalizer added to bindings to ensure we can look up the
2929
// corresponding CRP name for deleting bindings to trigger a new scheduling cycle.
3030
// TODO: migrate the finalizer to the new name "scheduler-binding-cleanup" in the future.
31-
SchedulerBindingCleanupFinalizer = fleetPrefix + "scheduler-crb-cleanup"
31+
SchedulerBindingCleanupFinalizer = FleetPrefix + "scheduler-crb-cleanup"
3232
)
3333

3434
// make sure the BindingObj and BindingObjList interfaces are implemented by the

apis/placement/v1beta1/clusterresourceplacement_types.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ import (
2929
const (
3030
// PlacementCleanupFinalizer is a finalizer added by the placement controller to all placement objects, to make sure
3131
// that the placement controller can react to placement object deletions if necessary.
32-
PlacementCleanupFinalizer = fleetPrefix + "crp-cleanup"
32+
PlacementCleanupFinalizer = FleetPrefix + "crp-cleanup"
3333

3434
// SchedulerCleanupFinalizer is a finalizer added by the scheduler to placement objects, to make sure
3535
// that all bindings derived from a placement object can be cleaned up after the placement object is deleted.
36-
SchedulerCleanupFinalizer = fleetPrefix + "scheduler-cleanup"
36+
SchedulerCleanupFinalizer = FleetPrefix + "scheduler-cleanup"
3737
)
3838

3939
// make sure the PlacementObj and PlacementObjList interfaces are implemented by the
@@ -1521,7 +1521,7 @@ func (m *ClusterResourcePlacement) SetPlacementStatus(status PlacementStatus) {
15211521
const (
15221522
// ResourcePlacementCleanupFinalizer is a finalizer added by the RP controller to all RPs, to make sure
15231523
// that the RP controller can react to RP deletions if necessary.
1524-
ResourcePlacementCleanupFinalizer = fleetPrefix + "rp-cleanup"
1524+
ResourcePlacementCleanupFinalizer = FleetPrefix + "rp-cleanup"
15251525
)
15261526

15271527
// +genclient

apis/placement/v1beta1/commons.go

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,32 +58,32 @@ const (
5858
)
5959

6060
const (
61-
// fleetPrefix is the prefix used for official fleet labels/annotations.
61+
// FleetPrefix is the prefix used for official fleet labels/annotations.
6262
// Unprefixed labels/annotations are reserved for end-users
6363
// See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#label-selector-and-annotation-conventions
64-
fleetPrefix = "kubernetes-fleet.io/"
64+
FleetPrefix = "kubernetes-fleet.io/"
6565

6666
// MemberClusterFinalizer is used to make sure that we handle gc of all the member cluster resources on the hub cluster.
67-
MemberClusterFinalizer = fleetPrefix + "membercluster-finalizer"
67+
MemberClusterFinalizer = FleetPrefix + "membercluster-finalizer"
6868

6969
// WorkFinalizer is used by the work generator to make sure that the binding is not deleted until the work objects
7070
// it generates are all deleted, or used by the work controller to make sure the work has been deleted in the member
7171
// cluster.
72-
WorkFinalizer = fleetPrefix + "work-cleanup"
72+
WorkFinalizer = FleetPrefix + "work-cleanup"
7373

7474
// ClusterResourcePlacementStatusCleanupFinalizer is a finalizer added by the controller to all ClusterResourcePlacementStatus objects, to make sure
7575
// that the controller can react to ClusterResourcePlacementStatus deletions if necessary.
76-
ClusterResourcePlacementStatusCleanupFinalizer = fleetPrefix + "cluster-resource-placement-status-cleanup"
76+
ClusterResourcePlacementStatusCleanupFinalizer = FleetPrefix + "cluster-resource-placement-status-cleanup"
7777

7878
// PlacementTrackingLabel points to the placement that creates this resource binding.
7979
// TODO: migrate the label content to "parent-placement" to work with both the PR and CRP
80-
PlacementTrackingLabel = fleetPrefix + "parent-CRP"
80+
PlacementTrackingLabel = FleetPrefix + "parent-CRP"
8181

8282
// IsLatestSnapshotLabel indicates if the snapshot is the latest one.
83-
IsLatestSnapshotLabel = fleetPrefix + "is-latest-snapshot"
83+
IsLatestSnapshotLabel = FleetPrefix + "is-latest-snapshot"
8484

8585
// FleetResourceLabelKey indicates that the resource is a fleet resource.
86-
FleetResourceLabelKey = fleetPrefix + "is-fleet-resource"
86+
FleetResourceLabelKey = FleetPrefix + "is-fleet-resource"
8787

8888
// FirstWorkNameFmt is the format of the name of the work generated with the first resource snapshot.
8989
// The name of the first work is {crpName}-work.
@@ -105,59 +105,59 @@ const (
105105
WorkNameWithEnvelopeCRFmt = "%s-envelope-%s"
106106

107107
// ParentClusterResourceOverrideSnapshotHashAnnotation is the annotation to work that contains the hash of the parent cluster resource override snapshot list.
108-
ParentClusterResourceOverrideSnapshotHashAnnotation = fleetPrefix + "parent-cluster-resource-override-snapshot-hash"
108+
ParentClusterResourceOverrideSnapshotHashAnnotation = FleetPrefix + "parent-cluster-resource-override-snapshot-hash"
109109

110110
// ParentResourceOverrideSnapshotHashAnnotation is the annotation to work that contains the hash of the parent resource override snapshot list.
111-
ParentResourceOverrideSnapshotHashAnnotation = fleetPrefix + "parent-resource-override-snapshot-hash"
111+
ParentResourceOverrideSnapshotHashAnnotation = FleetPrefix + "parent-resource-override-snapshot-hash"
112112

113113
// ParentResourceSnapshotNameAnnotation is the annotation applied to work that contains the name of the master resource snapshot that generates the work.
114-
ParentResourceSnapshotNameAnnotation = fleetPrefix + "parent-resource-snapshot-name"
114+
ParentResourceSnapshotNameAnnotation = FleetPrefix + "parent-resource-snapshot-name"
115115

116116
// ParentResourceSnapshotIndexLabel is the label applied to work that contains the index of the resource snapshot that generates the work.
117-
ParentResourceSnapshotIndexLabel = fleetPrefix + "parent-resource-snapshot-index"
117+
ParentResourceSnapshotIndexLabel = FleetPrefix + "parent-resource-snapshot-index"
118118

119119
// ParentBindingLabel is the label applied to work that contains the name of the binding that generates the work.
120-
ParentBindingLabel = fleetPrefix + "parent-resource-binding"
120+
ParentBindingLabel = FleetPrefix + "parent-resource-binding"
121121

122122
// ParentNamespaceLabel is the label applied to work that contains the namespace of the binding that generates the work.
123-
ParentNamespaceLabel = fleetPrefix + "parent-placement-namespace"
123+
ParentNamespaceLabel = FleetPrefix + "parent-placement-namespace"
124124

125125
// CRPGenerationAnnotation indicates the generation of the placement from which an object is derived or last updated.
126126
// TODO: rename this variable
127-
CRPGenerationAnnotation = fleetPrefix + "CRP-generation"
127+
CRPGenerationAnnotation = FleetPrefix + "CRP-generation"
128128

129129
// EnvelopeConfigMapAnnotation indicates the configmap is an envelope configmap containing resources we need to apply to the member cluster instead of the configMap itself.
130-
EnvelopeConfigMapAnnotation = fleetPrefix + "envelope-configmap"
130+
EnvelopeConfigMapAnnotation = FleetPrefix + "envelope-configmap"
131131

132132
// EnvelopeTypeLabel marks the work object as generated from an envelope object.
133133
// The value of the annotation is the type of the envelope object.
134-
EnvelopeTypeLabel = fleetPrefix + "envelope-work"
134+
EnvelopeTypeLabel = FleetPrefix + "envelope-work"
135135

136136
// EnvelopeNamespaceLabel contains the namespace of the envelope object that the work is generated from.
137-
EnvelopeNamespaceLabel = fleetPrefix + "envelope-namespace"
137+
EnvelopeNamespaceLabel = FleetPrefix + "envelope-namespace"
138138

139139
// EnvelopeNameLabel contains the name of the envelope object that the work is generated from.
140-
EnvelopeNameLabel = fleetPrefix + "envelope-name"
140+
EnvelopeNameLabel = FleetPrefix + "envelope-name"
141141

142142
// PreviousBindingStateAnnotation records the previous state of a binding.
143143
// This is used to remember if an "unscheduled" binding was moved from a "bound" state or a "scheduled" state.
144-
PreviousBindingStateAnnotation = fleetPrefix + "previous-binding-state"
144+
PreviousBindingStateAnnotation = FleetPrefix + "previous-binding-state"
145145

146146
// ClusterStagedUpdateRunFinalizer is used by the ClusterStagedUpdateRun controller to make sure that the ClusterStagedUpdateRun
147147
// object is not deleted until all its dependent resources are deleted.
148-
ClusterStagedUpdateRunFinalizer = fleetPrefix + "stagedupdaterun-finalizer"
148+
ClusterStagedUpdateRunFinalizer = FleetPrefix + "stagedupdaterun-finalizer"
149149

150150
// TargetUpdateRunLabel indicates the target update run on a staged run related object.
151-
TargetUpdateRunLabel = fleetPrefix + "targetupdaterun"
151+
TargetUpdateRunLabel = FleetPrefix + "targetupdaterun"
152152

153153
// UpdateRunDeleteStageName is the name of delete stage in the staged update run.
154-
UpdateRunDeleteStageName = fleetPrefix + "deleteStage"
154+
UpdateRunDeleteStageName = FleetPrefix + "deleteStage"
155155

156156
// IsLatestUpdateRunApprovalLabel indicates if the approval is the latest approval on a staged run.
157-
IsLatestUpdateRunApprovalLabel = fleetPrefix + "isLatestUpdateRunApproval"
157+
IsLatestUpdateRunApprovalLabel = FleetPrefix + "isLatestUpdateRunApproval"
158158

159159
// TargetUpdatingStageNameLabel indicates the updating stage name on a staged run related object.
160-
TargetUpdatingStageNameLabel = fleetPrefix + "targetUpdatingStage"
160+
TargetUpdatingStageNameLabel = FleetPrefix + "targetUpdatingStage"
161161

162162
// ApprovalTaskNameFmt is the format of the approval task name.
163163
ApprovalTaskNameFmt = "%s-%s"

apis/placement/v1beta1/overridesnapshot_types.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2121
const (
2222

2323
// OverrideIndexLabel is the label that indicate the policy snapshot index of a cluster policy.
24-
OverrideIndexLabel = fleetPrefix + "override-index"
24+
OverrideIndexLabel = FleetPrefix + "override-index"
2525

2626
// OverrideSnapshotNameFmt is clusterResourceOverrideSnapshot name format: {CROName}-{OverrideSnapshotIndex}.
2727
OverrideSnapshotNameFmt = "%s-%d"
2828

2929
// OverrideTrackingLabel is the label that points to the cluster resource override that creates a resource snapshot.
30-
OverrideTrackingLabel = fleetPrefix + "parent-resource-override"
30+
OverrideTrackingLabel = FleetPrefix + "parent-resource-override"
3131

3232
// OverrideFinalizer is a finalizer added by the override controllers to all override, to make sure
3333
// that the override controller can react to override deletions if necessary.
34-
OverrideFinalizer = fleetPrefix + "override-cleanup"
34+
OverrideFinalizer = FleetPrefix + "override-cleanup"
3535
)
3636

3737
// +genclient

apis/placement/v1beta1/policysnapshot_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ import (
2626

2727
const (
2828
// PolicyIndexLabel is the label that indicate the policy snapshot index of a cluster policy.
29-
PolicyIndexLabel = fleetPrefix + "policy-index"
29+
PolicyIndexLabel = FleetPrefix + "policy-index"
3030

3131
// PolicySnapshotNameFmt is clusterPolicySnapshot name format: {CRPName}-{PolicySnapshotIndex}.
3232
PolicySnapshotNameFmt = "%s-%d"
3333

3434
// NumberOfClustersAnnotation is the annotation that indicates how many clusters should be selected for selectN placement type.
35-
NumberOfClustersAnnotation = fleetPrefix + "number-of-clusters"
35+
NumberOfClustersAnnotation = FleetPrefix + "number-of-clusters"
3636
)
3737

3838
// make sure the PolicySnapshotObj and PolicySnapshotList interfaces are implemented by the

apis/placement/v1beta1/resourcesnapshot_types.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,23 @@ import (
2727

2828
const (
2929
// ResourceIndexLabel is the label that indicate the resource snapshot index of a cluster resource snapshot.
30-
ResourceIndexLabel = fleetPrefix + "resource-index"
30+
ResourceIndexLabel = FleetPrefix + "resource-index"
3131

3232
// ResourceGroupHashAnnotation is the annotation that contains the value of the sha-256 hash
3333
// value of all the snapshots belong to the same snapshot index.
34-
ResourceGroupHashAnnotation = fleetPrefix + "resource-hash"
34+
ResourceGroupHashAnnotation = FleetPrefix + "resource-hash"
3535

3636
// NumberOfEnvelopedObjectsAnnotation is the annotation that contains the number of the enveloped objects in the resource snapshot group.
37-
NumberOfEnvelopedObjectsAnnotation = fleetPrefix + "number-of-enveloped-object"
37+
NumberOfEnvelopedObjectsAnnotation = FleetPrefix + "number-of-enveloped-object"
3838

3939
// NumberOfResourceSnapshotsAnnotation is the annotation that contains the total number of resource snapshots.
40-
NumberOfResourceSnapshotsAnnotation = fleetPrefix + "number-of-resource-snapshots"
40+
NumberOfResourceSnapshotsAnnotation = FleetPrefix + "number-of-resource-snapshots"
4141

4242
// SubindexOfResourceSnapshotAnnotation is the annotation to store the subindex of resource snapshot in the group.
43-
SubindexOfResourceSnapshotAnnotation = fleetPrefix + "subindex-of-resource-snapshot"
43+
SubindexOfResourceSnapshotAnnotation = FleetPrefix + "subindex-of-resource-snapshot"
4444

4545
// NextResourceSnapshotCandidateDetectionTimeAnnotation is the annotation to store the time of next resourceSnapshot candidate detected by the controller.
46-
NextResourceSnapshotCandidateDetectionTimeAnnotation = fleetPrefix + "next-resource-snapshot-candidate-detection-time"
46+
NextResourceSnapshotCandidateDetectionTimeAnnotation = FleetPrefix + "next-resource-snapshot-candidate-detection-time"
4747

4848
// ResourceSnapshotNameFmt is resourcePolicySnapshot name format: {CRPName}-{resourceIndex}-snapshot.
4949
ResourceSnapshotNameFmt = "%s-%d-snapshot"

apis/placement/v1beta1/work_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ import (
4040
// The following definitions are originally declared in the controllers/workv1alpha1/manager.go file.
4141
const (
4242
// ManifestHashAnnotation is the annotation that indicates whether the spec of the object has been changed or not.
43-
ManifestHashAnnotation = fleetPrefix + "spec-hash"
43+
ManifestHashAnnotation = FleetPrefix + "spec-hash"
4444

4545
// LastAppliedConfigAnnotation is to record the last applied configuration on the object.
46-
LastAppliedConfigAnnotation = fleetPrefix + "last-applied-configuration"
46+
LastAppliedConfigAnnotation = FleetPrefix + "last-applied-configuration"
4747

4848
// WorkConditionTypeApplied represents workload in Work is applied successfully on the spoke cluster.
4949
WorkConditionTypeApplied = "Applied"

pkg/webhook/validation/uservalidation.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
1919

2020
clusterv1beta1 "github.com/kubefleet-dev/kubefleet/apis/cluster/v1beta1"
21+
placementv1beta1 "github.com/kubefleet-dev/kubefleet/apis/placement/v1beta1"
2122
fleetv1alpha1 "github.com/kubefleet-dev/kubefleet/apis/v1alpha1"
2223
"github.com/kubefleet-dev/kubefleet/pkg/utils"
2324
)
@@ -109,14 +110,11 @@ func ValidateFleetMemberClusterUpdate(currentMC, oldMC clusterv1beta1.MemberClus
109110
return admission.Denied(err.Error())
110111
}
111112

112-
// Users are no longer allowed to modify labels of fleet member cluster through webhook.
113-
// This will be disabled until member labels are accessible through CLI
114-
if denyModifyMemberClusterLabels {
115-
isLabelUpdated := isMapFieldUpdated(currentMC.GetLabels(), oldMC.GetLabels())
116-
if isLabelUpdated && !isUserInGroup(userInfo, mastersGroup) {
117-
klog.V(2).InfoS(DeniedModifyMemberClusterLabels, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
118-
return admission.Denied(DeniedModifyMemberClusterLabels)
119-
}
113+
isLabelUpdated := isMapFieldUpdated(currentMC.GetLabels(), oldMC.GetLabels())
114+
if isLabelUpdated && !isUserInGroup(userInfo, mastersGroup) && shouldDenyLabelModification(currentMC.GetLabels(), oldMC.GetLabels(), denyModifyMemberClusterLabels) {
115+
// allow any user to modify kubernetes-fleet.io/* labels, but restricts other label modifications given denyModifyMemberClusterLabels is true.
116+
klog.V(2).InfoS(DeniedModifyMemberClusterLabels, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
117+
return admission.Denied(DeniedModifyMemberClusterLabels)
120118
}
121119

122120
isAnnotationUpdated := isFleetAnnotationUpdated(currentMC.Annotations, oldMC.Annotations)
@@ -179,6 +177,29 @@ func isUserInGroup(userInfo authenticationv1.UserInfo, groupName string) bool {
179177
return slices.Contains(userInfo.Groups, groupName)
180178
}
181179

180+
// shouldDenyLabelModification returns true if any labels (besides kubernetes-fleet.io/* labels) are being modified and denyModifyMemberClusterLabels is true.
181+
func shouldDenyLabelModification(currentLabels, oldLabels map[string]string, denyModifyMemberClusterLabels bool) bool {
182+
if !denyModifyMemberClusterLabels {
183+
return false
184+
}
185+
for k, v := range currentLabels {
186+
oldV, exists := oldLabels[k]
187+
if !exists || oldV != v {
188+
if !strings.HasPrefix(k, placementv1beta1.FleetPrefix) {
189+
return true
190+
}
191+
}
192+
}
193+
for k := range oldLabels {
194+
if _, exists := currentLabels[k]; !exists {
195+
if !strings.HasPrefix(k, placementv1beta1.FleetPrefix) {
196+
return true
197+
}
198+
}
199+
}
200+
return false
201+
}
202+
182203
// isMemberClusterMapFieldUpdated return true if member cluster label is updated.
183204
func isMapFieldUpdated(currentMap, oldMap map[string]string) bool {
184205
return !reflect.DeepEqual(currentMap, oldMap)

0 commit comments

Comments
 (0)