Skip to content

Commit 096fce8

Browse files
authored
feat: read own deployment configuration resource to get image pull secrets (#152)
1 parent 2ed17a0 commit 096fce8

36 files changed

+628
-113
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.9.0-dev
1+
v0.9.0

api/crds/manifests/landscaper.services.openmcp.cloud_providerconfigs.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ spec:
5959
image:
6060
minLength: 1
6161
type: string
62+
imagePullSecrets:
63+
items:
64+
description: LocalObjectReference is a reference to an object
65+
in the same namespace as the resource referencing it.
66+
properties:
67+
name:
68+
default: ""
69+
description: |-
70+
Name of the referent.
71+
This field is effectively required, but due to backwards compatibility is
72+
allowed to be empty. Instances of this type with an empty value here are
73+
almost certainly wrong.
74+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
75+
type: string
76+
type: object
77+
x-kubernetes-map-type: atomic
78+
type: array
6279
required:
6380
- image
6481
type: object
@@ -69,6 +86,23 @@ spec:
6986
image:
7087
minLength: 1
7188
type: string
89+
imagePullSecrets:
90+
items:
91+
description: LocalObjectReference is a reference to an object
92+
in the same namespace as the resource referencing it.
93+
properties:
94+
name:
95+
default: ""
96+
description: |-
97+
Name of the referent.
98+
This field is effectively required, but due to backwards compatibility is
99+
allowed to be empty. Instances of this type with an empty value here are
100+
almost certainly wrong.
101+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
102+
type: string
103+
type: object
104+
x-kubernetes-map-type: atomic
105+
type: array
72106
required:
73107
- image
74108
type: object
@@ -79,6 +113,23 @@ spec:
79113
image:
80114
minLength: 1
81115
type: string
116+
imagePullSecrets:
117+
items:
118+
description: LocalObjectReference is a reference to an object
119+
in the same namespace as the resource referencing it.
120+
properties:
121+
name:
122+
default: ""
123+
description: |-
124+
Name of the referent.
125+
This field is effectively required, but due to backwards compatibility is
126+
allowed to be empty. Instances of this type with an empty value here are
127+
almost certainly wrong.
128+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
129+
type: string
130+
type: object
131+
x-kubernetes-map-type: atomic
132+
type: array
82133
required:
83134
- image
84135
type: object
@@ -89,6 +140,23 @@ spec:
89140
image:
90141
minLength: 1
91142
type: string
143+
imagePullSecrets:
144+
items:
145+
description: LocalObjectReference is a reference to an object
146+
in the same namespace as the resource referencing it.
147+
properties:
148+
name:
149+
default: ""
150+
description: |-
151+
Name of the referent.
152+
This field is effectively required, but due to backwards compatibility is
153+
allowed to be empty. Instances of this type with an empty value here are
154+
almost certainly wrong.
155+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
156+
type: string
157+
type: object
158+
x-kubernetes-map-type: atomic
159+
type: array
92160
required:
93161
- image
94162
type: object

api/v1alpha2/providerconfiguration_types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1alpha2
1818

1919
import (
20+
"github.com/openmcp-project/openmcp-operator/api/common"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122
)
2223

@@ -65,7 +66,8 @@ type Deployment struct {
6566
type ImageConfiguration struct {
6667
// +kubebuilder:validation:Required
6768
// +kubebuilder:validation:MinLength=1
68-
Image string `json:"image"`
69+
Image string `json:"image"`
70+
ImagePullSecrets []common.LocalObjectReference `json:"imagePullSecrets,omitempty"`
6971
}
7072

7173
// +kubebuilder:object:root=true

api/v1alpha2/zz_generated.deepcopy.go

Lines changed: 10 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/service-provider-landscaper/app/run.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
rbacv1 "k8s.io/api/rbac/v1"
1616

1717
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
18+
deploymentv1alpha1 "github.com/openmcp-project/openmcp-operator/api/provider/v1alpha1"
1819
"github.com/spf13/cobra"
1920
"k8s.io/apimachinery/pkg/runtime"
2021
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -89,6 +90,7 @@ func (o *RunOptions) Run(ctx context.Context) error {
8990
platformScheme := runtime.NewScheme()
9091
utilruntime.Must(clientgoscheme.AddToScheme(platformScheme))
9192
utilruntime.Must(clustersv1alpha1.AddToScheme(platformScheme))
93+
utilruntime.Must(deploymentv1alpha1.AddToScheme(platformScheme))
9294
providerscheme.InstallProviderAPIs(platformScheme)
9395

9496
onboardingScheme := runtime.NewScheme()
@@ -146,6 +148,8 @@ func (o *RunOptions) Run(ctx context.Context) error {
146148
OnboardingCluster: onboardingCluster,
147149
PlatformCluster: o.Clusters.Platform,
148150
Scheme: mgr.GetScheme(),
151+
ProviderName: o.ProviderName,
152+
ProviderNamespace: providerSystemNamespace,
149153
}).SetupWithManager(mgr); err != nil {
150154
return fmt.Errorf("unable to create controller: %w", err)
151155
}

internal/controller/landscaper_controller.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ type LandscaperReconciler struct {
4545
ClusterAccessReconciler clusteraccess.Reconciler
4646
Scheme *runtime.Scheme
4747
DNSReconciler *dns.Reconciler
48+
ProviderName string
49+
ProviderNamespace string
4850

4951
InstanceClusterAccess InstanceClusterAccess
5052
}

internal/controller/landscaper_controller_test.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
testutils "github.com/openmcp-project/controller-utils/pkg/testing"
1717
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
18+
deploymentv1alpha1 "github.com/openmcp-project/openmcp-operator/api/provider/v1alpha1"
1819
"github.com/openmcp-project/openmcp-operator/lib/clusteraccess"
1920
rbacv1 "k8s.io/api/rbac/v1"
2021
"k8s.io/apimachinery/pkg/runtime"
@@ -61,6 +62,23 @@ func setDeploymentReady(ctx context.Context, deployment *appsv1.Deployment, c cl
6162
Expect(c.Status().Update(ctx, deployment)).To(Succeed())
6263
}
6364

65+
// expectImagePullSecretsValid verifies that a deployment has the expected number of image pull secrets
66+
// and that each secret exists in the given namespace with the correct type and data
67+
func expectImagePullSecretsValid(ctx context.Context, c client.Client, deployment *appsv1.Deployment, expectedCount int, namespace string) {
68+
Expect(deployment.Spec.Template.Spec.ImagePullSecrets).To(HaveLen(expectedCount))
69+
for _, s := range deployment.Spec.Template.Spec.ImagePullSecrets {
70+
secret := &corev1.Secret{
71+
ObjectMeta: metav1.ObjectMeta{
72+
Name: s.Name,
73+
Namespace: namespace,
74+
},
75+
}
76+
Expect(c.Get(ctx, client.ObjectKeyFromObject(secret), secret)).To(Succeed())
77+
Expect(secret.Type).To(Equal(corev1.SecretTypeDockerConfigJson))
78+
Expect(secret.Data).To(HaveKey(".dockerconfigjson"))
79+
}
80+
}
81+
6482
type testInstanceClusterAccess struct {
6583
mcpCluster *clusters.Cluster
6684
workloadCluster *clusters.Cluster
@@ -78,6 +96,7 @@ func buildTestEnvironmentReconcile(testdataDir string, objectsWithStatus ...clie
7896
scheme := runtime.NewScheme()
7997
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
8098
utilruntime.Must(clustersv1alpha1.AddToScheme(scheme))
99+
utilruntime.Must(deploymentv1alpha1.AddToScheme(scheme))
81100
utilruntime.Must(v1alpha2.AddToScheme(scheme))
82101
utilruntime.Must(gatewayv1.Install(scheme))
83102
utilruntime.Must(gatewayv1alpha2.Install(scheme))
@@ -118,6 +137,8 @@ func buildTestEnvironmentReconcile(testdataDir string, objectsWithStatus ...clie
118137
mcpCluster: clusters.NewTestClusterFromClient("mcp", c),
119138
workloadCluster: clusters.NewTestClusterFromClient("workload", c),
120139
},
140+
ProviderName: "landscaper",
141+
ProviderNamespace: "openmcp-system",
121142
}
122143

123144
return r
@@ -372,13 +393,20 @@ var _ = Describe("Landscaper Controller", func() {
372393

373394
Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(installationNs), installationNs)).To(Succeed())
374395

375-
// set deployments to ready
376396
Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(lsControllerDeployment), lsControllerDeployment)).To(Succeed())
377397
Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(lsMainDeployment), lsMainDeployment)).To(Succeed())
378398
Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(lsWebhooksServerDeployment), lsWebhooksServerDeployment)).To(Succeed())
379399
Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(manifestDeployerDeployment), manifestDeployerDeployment)).To(Succeed())
380400
Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(helmDeployerDeployment), helmDeployerDeployment)).To(Succeed())
381401

402+
// expect the image pull secrets to be created in the installation namespace
403+
expectImagePullSecretsValid(env.Ctx, env.Client(), lsControllerDeployment, 2, installationNamespace)
404+
expectImagePullSecretsValid(env.Ctx, env.Client(), lsMainDeployment, 2, installationNamespace)
405+
expectImagePullSecretsValid(env.Ctx, env.Client(), lsWebhooksServerDeployment, 2, installationNamespace)
406+
expectImagePullSecretsValid(env.Ctx, env.Client(), manifestDeployerDeployment, 2, installationNamespace)
407+
expectImagePullSecretsValid(env.Ctx, env.Client(), helmDeployerDeployment, 1, installationNamespace)
408+
409+
// set deployments to ready
382410
setDeploymentReady(env.Ctx, lsControllerDeployment, env.Client())
383411
setDeploymentReady(env.Ctx, lsMainDeployment, env.Client())
384412
setDeploymentReady(env.Ctx, lsWebhooksServerDeployment, env.Client())

internal/controller/reconcile.go

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
"time"
1010

1111
"github.com/openmcp-project/controller-utils/pkg/clusters"
12+
"github.com/openmcp-project/openmcp-operator/api/common"
13+
"github.com/openmcp-project/openmcp-operator/api/provider/v1alpha1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1215

1316
"github.com/openmcp-project/service-provider-landscaper/internal/dns"
1417

@@ -143,7 +146,7 @@ func (r *LandscaperReconciler) handleCreateUpdateOperation(ctx context.Context,
143146
return reconcile.Result{RequeueAfter: dnsResult.RequeueAfter}, status, nil
144147
}
145148

146-
conf, err := r.createConfig(ls, mcpCluster, workloadCluster, providerConfig, dnsResult.HostName)
149+
conf, err := r.createConfig(ctx, ls, mcpCluster, workloadCluster, providerConfig, dnsResult.HostName)
147150
if err != nil {
148151
log.Error(err, "failed to create configuration for landscaper instance")
149152
status.setInstallConfigurationError(err)
@@ -232,7 +235,7 @@ func (r *LandscaperReconciler) handleDeleteOperation(ctx context.Context, ls *v1
232235
return reconcile.Result{}, status, err
233236
}
234237

235-
conf, err := r.createConfig(ls, mcpCluster, workloadCluster, providerConfig, "")
238+
conf, err := r.createConfig(ctx, ls, mcpCluster, workloadCluster, providerConfig, "")
236239
if err != nil {
237240
log.Error(err, "failed to create configuration to uninstall landscaper instance")
238241
status.setUninstallConfigurationError(err)
@@ -380,7 +383,20 @@ func (r *LandscaperReconciler) getProviderConfigForLandscaper(ctx context.Contex
380383
return providerConfig, nil
381384
}
382385

383-
func (r *LandscaperReconciler) createConfig(ls *v1alpha2.Landscaper, mcpCluster, workloadCluster *clusters.Cluster, providerConfig *v1alpha2.ProviderConfig, workloadClusterDomain string) (*instance.Configuration, error) {
386+
func (r *LandscaperReconciler) getServiceProviderResource(ctx context.Context) (*v1alpha1.ServiceProvider, error) {
387+
serviceProvider := &v1alpha1.ServiceProvider{
388+
ObjectMeta: metav1.ObjectMeta{
389+
Name: r.ProviderName,
390+
},
391+
}
392+
if err := r.PlatformCluster.Client().Get(ctx, client.ObjectKeyFromObject(serviceProvider), serviceProvider); err != nil {
393+
return nil, fmt.Errorf("failed to get service provider resource %s/%s: %w", r.ProviderNamespace, r.ProviderName, err)
394+
}
395+
396+
return serviceProvider, nil
397+
}
398+
399+
func (r *LandscaperReconciler) createConfig(ctx context.Context, ls *v1alpha2.Landscaper, mcpCluster, workloadCluster *clusters.Cluster, providerConfig *v1alpha2.ProviderConfig, workloadClusterDomain string) (*instance.Configuration, error) {
384400
inst := identity.Instance(identity.GetInstanceID(ls))
385401

386402
cpu, err := resource.ParseQuantity("10m")
@@ -397,23 +413,41 @@ func (r *LandscaperReconciler) createConfig(ls *v1alpha2.Landscaper, mcpCluster,
397413
core.ResourceMemory: memory,
398414
},
399415
}
416+
417+
serviceProvider, err := r.getServiceProviderResource(ctx)
418+
if err != nil {
419+
return nil, err
420+
}
421+
422+
getImagePullSecrets := func(imageConfiguration *v1alpha2.ImageConfiguration) []common.LocalObjectReference {
423+
if imageConfiguration == nil {
424+
return serviceProvider.Spec.ImagePullSecrets
425+
}
426+
427+
return imageConfiguration.ImagePullSecrets
428+
}
429+
400430
conf := &instance.Configuration{
401-
Instance: inst,
402-
Version: ls.Spec.Version,
403-
MCPCluster: mcpCluster,
404-
WorkloadCluster: workloadCluster,
405-
WorkloadClusterDomain: fmt.Sprintf("https://%s:%d", workloadClusterDomain, dnsServicePort()), // 9443 is the port for TLS passthrough configured in the Gateway
431+
Instance: inst,
432+
Version: ls.Spec.Version,
433+
PlatformCluster: r.PlatformCluster,
434+
PlatformClusterNamespace: r.ProviderNamespace,
435+
MCPCluster: mcpCluster,
436+
WorkloadCluster: workloadCluster,
437+
WorkloadClusterDomain: fmt.Sprintf("https://%s:%d", workloadClusterDomain, dnsServicePort()), // 9443 is the port for TLS passthrough configured in the Gateway
406438
Landscaper: instance.LandscaperConfig{
407439
Controller: instance.ControllerConfig{
408440
Image: v1alpha2.ImageConfiguration{
409-
Image: providerConfig.GetLandscaperControllerImageLocation(ls.Spec.Version),
441+
Image: providerConfig.GetLandscaperControllerImageLocation(ls.Spec.Version),
442+
ImagePullSecrets: getImagePullSecrets(providerConfig.Spec.Deployment.LandscaperController),
410443
},
411444
Resources: resources,
412445
ResourcesMain: resources,
413446
},
414447
WebhooksServer: instance.WebhooksServerConfig{
415448
Image: v1alpha2.ImageConfiguration{
416-
Image: providerConfig.GetLandscaperWebhooksServerImageLocation(ls.Spec.Version),
449+
Image: providerConfig.GetLandscaperWebhooksServerImageLocation(ls.Spec.Version),
450+
ImagePullSecrets: getImagePullSecrets(providerConfig.Spec.Deployment.LandscaperWebhooksServer),
417451
},
418452
Resources: resources,
419453
ServicePort: dnsServicePort(),
@@ -422,13 +456,15 @@ func (r *LandscaperReconciler) createConfig(ls *v1alpha2.Landscaper, mcpCluster,
422456
},
423457
ManifestDeployer: instance.ManifestDeployerConfig{
424458
Image: v1alpha2.ImageConfiguration{
425-
Image: providerConfig.GetManifestDeployerImageLocation(ls.Spec.Version),
459+
Image: providerConfig.GetManifestDeployerImageLocation(ls.Spec.Version),
460+
ImagePullSecrets: getImagePullSecrets(providerConfig.Spec.Deployment.ManifestDeployer),
426461
},
427462
Resources: resources,
428463
},
429464
HelmDeployer: instance.HelmDeployerConfig{
430465
Image: v1alpha2.ImageConfiguration{
431-
Image: providerConfig.GetHelmDeployerImageLocation(ls.Spec.Version),
466+
Image: providerConfig.GetHelmDeployerImageLocation(ls.Spec.Version),
467+
ImagePullSecrets: getImagePullSecrets(providerConfig.Spec.Deployment.HelmDeployer),
432468
},
433469
Resources: resources,
434470
},

internal/controller/testdata/test-01/providerconfig.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ spec:
1313

1414
helmDeployer:
1515
image: other.registry.test/landscaper/helm-deployer/images/helm-deployer-controller
16+
imagePullSecrets:
17+
- name: helm-deployer-secret
1618

1719
workloadClusterDomain: workload.cluster.local

0 commit comments

Comments
 (0)