Skip to content

Commit c65af88

Browse files
committed
implement mcp purpose override
1 parent 1e9d280 commit c65af88

File tree

4 files changed

+140
-6
lines changed

4 files changed

+140
-6
lines changed

api/constants/constants.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const (
1616

1717
// ManagedByLabel is used to indicate which controller manages the resource.
1818
ManagedByLabel = OpenMCPGroupName + "/managed-by"
19+
// ManagedPurposeLabel is used to indicate the purpose of the resource.
20+
ManagedPurposeLabel = OpenMCPGroupName + "/managed-purpose"
1921

2022
// OnboardingNameLabel is used to store the name on the onboarding cluster of a resource.
2123
OnboardingNameLabel = OpenMCPGroupName + "/onboarding-name"

api/core/v2alpha1/constants.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ const (
88
)
99

1010
const (
11-
MCPNameLabel = GroupName + "/mcp-name"
12-
MCPNamespaceLabel = GroupName + "/mcp-namespace"
13-
OIDCProviderLabel = GroupName + "/oidc-provider"
11+
MCPNameLabel = GroupName + "/mcp-name"
12+
MCPNamespaceLabel = GroupName + "/mcp-namespace"
13+
OIDCProviderLabel = GroupName + "/oidc-provider"
14+
MCPPurposeOverrideLabel = GroupName + "/purpose-override"
15+
16+
// ManagedPurposeMCPPurposeOverride is used as value for the managed purpose label. It must not be modified.
17+
ManagedPurposeMCPPurposeOverride = "mcp-purpose-override"
1418

1519
MCPFinalizer = GroupName + "/mcp"
1620

cmd/openmcp-operator/app/mcp/init.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,28 @@ import (
77
"time"
88

99
"github.com/spf13/cobra"
10+
admissionv1 "k8s.io/api/admissionregistration/v1"
1011
rbacv1 "k8s.io/api/rbac/v1"
1112
"k8s.io/apimachinery/pkg/runtime"
13+
"k8s.io/utils/ptr"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
1215

1316
crdutil "github.com/openmcp-project/controller-utils/pkg/crds"
17+
"github.com/openmcp-project/controller-utils/pkg/resources"
1418

1519
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
1620
apiconst "github.com/openmcp-project/openmcp-operator/api/constants"
21+
corev2alpha1 "github.com/openmcp-project/openmcp-operator/api/core/v2alpha1"
1722
"github.com/openmcp-project/openmcp-operator/api/crds"
1823
"github.com/openmcp-project/openmcp-operator/api/install"
1924
"github.com/openmcp-project/openmcp-operator/cmd/openmcp-operator/app/options"
2025
"github.com/openmcp-project/openmcp-operator/internal/controllers/managedcontrolplane"
2126
"github.com/openmcp-project/openmcp-operator/lib/clusteraccess"
2227
)
2328

29+
// currently hard-coded, can be made configurable in the future if needed
30+
const MCPPurposeOverrideValidationPolicyName = "mcp-purpose-override-validation"
31+
2432
func NewInitCommand(po *options.PersistentOptions) *cobra.Command {
2533
opts := &InitOptions{
2634
PersistentOptions: po,
@@ -94,6 +102,11 @@ func (o *InitOptions) Run(ctx context.Context) error {
94102
Resources: []string{"customresourcedefinitions"},
95103
Verbs: []string{"*"},
96104
},
105+
{
106+
APIGroups: []string{"admissionregistration.k8s.io"},
107+
Resources: []string{"validatingadmissionpolicies", "validatingadmissionpolicybindings"},
108+
Verbs: []string{"*"},
109+
},
97110
},
98111
},
99112
})
@@ -111,6 +124,115 @@ func (o *InitOptions) Run(ctx context.Context) error {
111124
if err := crdManager.CreateOrUpdateCRDs(ctx, &o.Log); err != nil {
112125
return fmt.Errorf("error creating/updating CRDs: %w", err)
113126
}
127+
128+
// ensure ValidatingAdmissionPolicy to prevent removal or changes to the MCP purpose override label
129+
labelSelector := client.MatchingLabels{
130+
apiconst.ManagedByLabel: managedcontrolplane.ControllerName,
131+
apiconst.ManagedPurposeLabel: corev2alpha1.ManagedPurposeMCPPurposeOverride,
132+
}
133+
evapbs := &admissionv1.ValidatingAdmissionPolicyBindingList{}
134+
if err := onboardingCluster.Client().List(ctx, evapbs, labelSelector); err != nil {
135+
return fmt.Errorf("error listing ValidatingAdmissionPolicyBindings: %w", err)
136+
}
137+
for _, evapb := range evapbs.Items {
138+
if evapb.Name != MCPPurposeOverrideValidationPolicyName {
139+
setupLog.Info("Deleting existing ValidatingAdmissionPolicyBinding with architecture immutability purpose", "name", evapb.Name)
140+
if err := onboardingCluster.Client().Delete(ctx, &evapb); client.IgnoreNotFound(err) != nil {
141+
return fmt.Errorf("error deleting ValidatingAdmissionPolicyBinding '%s': %w", evapb.Name, err)
142+
}
143+
}
144+
}
145+
evaps := &admissionv1.ValidatingAdmissionPolicyList{}
146+
if err := onboardingCluster.Client().List(ctx, evaps, labelSelector); err != nil {
147+
return fmt.Errorf("error listing ValidatingAdmissionPolicies: %w", err)
148+
}
149+
for _, evap := range evaps.Items {
150+
if evap.Name != MCPPurposeOverrideValidationPolicyName {
151+
setupLog.Info("Deleting existing ValidatingAdmissionPolicy with architecture immutability purpose", "name", evap.Name)
152+
if err := onboardingCluster.Client().Delete(ctx, &evap); client.IgnoreNotFound(err) != nil {
153+
return fmt.Errorf("error deleting ValidatingAdmissionPolicy '%s': %w", evap.Name, err)
154+
}
155+
}
156+
}
157+
setupLog.Info("creating/updating ValidatingAdmissionPolicies to prevent undesired changes to the MCP purpose override label ...")
158+
vapm := resources.NewValidatingAdmissionPolicyMutator(MCPPurposeOverrideValidationPolicyName, admissionv1.ValidatingAdmissionPolicySpec{
159+
FailurePolicy: ptr.To(admissionv1.Fail),
160+
MatchConstraints: &admissionv1.MatchResources{
161+
ResourceRules: []admissionv1.NamedRuleWithOperations{
162+
{
163+
RuleWithOperations: admissionv1.RuleWithOperations{
164+
Operations: []admissionv1.OperationType{
165+
admissionv1.Create,
166+
admissionv1.Update,
167+
},
168+
Rule: admissionv1.Rule{ // match all resources, actual restriction happens in the binding
169+
APIGroups: []string{"*"},
170+
APIVersions: []string{"*"},
171+
Resources: []string{"*"},
172+
},
173+
},
174+
},
175+
},
176+
},
177+
Variables: []admissionv1.Variable{
178+
{
179+
Name: "purposeOverrideLabel",
180+
Expression: fmt.Sprintf(`(has(object.metadata.labels) && "%s" in object.metadata.labels) ? object.metadata.labels["%s"] : ""`, corev2alpha1.MCPPurposeOverrideLabel, corev2alpha1.MCPPurposeOverrideLabel),
181+
},
182+
{
183+
Name: "oldPurposeOverrideLabel",
184+
Expression: fmt.Sprintf(`(oldObject != null && has(oldObject.metadata.labels) && "%s" in oldObject.metadata.labels) ? oldObject.metadata.labels["%s"] : ""`, corev2alpha1.MCPPurposeOverrideLabel, corev2alpha1.MCPPurposeOverrideLabel),
185+
},
186+
},
187+
Validations: []admissionv1.Validation{
188+
{
189+
Expression: `request.operation == "CREATE" || (variables.oldPurposeOverrideLabel == variables.purposeOverrideLabel)`,
190+
Message: fmt.Sprintf(`The label "%s" is immutable, it cannot be added after creation and is not allowed to be changed or removed once set.`, corev2alpha1.MCPPurposeOverrideLabel),
191+
},
192+
},
193+
})
194+
vapm.MetadataMutator().WithLabels(map[string]string{
195+
apiconst.ManagedByLabel: managedcontrolplane.ControllerName,
196+
apiconst.ManagedPurposeLabel: corev2alpha1.ManagedPurposeMCPPurposeOverride,
197+
})
198+
if err := resources.CreateOrUpdateResource(ctx, onboardingCluster.Client(), vapm); err != nil {
199+
return fmt.Errorf("error creating/updating ValidatingAdmissionPolicy for mcp purpose override validation: %w", err)
200+
}
201+
202+
vapbm := resources.NewValidatingAdmissionPolicyBindingMutator(MCPPurposeOverrideValidationPolicyName, admissionv1.ValidatingAdmissionPolicyBindingSpec{
203+
PolicyName: MCPPurposeOverrideValidationPolicyName,
204+
ValidationActions: []admissionv1.ValidationAction{
205+
admissionv1.Deny,
206+
},
207+
MatchResources: &admissionv1.MatchResources{
208+
ResourceRules: []admissionv1.NamedRuleWithOperations{
209+
{
210+
RuleWithOperations: admissionv1.RuleWithOperations{
211+
Operations: []admissionv1.OperationType{
212+
admissionv1.Create,
213+
admissionv1.Update,
214+
},
215+
Rule: admissionv1.Rule{
216+
APIGroups: []string{corev2alpha1.GroupVersion.Group},
217+
APIVersions: []string{corev2alpha1.GroupVersion.Version},
218+
Resources: []string{
219+
"managedcontrolplanev2s",
220+
},
221+
},
222+
},
223+
},
224+
},
225+
},
226+
})
227+
vapbm.MetadataMutator().WithLabels(map[string]string{
228+
apiconst.ManagedByLabel: managedcontrolplane.ControllerName,
229+
apiconst.ManagedPurposeLabel: corev2alpha1.ManagedPurposeMCPPurposeOverride,
230+
})
231+
if err := resources.CreateOrUpdateResource(ctx, onboardingCluster.Client(), vapbm); err != nil {
232+
return fmt.Errorf("error creating/updating ValidatingAdmissionPolicyBinding for mcp purpose override validation: %w", err)
233+
}
234+
setupLog.Info("ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding for mcp purpose override validation created/updated")
235+
114236
log.Info("Finished init command")
115237
return nil
116238
}

internal/controllers/managedcontrolplane/controller.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,17 +204,23 @@ func (r *ManagedControlPlaneReconciler) handleCreateOrUpdate(ctx context.Context
204204
cr := &clustersv1alpha1.ClusterRequest{}
205205
cr.Name = mcp.Name
206206
cr.Namespace = platformNamespace
207+
// determine cluster request purpose
208+
purpose := r.Config.MCPClusterPurpose
209+
if override, ok := mcp.Labels[corev2alpha1.MCPPurposeOverrideLabel]; ok && override != "" {
210+
log.Info("Using purpose override from MCP label", "purposeOverride", override)
211+
purpose = override
212+
}
207213
if err := r.PlatformCluster.Client().Get(ctx, client.ObjectKeyFromObject(cr), cr); err != nil {
208214
if !apierrors.IsNotFound(err) {
209215
rr.ReconcileError = errutils.WithReason(fmt.Errorf("unable to get ClusterRequest '%s/%s': %w", cr.Namespace, cr.Name, err), cconst.ReasonPlatformClusterInteractionProblem)
210216
createCon(corev2alpha1.ConditionClusterRequestReady, metav1.ConditionFalse, rr.ReconcileError.Reason(), rr.ReconcileError.Error())
211217
return rr
212218
}
213219

214-
log.Info("ClusterRequest not found, creating it", "clusterRequestName", cr.Name, "clusterRequestNamespace", cr.Namespace, "purpose", r.Config.MCPClusterPurpose)
220+
log.Info("ClusterRequest not found, creating it", "clusterRequestName", cr.Name, "clusterRequestNamespace", cr.Namespace, "purpose", purpose)
215221
cr.Labels = mcpLabels
216222
cr.Spec = clustersv1alpha1.ClusterRequestSpec{
217-
Purpose: r.Config.MCPClusterPurpose,
223+
Purpose: purpose,
218224
WaitForClusterDeletion: ptr.To(true),
219225
}
220226
if err := r.PlatformCluster.Client().Create(ctx, cr); err != nil {
@@ -223,7 +229,7 @@ func (r *ManagedControlPlaneReconciler) handleCreateOrUpdate(ctx context.Context
223229
return rr
224230
}
225231
} else {
226-
log.Debug("ClusterRequest found", "clusterRequestName", cr.Name, "clusterRequestNamespace", cr.Namespace, "purposeInConfig", r.Config.MCPClusterPurpose, "purposeInClusterRequest", cr.Spec.Purpose)
232+
log.Debug("ClusterRequest found", "clusterRequestName", cr.Name, "clusterRequestNamespace", cr.Namespace, "configuredPurpose", purpose, "purposeInClusterRequest", cr.Spec.Purpose)
227233
}
228234

229235
// check if the ClusterRequest is ready

0 commit comments

Comments
 (0)