99 "github.com/openmcp-project/mcp-operator/internal/releasechannel"
1010 "github.com/openmcp-project/mcp-operator/internal/utils/apiserver"
1111
12+ mcpocfg "github.com/openmcp-project/mcp-operator/internal/config"
1213 apiservercontroller "github.com/openmcp-project/mcp-operator/internal/controller/core/apiserver"
1314 authenticationcontroller "github.com/openmcp-project/mcp-operator/internal/controller/core/authentication"
1415 authorizationcontroller "github.com/openmcp-project/mcp-operator/internal/controller/core/authorization"
@@ -17,12 +18,16 @@ import (
1718 landscapercontroller "github.com/openmcp-project/mcp-operator/internal/controller/core/landscaper"
1819 mcpcontroller "github.com/openmcp-project/mcp-operator/internal/controller/core/managedcontrolplane"
1920
21+ admissionv1 "k8s.io/api/admissionregistration/v1"
2022 "sigs.k8s.io/controller-runtime/pkg/cluster"
2123
24+ v2install "github.com/openmcp-project/openmcp-operator/api/install"
25+
2226 laasinstall "github.com/gardener/landscaper-service/pkg/apis/core/install"
2327 cocorev1beta1 "github.com/openmcp-project/control-plane-operator/api/v1beta1"
2428 "github.com/openmcp-project/controller-utils/pkg/init/webhooks"
2529 "github.com/openmcp-project/controller-utils/pkg/logging"
30+ "github.com/openmcp-project/controller-utils/pkg/resources"
2631 "github.com/spf13/cobra"
2732 "k8s.io/apimachinery/pkg/runtime"
2833 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -45,6 +50,8 @@ import (
4550 openmcpinstall "github.com/openmcp-project/mcp-operator/api/install"
4651)
4752
53+ const OperatorName = "ManagedControlPlaneOperator"
54+
4855func NewMCPOperatorCommand (ctx context.Context ) * cobra.Command {
4956 options := NewOptions ()
5057
@@ -143,6 +150,125 @@ func (o *Options) runInit(ctx context.Context) error {
143150 }
144151 }
145152
153+ // manage architecture immutability
154+ labelSelector := client.MatchingLabels {
155+ openmcpv1alpha1 .ManagedByLabel : OperatorName ,
156+ openmcpv1alpha1 .ManagedPurposeLabel : openmcpv1alpha1 .ManagedPurposeArchitectureImmutability ,
157+ }
158+ evapbs := & admissionv1.ValidatingAdmissionPolicyBindingList {}
159+ if err := crateClient .List (ctx , evapbs , labelSelector ); err != nil {
160+ return fmt .Errorf ("error listing ValidatingAdmissionPolicyBindings: %w" , err )
161+ }
162+ for _ , evapb := range evapbs .Items {
163+ if mcpocfg .Config .Architecture .Immutability .Disabled || evapb .Name != mcpocfg .Config .Architecture .Immutability .PolicyName {
164+ setupLog .Info ("Deleting existing ValidatingAdmissionPolicyBinding with architecture immutability purpose" , "name" , evapb .Name )
165+ if err := crateClient .Delete (ctx , & evapb ); client .IgnoreNotFound (err ) != nil {
166+ return fmt .Errorf ("error deleting ValidatingAdmissionPolicyBinding '%s': %w" , evapb .Name , err )
167+ }
168+ }
169+ }
170+ evaps := & admissionv1.ValidatingAdmissionPolicyList {}
171+ if err := crateClient .List (ctx , evaps , labelSelector ); err != nil {
172+ return fmt .Errorf ("error listing ValidatingAdmissionPolicies: %w" , err )
173+ }
174+ for _ , evap := range evaps .Items {
175+ if mcpocfg .Config .Architecture .Immutability .Disabled || evap .Name != mcpocfg .Config .Architecture .Immutability .PolicyName {
176+ setupLog .Info ("Deleting existing ValidatingAdmissionPolicy with architecture immutability purpose" , "name" , evap .Name )
177+ if err := crateClient .Delete (ctx , & evap ); client .IgnoreNotFound (err ) != nil {
178+ return fmt .Errorf ("error deleting ValidatingAdmissionPolicy '%s': %w" , evap .Name , err )
179+ }
180+ }
181+ }
182+ if ! mcpocfg .Config .Architecture .Immutability .Disabled {
183+ setupLog .Info ("Architecture immutability validation enabled, creating/updating ValidatingAdmissionPolicies ..." )
184+ vapm := resources .NewValidatingAdmissionPolicyMutator (mcpocfg .Config .Architecture .Immutability .PolicyName , admissionv1.ValidatingAdmissionPolicySpec {
185+ FailurePolicy : ptr .To (admissionv1 .Fail ),
186+ MatchConstraints : & admissionv1.MatchResources {
187+ ResourceRules : []admissionv1.NamedRuleWithOperations {
188+ {
189+ RuleWithOperations : admissionv1.RuleWithOperations {
190+ Operations : []admissionv1.OperationType {
191+ admissionv1 .Create ,
192+ admissionv1 .Update ,
193+ },
194+ Rule : admissionv1.Rule { // match all resources, actual restriction happens in the binding
195+ APIGroups : []string {"*" },
196+ APIVersions : []string {"*" },
197+ Resources : []string {"*" },
198+ },
199+ },
200+ },
201+ },
202+ },
203+ Variables : []admissionv1.Variable {
204+ {
205+ Name : "archLabel" ,
206+ Expression : fmt .Sprintf (`(has(object.metadata.labels) && "%s" in object.metadata.labels) ? object.metadata.labels["%s"] : ""` , openmcpv1alpha1 .ArchitectureVersionLabel , openmcpv1alpha1 .ArchitectureVersionLabel ),
207+ },
208+ {
209+ Name : "oldArchLabel" ,
210+ Expression : fmt .Sprintf (`(oldObject != null && has(oldObject.metadata.labels) && "%s" in oldObject.metadata.labels) ? oldObject.metadata.labels["%s"] : ""` , openmcpv1alpha1 .ArchitectureVersionLabel , openmcpv1alpha1 .ArchitectureVersionLabel ),
211+ },
212+ },
213+ Validations : []admissionv1.Validation {
214+ {
215+ Expression : fmt .Sprintf (`variables.archLabel == "%s" || variables.archLabel == "%s"` , openmcpv1alpha1 .ArchitectureV1 , openmcpv1alpha1 .ArchitectureV2 ),
216+ Message : fmt .Sprintf (`The label "%s" must be set and its value must be either "%s" or "%s".` , openmcpv1alpha1 .ArchitectureVersionLabel , openmcpv1alpha1 .ArchitectureV1 , openmcpv1alpha1 .ArchitectureV2 ),
217+ },
218+ {
219+ Expression : fmt .Sprintf (`request.operation == "CREATE" || (variables.oldArchLabel == "" && variables.archLabel == "%s") || (variables.oldArchLabel == variables.archLabel)` , openmcpv1alpha1 .ArchitectureV1 ),
220+ Message : fmt .Sprintf (`The label "%s" is immutable, it may not be changed or removed once set. Adding it to existing resources is only allowed with "%s" as value.` , openmcpv1alpha1 .ArchitectureVersionLabel , openmcpv1alpha1 .ArchitectureV1 ),
221+ },
222+ },
223+ })
224+ vapm .MetadataMutator ().WithLabels (map [string ]string {
225+ openmcpv1alpha1 .ManagedByLabel : OperatorName ,
226+ openmcpv1alpha1 .ManagedPurposeLabel : openmcpv1alpha1 .ManagedPurposeArchitectureImmutability ,
227+ })
228+ if err := resources .CreateOrUpdateResource (ctx , crateClient , vapm ); err != nil {
229+ return fmt .Errorf ("error creating/updating ValidatingAdmissionPolicy for architecture immutability: %w" , err )
230+ }
231+
232+ vapbm := resources .NewValidatingAdmissionPolicyBindingMutator (mcpocfg .Config .Architecture .Immutability .PolicyName , admissionv1.ValidatingAdmissionPolicyBindingSpec {
233+ PolicyName : mcpocfg .Config .Architecture .Immutability .PolicyName ,
234+ ValidationActions : []admissionv1.ValidationAction {
235+ admissionv1 .Deny ,
236+ },
237+ MatchResources : & admissionv1.MatchResources {
238+ ResourceRules : []admissionv1.NamedRuleWithOperations {
239+ {
240+ RuleWithOperations : admissionv1.RuleWithOperations {
241+ Operations : []admissionv1.OperationType {
242+ admissionv1 .Create ,
243+ admissionv1 .Update ,
244+ },
245+ Rule : admissionv1.Rule {
246+ APIGroups : []string {openmcpv1alpha1 .GroupVersion .Group },
247+ APIVersions : []string {openmcpv1alpha1 .GroupVersion .Version },
248+ Resources : []string {
249+ "apiservers" ,
250+ "landscapers" ,
251+ "cloudorchestrators" ,
252+ "authentications" ,
253+ "authorizations" ,
254+ },
255+ },
256+ },
257+ },
258+ },
259+ },
260+ })
261+ vapbm .MetadataMutator ().WithLabels (map [string ]string {
262+ openmcpv1alpha1 .ManagedByLabel : OperatorName ,
263+ openmcpv1alpha1 .ManagedPurposeLabel : openmcpv1alpha1 .ManagedPurposeArchitectureImmutability ,
264+ })
265+ if err := resources .CreateOrUpdateResource (ctx , crateClient , vapbm ); err != nil {
266+ return fmt .Errorf ("error creating/updating ValidatingAdmissionPolicyBinding for architecture immutability: %w" , err )
267+ }
268+
269+ setupLog .Info ("ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding for architecture immutability created/updated" )
270+ }
271+
146272 return nil
147273}
148274
@@ -219,7 +345,15 @@ func (o *Options) run(ctx context.Context) error {
219345
220346 if o .ActiveControllers .Has (ControllerIDAPIServer ) {
221347 // APIServer controller
222- apiServerProvider , err := apiservercontroller .NewAPIServerProvider (ctx , mgr .GetClient (), o .APIServerConfig )
348+ // build platform cluster client for v2 path
349+ v2scheme := v2install .InstallOperatorAPIs (runtime .NewScheme ())
350+ platformClient , err := client .New (o .LaaSClusterConfig , client.Options {
351+ Scheme : v2scheme ,
352+ })
353+ if err != nil {
354+ return fmt .Errorf ("error creating platform cluster client: %w" , err )
355+ }
356+ apiServerProvider , err := apiservercontroller .NewAPIServerProvider (ctx , mgr .GetClient (), platformClient , o .APIServerConfig )
223357 if err != nil {
224358 return fmt .Errorf ("error creating %s: %w" , apiservercontroller .ControllerName , err )
225359 }
0 commit comments