Skip to content

Commit 847b6e7

Browse files
authored
feat: add Landscaper v2 bridge (#115)
* implement landscaper v2 bridge * add tests for landscaper v2 bridge logic * fix bug * fix missing scheme * add landscaper architecture config to helm chart * improve debuggability * fix docs and apiserver key * fix rbac
1 parent 1098c56 commit 847b6e7

File tree

15 files changed

+288
-73
lines changed

15 files changed

+288
-73
lines changed

api/constants/conditions.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ const (
99
ConditionAccessRequestGranted = "AccessRequestGranted"
1010
ConditionAccessRequestDeleted = "AccessRequestDeleted"
1111
ConditionClusterRequestDeleted = "ClusterRequestDeleted"
12+
13+
ConditionLandscaperV2ResourceCreatedOrUpdated = "LandscaperV2ResourceCreatedOrUpdated"
14+
ConditionLandscaperV2ResourceDeleted = "LandscaperV2ResourceDeleted"
1215
)

charts/mcp-operator/templates/configmap-mcp-operator-config.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ data:
1414
{{- .Values.architecture.immutability | toYaml | nindent 8 }}
1515
{{- end }}
1616
{{- if and .Values.apiserver .Values.apiserver.architecture }}
17-
apiserver:
17+
apiServer:
1818
version: {{ .Values.apiserver.architecture.version | default "v1" }}
1919
allowOverride: {{ .Values.apiserver.architecture.allowOverride | default false }}
2020
{{- end }}
21+
landscaper:
22+
version: {{ .Values.landscaper.architecture.version | default "v1" }}
23+
allowOverride: {{ .Values.landscaper.architecture.allowOverride | default false }}
24+
{{- end }}

charts/mcp-operator/templates/rbac.yaml

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,43 @@ roleRef:
116116
name: {{ include "mcp-operator.landscaper.clusterrole" . }}
117117
apiGroup: rbac.authorization.k8s.io
118118
---
119+
apiVersion: {{ include "rbacversion" . }}
120+
kind: ClusterRole
121+
metadata:
122+
name: {{ include "mcp-operator.v2bridge.clusterrole" . }}
123+
labels:
124+
{{- include "mcp-operator.labels" . | nindent 4 }}
125+
rules:
126+
- apiGroups:
127+
- clusters.openmcp.cloud
128+
resources:
129+
- "*"
130+
verbs:
131+
- "*"
132+
- apiGroups:
133+
- ""
134+
resources:
135+
- secrets
136+
verbs:
137+
- get
138+
- list
139+
- watch
140+
---
141+
kind: ClusterRoleBinding
142+
apiVersion: {{ include "rbacversion" . }}
143+
metadata:
144+
name: {{ include "mcp-operator.v2bridge.clusterrole" . }}
145+
labels:
146+
{{- include "mcp-operator.labels" . | nindent 4 }}
147+
subjects:
148+
- kind: ServiceAccount
149+
name: mcp-operator
150+
namespace: {{ .Release.Namespace }}
151+
roleRef:
152+
kind: ClusterRole
153+
name: {{ include "mcp-operator.v2bridge.clusterrole" . }}
154+
apiGroup: rbac.authorization.k8s.io
155+
---
119156
{{- end }}
120157
{{- end }}
121158
{{- if has "cloudorchestrator" ( include "mcp-operator.activeControllers" .Values | fromYamlArray ) }}
@@ -181,32 +218,3 @@ subjects:
181218
namespace: {{ .Release.Namespace }}
182219
---
183220
{{- end }}
184-
apiVersion: {{ include "rbacversion" . }}
185-
kind: ClusterRole
186-
metadata:
187-
name: {{ include "mcp-operator.v2bridge.clusterrole" . }}
188-
labels:
189-
{{- include "mcp-operator.labels" . | nindent 4 }}
190-
rules:
191-
- apiGroups:
192-
- clusters.openmcp.cloud
193-
resources:
194-
- "*"
195-
verbs:
196-
- "*"
197-
---
198-
kind: ClusterRoleBinding
199-
apiVersion: {{ include "rbacversion" . }}
200-
metadata:
201-
name: {{ include "mcp-operator.v2bridge.clusterrole" . }}
202-
labels:
203-
{{- include "mcp-operator.labels" . | nindent 4 }}
204-
subjects:
205-
- kind: ServiceAccount
206-
name: mcp-operator
207-
namespace: {{ .Release.Namespace }}
208-
roleRef:
209-
kind: ClusterRole
210-
name: {{ include "mcp-operator.v2bridge.clusterrole" . }}
211-
apiGroup: rbac.authorization.k8s.io
212-
---

charts/mcp-operator/values.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ apiserver:
109109

110110
landscaper:
111111
disabled: false
112+
# architecture:
113+
# version: v1
114+
# allowOverride: false
112115
clusters:
113116
# core:
114117
# # specify either kubeconfig or host, audience, and one of caData or caConfigMapName.

cmd/mcp-operator/app/app.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"sigs.k8s.io/controller-runtime/pkg/cluster"
2323

2424
v2install "github.com/openmcp-project/openmcp-operator/api/install"
25+
lsv2install "github.com/openmcp-project/service-provider-landscaper/api/install"
2526

2627
laasinstall "github.com/gardener/landscaper-service/pkg/apis/core/install"
2728
cocorev1beta1 "github.com/openmcp-project/control-plane-operator/api/v1beta1"
@@ -290,6 +291,7 @@ func (o *Options) run(ctx context.Context) error {
290291
sc := runtime.NewScheme()
291292
openmcpinstall.Install(sc)
292293
utilruntime.Must(clientgoscheme.AddToScheme(sc))
294+
lsv2install.InstallProviderAPIs(sc)
293295
mgr, err := ctrl.NewManager(o.CrateClusterConfig, ctrl.Options{
294296
Scheme: sc,
295297
Metrics: server.Options{

docs/architecture-v2/bridge.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ In order to migrate an existing MCP landscape to the new v2 architecture step by
44

55
The bridge is currently implemented for the following components:
66
- `APIServer`
7+
- `Landscaper`
78

89
## Architecture Configuration
910

@@ -12,7 +13,10 @@ To configure for which components the bridge is enabled, set the architecture co
1213
immutability:
1314
policyName: mcp-architecture-immutability
1415
disabled: false
15-
apiServer:
16+
apiserver:
17+
version: v1
18+
allowOverride: false
19+
landscaper:
1620
version: v1
1721
allowOverride: false
1822
# more components are to follow

go.mod

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ require (
1717
github.com/openmcp-project/control-plane-operator v0.1.10
1818
github.com/openmcp-project/controller-utils v0.13.1
1919
github.com/openmcp-project/mcp-operator/api v0.33.1
20-
github.com/openmcp-project/openmcp-operator/api v0.7.0
21-
github.com/openmcp-project/openmcp-operator/lib v0.8.3
20+
github.com/openmcp-project/openmcp-operator/api v0.9.0
21+
github.com/openmcp-project/openmcp-operator/lib v0.9.0
22+
github.com/openmcp-project/service-provider-landscaper v0.4.0
2223
github.com/spf13/cobra v1.9.1
2324
github.com/spf13/pflag v1.0.7
2425
github.com/stretchr/testify v1.10.0
@@ -85,7 +86,7 @@ require (
8586
go.uber.org/zap v1.27.0 // indirect
8687
go.yaml.in/yaml/v2 v2.4.2 // indirect
8788
go.yaml.in/yaml/v3 v3.0.3 // indirect
88-
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect
89+
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
8990
golang.org/x/mod v0.26.0 // indirect
9091
golang.org/x/net v0.42.0 // indirect
9192
golang.org/x/oauth2 v0.29.0 // indirect

go.sum

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,12 @@ github.com/openmcp-project/control-plane-operator v0.1.10 h1:5ticEP3llBmIHQkBzkZ
115115
github.com/openmcp-project/control-plane-operator v0.1.10/go.mod h1:GNu9LBTPWoE3dKsBo2kS+SeKSLU2qLtu3VjpaznsB2o=
116116
github.com/openmcp-project/controller-utils v0.13.1 h1:+06c0bs1BIO+hBsTcuiEK5y8vpDFoZPml59WNm8fagM=
117117
github.com/openmcp-project/controller-utils v0.13.1/go.mod h1:Z1ytVshYcgJq3VQVGqkuZsjO/BCr4UYAaVpHl6JSIMI=
118-
github.com/openmcp-project/openmcp-operator/api v0.7.0 h1:DvaMS3xtAvahGOQm9sI26aotupa8XkwZP52HfOhZ9K0=
119-
github.com/openmcp-project/openmcp-operator/api v0.7.0/go.mod h1:TuAq8Fbrzuykxw/h589M8+QfHotwero5MPWVzdFAqkw=
120-
github.com/openmcp-project/openmcp-operator/lib v0.8.3 h1:2bb1zbP6Si7/fUfXT9M5B0xQnd7O4zGJXaUoe9pDmcA=
121-
github.com/openmcp-project/openmcp-operator/lib v0.8.3/go.mod h1:oydIXRZoNDxtI4DI/JBUB08UPzvfdaKLqHvC4S4HXHQ=
118+
github.com/openmcp-project/openmcp-operator/api v0.9.0 h1:Ss2XTHci3QDwtVQ9QCpAez68WPJNHpabpwMTpFYn0NE=
119+
github.com/openmcp-project/openmcp-operator/api v0.9.0/go.mod h1:cqSmw3+8QJ2Va15aeCmISKIqcWnTpPSb8kch6eyfa6U=
120+
github.com/openmcp-project/openmcp-operator/lib v0.9.0 h1:jbuuZ2HlR0T1wO+Ty8AEaswNeoUK7eGUIw+8zEaicJo=
121+
github.com/openmcp-project/openmcp-operator/lib v0.9.0/go.mod h1:ILD0kCivVMZM5G38GJXflN1AXuQZX4wfH1FgNAcrXo0=
122+
github.com/openmcp-project/service-provider-landscaper v0.4.0 h1:H55q5whlcb1fyhY7/dkeigcp9UPXZQj+WDI57XVY4vY=
123+
github.com/openmcp-project/service-provider-landscaper v0.4.0/go.mod h1:5VU8eJ5nA2Kz0Kc4qbmw6ocMNCgLKzDu5mX6w5hOHa8=
122124
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
123125
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
124126
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -181,8 +183,8 @@ go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
181183
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
182184
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
183185
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
184-
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc=
185-
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
186+
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
187+
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
186188
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
187189
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
188190
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=

internal/controller/core/apiserver/controller_test.go

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

2828
"github.com/openmcp-project/controller-utils/pkg/testing"
2929
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
30+
commonapi "github.com/openmcp-project/openmcp-operator/api/common"
3031
openmcpclusterutils "github.com/openmcp-project/openmcp-operator/lib/utils"
3132

3233
gardenv1beta1 "github.com/openmcp-project/mcp-operator/api/external/gardener/pkg/apis/core/v1beta1"
@@ -383,10 +384,8 @@ var _ = Describe("CO-1153 APIServer Controller", func() {
383384
Expect(env.Client(testutils.LaaSCoreCluster).Create(env.Ctx, cluster)).To(Succeed())
384385

385386
cr.Status.Phase = clustersv1alpha1.REQUEST_GRANTED
386-
cr.Status.Cluster = &clustersv1alpha1.NamespacedObjectReference{
387-
ObjectReference: clustersv1alpha1.ObjectReference{
388-
Name: cluster.Name,
389-
},
387+
cr.Status.Cluster = &commonapi.ObjectReference{
388+
Name: cluster.Name,
390389
Namespace: cluster.Namespace,
391390
}
392391
Expect(env.Client(testutils.LaaSCoreCluster).Status().Update(env.Ctx, cr)).To(Succeed())
@@ -426,11 +425,11 @@ var _ = Describe("CO-1153 APIServer Controller", func() {
426425
dummyShootJson, err := json.Marshal(dummyShoot)
427426
Expect(err).NotTo(HaveOccurred())
428427
cluster.Status = clustersv1alpha1.ClusterStatus{
429-
Phase: clustersv1alpha1.CLUSTER_PHASE_READY,
430428
ProviderStatus: &runtime.RawExtension{
431429
Raw: dummyShootJson,
432430
},
433431
}
432+
cluster.Status.Phase = clustersv1alpha1.CLUSTER_PHASE_READY
434433
Expect(env.Client(testutils.LaaSCoreCluster).Status().Update(env.Ctx, cluster)).To(Succeed())
435434

436435
// reconcile again, should now get further
@@ -472,10 +471,8 @@ var _ = Describe("CO-1153 APIServer Controller", func() {
472471
Expect(env.Client(testutils.LaaSCoreCluster).Create(env.Ctx, access)).To(Succeed())
473472

474473
ar.Status.Phase = clustersv1alpha1.REQUEST_GRANTED
475-
ar.Status.SecretRef = &clustersv1alpha1.NamespacedObjectReference{
476-
ObjectReference: clustersv1alpha1.ObjectReference{
477-
Name: access.Name,
478-
},
474+
ar.Status.SecretRef = &commonapi.ObjectReference{
475+
Name: access.Name,
479476
Namespace: access.Namespace,
480477
}
481478
Expect(env.Client(testutils.LaaSCoreCluster).Status().Update(env.Ctx, ar)).To(Succeed())

internal/controller/core/apiserver/v2.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import (
44
"context"
55
"fmt"
66
"strconv"
7+
"strings"
78
"time"
89

10+
"github.com/openmcp-project/controller-utils/pkg/collections"
911
"github.com/openmcp-project/controller-utils/pkg/logging"
1012
corev1 "k8s.io/api/core/v1"
1113
rbacv1 "k8s.io/api/rbac/v1"
@@ -22,6 +24,7 @@ import (
2224
gcpv1alpha1 "github.com/openmcp-project/cluster-provider-gardener/api/core/v1alpha1"
2325
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
2426
clustersconst "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1/constants"
27+
commonapi "github.com/openmcp-project/openmcp-operator/api/common"
2528
openmcpclusterutils "github.com/openmcp-project/openmcp-operator/lib/utils"
2629

2730
gardenv1beta1 "github.com/openmcp-project/mcp-operator/api/external/gardener/pkg/apis/core/v1beta1"
@@ -107,7 +110,9 @@ func v2HandleCreateOrUpdate(ctx context.Context, as *openmcpv1alpha1.APIServer,
107110
clusterReadyCon.Status = openmcpv1alpha1.ComponentConditionStatusFromBool(cluster.Status.Phase == clustersv1alpha1.CLUSTER_PHASE_READY)
108111
if clusterReadyCon.Status != openmcpv1alpha1.ComponentConditionStatusTrue {
109112
clusterReadyCon.Reason = cconst.ReasonClusterNotReady
110-
clusterReadyCon.Message = cluster.Status.Message
113+
clusterReadyCon.Message = strings.Join(collections.ProjectSlice(cluster.Status.Conditions, func(con metav1.Condition) string {
114+
return fmt.Sprintf("[%s] %s", con.Reason, con.Message)
115+
}), "\n")
111116
if clusterReadyCon.Message == "" {
112117
clusterReadyCon.Message = "Cluster is not ready yet, no further information available"
113118
}
@@ -160,11 +165,10 @@ func v2HandleCreateOrUpdate(ctx context.Context, as *openmcpv1alpha1.APIServer,
160165
} else {
161166
clusterRequestGrantedCon.Status = openmcpv1alpha1.ComponentConditionStatusFalse
162167
clusterRequestGrantedCon.Reason = cconst.ReasonClusterRequestNotGranted
163-
crReason := cr.Status.Reason
164-
crMessage := cr.Status.Message
165-
if crReason == "" {
166-
crReason = "<NoReason>"
167-
}
168+
crReason := cconst.ReasonClusterRequestNotGranted
169+
crMessage := strings.Join(collections.ProjectSlice(cr.Status.Conditions, func(con metav1.Condition) string {
170+
return fmt.Sprintf("[%s] %s", con.Reason, con.Message)
171+
}), "\n")
168172
if crMessage == "" {
169173
crMessage = "<NoMessage>"
170174
}
@@ -228,11 +232,10 @@ func v2HandleCreateOrUpdate(ctx context.Context, as *openmcpv1alpha1.APIServer,
228232
if ar.Status.Phase != clustersv1alpha1.REQUEST_GRANTED && ar.Status.SecretRef == nil {
229233
accessRequestGrantedCon.Status = openmcpv1alpha1.ComponentConditionStatusFalse
230234
accessRequestGrantedCon.Reason = cconst.ReasonAccessRequestNotGranted
231-
arReason := cr.Status.Reason
232-
arMessage := cr.Status.Message
233-
if arReason == "" {
234-
arReason = "<NoReason>"
235-
}
235+
arReason := cconst.ReasonAccessRequestNotGranted
236+
arMessage := strings.Join(collections.ProjectSlice(ar.Status.Conditions, func(con metav1.Condition) string {
237+
return fmt.Sprintf("[%s] %s", con.Reason, con.Message)
238+
}), "\n")
236239
if arMessage == "" {
237240
arMessage = "<NoMessage>"
238241
}
@@ -472,11 +475,11 @@ func (m *AccessRequestMutator) MetadataMutator() resources.MetadataMutator {
472475
// Mutate implements resources.Mutator.
473476
func (m *AccessRequestMutator) Mutate(r *clustersv1alpha1.AccessRequest) error {
474477
if m.isClusterRef && r.Spec.ClusterRef == nil {
475-
r.Spec.ClusterRef = &clustersv1alpha1.NamespacedObjectReference{}
478+
r.Spec.ClusterRef = &commonapi.ObjectReference{}
476479
r.Spec.ClusterRef.Name = m.refName
477480
r.Spec.ClusterRef.Namespace = m.refNamespace
478481
} else if !m.isClusterRef && r.Spec.RequestRef == nil {
479-
r.Spec.RequestRef = &clustersv1alpha1.NamespacedObjectReference{}
482+
r.Spec.RequestRef = &commonapi.ObjectReference{}
480483
r.Spec.RequestRef.Name = m.refName
481484
r.Spec.RequestRef.Namespace = m.refNamespace
482485
}

0 commit comments

Comments
 (0)