Skip to content

Commit 33e0cca

Browse files
authored
chore: Improve mounting certs into users containers (#1980) (#1981)
* chore: Improve mounting certs into users containers Signed-off-by: Anatolii Bazko <abazko@redhat.com>
1 parent 51ff4df commit 33e0cca

File tree

12 files changed

+371
-157
lines changed

12 files changed

+371
-157
lines changed

controllers/usernamespace/usernamespace_controller.go

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -310,57 +310,16 @@ func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context,
310310
if err := deleteLegacyObject("trusted-ca-certs", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil {
311311
return err
312312
}
313-
targetConfigMapName := prefixedName("trusted-ca-certs")
314313

315-
delConfigMap := func() error {
316-
_, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetConfigMapName, Namespace: targetNs}, &corev1.ConfigMap{})
317-
return err
318-
}
319-
320-
sourceMap := &corev1.ConfigMap{}
321-
if err := r.client.Get(ctx, client.ObjectKey{Name: tls.CheMergedCABundleCertsCMName, Namespace: checluster.Namespace}, sourceMap); err != nil {
322-
if !errors.IsNotFound(err) {
323-
return err
324-
}
325-
326-
return delConfigMap()
327-
}
328-
329-
targetMap := &corev1.ConfigMap{
330-
TypeMeta: metav1.TypeMeta{
331-
Kind: "ConfigMap",
332-
APIVersion: "v1",
333-
},
334-
ObjectMeta: metav1.ObjectMeta{
335-
Name: targetConfigMapName,
336-
Namespace: targetNs,
337-
Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{
338-
dwconstants.DevWorkspaceMountLabel: "true",
339-
dwconstants.DevWorkspaceWatchConfigMapLabel: "true",
340-
}),
341-
Annotations: addToFirst(sourceMap.Annotations, map[string]string{
342-
dwconstants.DevWorkspaceMountAsAnnotation: "file",
343-
dwconstants.DevWorkspaceMountPathAnnotation: "/public-certs",
344-
}),
345-
},
346-
Data: sourceMap.Data,
347-
}
314+
// Remove trusted-ca-certs ConfigMap from the target namespace to reduce the number of ConfigMaps
315+
// and avoid mounting the same certificates under different paths.
316+
// See cerificates#syncCheCABundleCerts
317+
trustedCACertsCMKey := client.ObjectKey{Name: prefixedName("trusted-ca-certs"), Namespace: targetNs}
318+
_, err := deploy.Delete(deployContext, trustedCACertsCMKey, &corev1.ConfigMap{})
348319

349-
_, err := deploy.Sync(deployContext, targetMap, deploy.ConfigMapDiffOpts)
350320
return err
351321
}
352322

353-
func addToFirst(first map[string]string, second map[string]string) map[string]string {
354-
if first == nil {
355-
first = map[string]string{}
356-
}
357-
for k, v := range second {
358-
first[k] = v
359-
}
360-
361-
return first
362-
}
363-
364323
func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
365324
if err := deleteLegacyObject("proxy-settings", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil {
366325
return err

controllers/usernamespace/usernamespace_controller_test.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,6 @@ func TestCreatesDataInNamespace(t *testing.T) {
268268
assert.Equal(t, corev1.SecretTypeOpaque, cert.Type, "Unexpected secret type")
269269
assert.Equal(t, true, *cert.Immutable, "Unexpected mutability of the secret")
270270

271-
caCerts := corev1.ConfigMap{}
272-
assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-trusted-ca-certs", Namespace: namespace.GetName()}, &caCerts))
273-
assert.Equal(t, "file", caCerts.GetAnnotations()[dwconstants.DevWorkspaceMountAsAnnotation], "trusted certs should be annotated as mount as 'file'")
274-
assert.Equal(t, "/public-certs", caCerts.GetAnnotations()[dwconstants.DevWorkspaceMountPathAnnotation], "trusted certs annotated as mounted to an unexpected path")
275-
assert.Equal(t, "true", caCerts.GetLabels()[dwconstants.DevWorkspaceMountLabel], "trusted certs should be labeled as mounted")
276-
assert.Equal(t, 2, len(caCerts.Data), "Expecting exactly 2 data entries in the trusted cert config map")
277-
assert.Equal(t, "trusted cert 1", string(caCerts.Data["trusted1"]), "Unexpected trusted cert 1 value")
278-
assert.Equal(t, "trusted cert 2", string(caCerts.Data["trusted2"]), "Unexpected trusted cert 2 value")
279-
280271
gitTlsConfig := corev1.ConfigMap{}
281272
assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-git-tls-creds", Namespace: namespace.GetName()}, &gitTlsConfig))
282273
assert.Equal(t, "true", gitTlsConfig.Labels[dwconstants.DevWorkspaceGitTLSLabel])

pkg/common/constants/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ const (
9191
CheEclipseOrgScmServerEndpoint = "che.eclipse.org/scm-server-endpoint"
9292
CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest"
9393
CheEclipseOrgScmGitHubDisableSubdomainIsolation = "che.eclipse.org/scm-github-disable-subdomain-isolation"
94+
OpenShiftIOOwningComponent = "openshift.io/owning-component"
95+
ConfigOpenShiftIOInjectTrustedCaBundle = "config.openshift.io/inject-trusted-cabundle"
9496

9597
// DevEnvironments
9698
PerUserPVCStorageStrategy = "per-user"
@@ -128,6 +130,7 @@ const (
128130
WorkspacesConfig = "workspaces-config"
129131
InstallOrUpdateFailed = "InstallOrUpdateFailed"
130132
FinalizerSuffix = "finalizers.che.eclipse.org"
133+
PublicCertsDir = "/public-certs"
131134

132135
// DevWorkspace
133136
DevWorkspaceServiceAccountName = "devworkspace-controller-serviceaccount"

pkg/common/test/utils.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) 2019-2023 Red Hat, Inc.
2+
// Copyright (c) 2019-2025 Red Hat, Inc.
33
// This program and the accompanying materials are made
44
// available under the terms of the Eclipse Public License 2.0
55
// which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -18,6 +18,8 @@ import (
1818
"strings"
1919
"testing"
2020

21+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
22+
2123
securityv1 "github.com/openshift/api/security/v1"
2224

2325
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
@@ -85,26 +87,9 @@ func CompareResources(actualDeployment *appsv1.Deployment, expected TestExpected
8587
)
8688
}
8789

88-
func AssertEqualEnvVars(t *testing.T, expected []corev1.EnvVar, actual []corev1.EnvVar) {
89-
assert.Equal(t, len(expected), len(actual))
90-
for _, expectedEnvVar := range expected {
91-
found := true
92-
for _, actualEnvVar := range actual {
93-
if expectedEnvVar.Name == actualEnvVar.Name && expectedEnvVar.Value == actualEnvVar.Value {
94-
found = true
95-
break
96-
}
97-
}
98-
99-
if !found {
100-
t.Errorf("Expected env var %s=%s not found", expectedEnvVar.Name, expectedEnvVar.Value)
101-
}
102-
}
103-
}
104-
10590
func ValidateSecurityContext(actualDeployment *appsv1.Deployment, t *testing.T) {
10691
assert.Equal(t, corev1.Capability("ALL"), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.Capabilities.Drop[0])
107-
assert.Equal(t, pointer.BoolPtr(false), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)
92+
assert.Equal(t, pointer.Bool(false), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)
10893
}
10994

11095
func compareQuantity(resource string, actualQuantity *resource.Quantity, expected string, t *testing.T) {
@@ -164,7 +149,7 @@ func GetResourceQuantity(value string, defaultValue string) resource.Quantity {
164149
}
165150

166151
func EnableTestMode() {
167-
os.Setenv("MOCK_API", "1")
152+
_ = os.Setenv("MOCK_API", "1")
168153
}
169154

170155
func IsTestMode() bool {
@@ -216,3 +201,20 @@ func GetDeployContext(cheCluster *chev2.CheCluster, initObjs []runtime.Object) *
216201
CheHost: strings.TrimPrefix(cheCluster.Status.CheURL, "https://"),
217202
}
218203
}
204+
205+
// EnsureReconcile runs the testReconcileFunc until it returns done=true or 10 iterations
206+
func EnsureReconcile(
207+
t *testing.T,
208+
ctx *chetypes.DeployContext,
209+
testReconcileFunc func(ctx *chetypes.DeployContext) (result reconcile.Result, done bool, err error)) {
210+
211+
for i := 0; i < 10; i++ {
212+
_, done, err := testReconcileFunc(ctx)
213+
assert.NoError(t, err)
214+
if done {
215+
return
216+
}
217+
}
218+
219+
assert.Fail(t, "Reconcile did not finish in 10 iterations")
220+
}

pkg/common/utils/utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func GetPullPolicyFromDockerImage(dockerImage string) string {
153153
return "IfNotPresent"
154154
}
155155

156-
func GetMap(value map[string]string, defaultValue map[string]string) map[string]string {
156+
func GetMapOrDefault(value map[string]string, defaultValue map[string]string) map[string]string {
157157
ret := value
158158
if len(value) < 1 {
159159
ret = defaultValue

pkg/deploy/configmap.go

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) 2019-2023 Red Hat, Inc.
2+
// Copyright (c) 2019-2025 Red Hat, Inc.
33
// This program and the accompanying materials are made
44
// available under the terms of the Eclipse Public License 2.0
55
// which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -29,13 +29,35 @@ var ConfigMapDiffOpts = cmp.Options{
2929
}),
3030
}
3131

32+
// InitConfigMap returns a new ConfigMap Kubernetes object.
33+
func InitConfigMap(
34+
ctx *chetypes.DeployContext,
35+
name string,
36+
data map[string]string,
37+
component string) *corev1.ConfigMap {
38+
39+
return &corev1.ConfigMap{
40+
TypeMeta: metav1.TypeMeta{
41+
Kind: "ConfigMap",
42+
APIVersion: "v1",
43+
},
44+
ObjectMeta: metav1.ObjectMeta{
45+
Name: name,
46+
Namespace: ctx.CheCluster.Namespace,
47+
Labels: GetLabels(component),
48+
Annotations: map[string]string{},
49+
},
50+
Data: data,
51+
}
52+
}
53+
3254
func SyncConfigMapDataToCluster(
3355
deployContext *chetypes.DeployContext,
3456
name string,
3557
data map[string]string,
3658
component string) (bool, error) {
3759

38-
configMapSpec := GetConfigMapSpec(deployContext, name, data, component)
60+
configMapSpec := InitConfigMap(deployContext, name, data, component)
3961
return Sync(deployContext, configMapSpec, ConfigMapDiffOpts)
4062
}
4163

@@ -46,25 +68,33 @@ func SyncConfigMapSpecToCluster(
4668
return Sync(deployContext, configMapSpec, ConfigMapDiffOpts)
4769
}
4870

49-
func GetConfigMapSpec(
50-
deployContext *chetypes.DeployContext,
51-
name string,
52-
data map[string]string,
53-
component string) *corev1.ConfigMap {
71+
// SyncConfigMap synchronizes the ConfigMap with the cluster.
72+
func SyncConfigMap(
73+
ctx *chetypes.DeployContext,
74+
cm *corev1.ConfigMap,
75+
ensuredLabels []string,
76+
ensuredAnnotations []string) (bool, error) {
5477

55-
configMap := &corev1.ConfigMap{
56-
TypeMeta: metav1.TypeMeta{
57-
Kind: "ConfigMap",
58-
APIVersion: "v1",
59-
},
60-
ObjectMeta: metav1.ObjectMeta{
61-
Name: name,
62-
Namespace: deployContext.CheCluster.Namespace,
63-
Labels: GetLabels(component),
64-
Annotations: map[string]string{},
65-
},
66-
Data: data,
78+
diffs := cmp.Options{
79+
cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"),
80+
GetLabelsAndAnnotationsComparator(ensuredLabels, ensuredAnnotations),
81+
}
82+
83+
return Sync(ctx, cm, diffs)
84+
}
85+
86+
// SyncConfigMapIgnoreData synchronizes the ConfigMap with the cluster ignoring the data field.
87+
func SyncConfigMapIgnoreData(
88+
ctx *chetypes.DeployContext,
89+
cm *corev1.ConfigMap,
90+
ensuredLabels []string,
91+
ensuredAnnotations []string) (bool, error) {
92+
93+
diffs := cmp.Options{
94+
cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"),
95+
cmpopts.IgnoreFields(corev1.ConfigMap{}, "Data"),
96+
GetLabelsAndAnnotationsComparator(ensuredLabels, ensuredAnnotations),
6797
}
6898

69-
return configMap
99+
return Sync(ctx, cm, diffs)
70100
}

pkg/deploy/configmap_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,14 @@ func TestSyncConfigMapSpecDataToCluster(t *testing.T) {
9494
},
9595
}
9696

97-
spec := GetConfigMapSpec(deployContext, "test", map[string]string{"a": "b"}, "che")
97+
spec := InitConfigMap(deployContext, "test", map[string]string{"a": "b"}, "che")
9898
done, err := SyncConfigMapSpecToCluster(deployContext, spec)
9999
if !done || err != nil {
100100
t.Fatalf("Failed to sync config map: %v", err)
101101
}
102102

103103
// check if labels
104-
spec = GetConfigMapSpec(deployContext, "test", map[string]string{"a": "b"}, "che")
104+
spec = InitConfigMap(deployContext, "test", map[string]string{"a": "b"}, "che")
105105
spec.ObjectMeta.Labels = map[string]string{"l": "v"}
106106
_, err = SyncConfigMapSpecToCluster(deployContext, spec)
107107
if err != nil {

pkg/deploy/gateway/gateway.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func GetConfigmapForGatewayConfig(
276276
Namespace: deployContext.CheCluster.Namespace,
277277
Labels: labels.Merge(
278278
deploy.GetLabels(gatewayConfigComponentName),
279-
utils.GetMap(deployContext.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)),
279+
utils.GetMapOrDefault(deployContext.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)),
280280
},
281281
Data: map[string]string{
282282
componentName + ".yml": string(gatewayConfigContent),
@@ -499,7 +499,7 @@ func getGatewayDeploymentSpec(ctx *chetypes.DeployContext) (*appsv1.Deployment,
499499
}
500500

501501
func getContainersSpec(ctx *chetypes.DeployContext) []corev1.Container {
502-
configLabelsMap := utils.GetMap(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)
502+
configLabelsMap := utils.GetMapOrDefault(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)
503503
gatewayImage := defaults.GetGatewayImage(ctx.CheCluster)
504504
configSidecarImage := defaults.GetGatewayConfigSidecarImage(ctx.CheCluster)
505505
configLabels := labels.FormatLabels(configLabelsMap)

pkg/deploy/labels.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) 2019-2023 Red Hat, Inc.
2+
// Copyright (c) 2019-2025 Red Hat, Inc.
33
// This program and the accompanying materials are made
44
// available under the terms of the Eclipse Public License 2.0
55
// which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -27,6 +27,19 @@ func GetLabels(component string) map[string]string {
2727
}
2828
}
2929

30+
func GetDefaultKubernetesLabelsWith(labels ...string) []string {
31+
defaultK8sLabels := append(
32+
[]string{
33+
constants.KubernetesNameLabelKey,
34+
constants.KubernetesInstanceLabelKey,
35+
constants.KubernetesPartOfLabelKey,
36+
constants.KubernetesComponentLabelKey,
37+
constants.KubernetesManagedByLabelKey,
38+
}, labels...)
39+
40+
return defaultK8sLabels
41+
}
42+
3043
func GetManagedByLabel() string {
3144
return defaults.GetCheFlavor() + "-operator"
3245
}

pkg/deploy/sync.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,27 @@ func DeleteIgnoreIfNotFound(
202202
return err
203203
}
204204

205+
func GetLabelsAndAnnotationsComparator(
206+
ensuredLabels []string,
207+
ensuredAnnotations []string) cmp.Option {
208+
209+
return cmp.Comparer(func(x, y metav1.ObjectMeta) bool {
210+
for _, label := range ensuredLabels {
211+
if x.Labels[label] != y.Labels[label] {
212+
return false
213+
}
214+
}
215+
216+
for _, annotation := range ensuredAnnotations {
217+
if x.Annotations[annotation] != y.Annotations[annotation] {
218+
return false
219+
}
220+
}
221+
222+
return true
223+
})
224+
}
225+
205226
// doCreate creates object.
206227
// Return error if object cannot be created otherwise returns nil.
207228
func doCreate(

0 commit comments

Comments
 (0)