Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 5 additions & 46 deletions controllers/usernamespace/usernamespace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,57 +310,16 @@ func (r *CheUserNamespaceReconciler) reconcileTrustedCerts(ctx context.Context,
if err := deleteLegacyObject("trusted-ca-certs", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil {
return err
}
targetConfigMapName := prefixedName("trusted-ca-certs")

delConfigMap := func() error {
_, err := deploy.Delete(deployContext, client.ObjectKey{Name: targetConfigMapName, Namespace: targetNs}, &corev1.ConfigMap{})
return err
}

sourceMap := &corev1.ConfigMap{}
if err := r.client.Get(ctx, client.ObjectKey{Name: tls.CheMergedCABundleCertsCMName, Namespace: checluster.Namespace}, sourceMap); err != nil {
if !errors.IsNotFound(err) {
return err
}

return delConfigMap()
}

targetMap := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: targetConfigMapName,
Namespace: targetNs,
Labels: defaults.AddStandardLabelsForComponent(checluster, userSettingsComponentLabelValue, map[string]string{
dwconstants.DevWorkspaceMountLabel: "true",
dwconstants.DevWorkspaceWatchConfigMapLabel: "true",
}),
Annotations: addToFirst(sourceMap.Annotations, map[string]string{
dwconstants.DevWorkspaceMountAsAnnotation: "file",
dwconstants.DevWorkspaceMountPathAnnotation: "/public-certs",
}),
},
Data: sourceMap.Data,
}
// Remove trusted-ca-certs ConfigMap from the target namespace to reduce the number of ConfigMaps
// and avoid mounting the same certificates under different paths.
// See cerificates#syncCheCABundleCerts
trustedCACertsCMKey := client.ObjectKey{Name: prefixedName("trusted-ca-certs"), Namespace: targetNs}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on dogfooding, I see there is che-trusted-ca-certs, is it going to be removed by prefix?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, testing configuration of 3.19, am I correct that che-trusted-ca-certs and ca-certs-merged are pretty much identical?

Screenshot 2025-03-19 at 14 21 37

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. che-trusted-ca-certs will be removed completedly
  2. yes, those CMs are identical

_, err := deploy.Delete(deployContext, trustedCACertsCMKey, &corev1.ConfigMap{})

_, err := deploy.Sync(deployContext, targetMap, deploy.ConfigMapDiffOpts)
return err
}

func addToFirst(first map[string]string, second map[string]string) map[string]string {
if first == nil {
first = map[string]string{}
}
for k, v := range second {
first[k] = v
}

return first
}

func (r *CheUserNamespaceReconciler) reconcileProxySettings(ctx context.Context, targetNs string, checluster *chev2.CheCluster, deployContext *chetypes.DeployContext) error {
if err := deleteLegacyObject("proxy-settings", &corev1.ConfigMap{}, targetNs, checluster, deployContext); err != nil {
return err
Expand Down
9 changes: 0 additions & 9 deletions controllers/usernamespace/usernamespace_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,15 +268,6 @@ func TestCreatesDataInNamespace(t *testing.T) {
assert.Equal(t, corev1.SecretTypeOpaque, cert.Type, "Unexpected secret type")
assert.Equal(t, true, *cert.Immutable, "Unexpected mutability of the secret")

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

gitTlsConfig := corev1.ConfigMap{}
assert.NoError(t, cl.Get(ctx, client.ObjectKey{Name: "che-git-tls-creds", Namespace: namespace.GetName()}, &gitTlsConfig))
assert.Equal(t, "true", gitTlsConfig.Labels[dwconstants.DevWorkspaceGitTLSLabel])
Expand Down
3 changes: 3 additions & 0 deletions pkg/common/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ const (
CheEclipseOrgScmServerEndpoint = "che.eclipse.org/scm-server-endpoint"
CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest"
CheEclipseOrgScmGitHubDisableSubdomainIsolation = "che.eclipse.org/scm-github-disable-subdomain-isolation"
OpenShiftIOOwningComponent = "openshift.io/owning-component"
ConfigOpenShiftIOInjectTrustedCaBundle = "config.openshift.io/inject-trusted-cabundle"

// DevEnvironments
PerUserPVCStorageStrategy = "per-user"
Expand Down Expand Up @@ -128,6 +130,7 @@ const (
WorkspacesConfig = "workspaces-config"
InstallOrUpdateFailed = "InstallOrUpdateFailed"
FinalizerSuffix = "finalizers.che.eclipse.org"
PublicCertsDir = "/public-certs"

// DevWorkspace
DevWorkspaceServiceAccountName = "devworkspace-controller-serviceaccount"
Expand Down
42 changes: 22 additions & 20 deletions pkg/common/test/utils.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2023 Red Hat, Inc.
// Copyright (c) 2019-2025 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
Expand All @@ -18,6 +18,8 @@ import (
"strings"
"testing"

"sigs.k8s.io/controller-runtime/pkg/reconcile"

securityv1 "github.com/openshift/api/security/v1"

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

func AssertEqualEnvVars(t *testing.T, expected []corev1.EnvVar, actual []corev1.EnvVar) {
assert.Equal(t, len(expected), len(actual))
for _, expectedEnvVar := range expected {
found := true
for _, actualEnvVar := range actual {
if expectedEnvVar.Name == actualEnvVar.Name && expectedEnvVar.Value == actualEnvVar.Value {
found = true
break
}
}

if !found {
t.Errorf("Expected env var %s=%s not found", expectedEnvVar.Name, expectedEnvVar.Value)
}
}
}

func ValidateSecurityContext(actualDeployment *appsv1.Deployment, t *testing.T) {
assert.Equal(t, corev1.Capability("ALL"), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.Capabilities.Drop[0])
assert.Equal(t, pointer.BoolPtr(false), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)
assert.Equal(t, pointer.Bool(false), actualDeployment.Spec.Template.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation)
}

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

func EnableTestMode() {
os.Setenv("MOCK_API", "1")
_ = os.Setenv("MOCK_API", "1")
}

func IsTestMode() bool {
Expand Down Expand Up @@ -216,3 +201,20 @@ func GetDeployContext(cheCluster *chev2.CheCluster, initObjs []runtime.Object) *
CheHost: strings.TrimPrefix(cheCluster.Status.CheURL, "https://"),
}
}

// EnsureReconcile runs the testReconcileFunc until it returns done=true or 10 iterations
func EnsureReconcile(
t *testing.T,
ctx *chetypes.DeployContext,
testReconcileFunc func(ctx *chetypes.DeployContext) (result reconcile.Result, done bool, err error)) {

for i := 0; i < 10; i++ {
_, done, err := testReconcileFunc(ctx)
assert.NoError(t, err)
if done {
return
}
}

assert.Fail(t, "Reconcile did not finish in 10 iterations")
}
2 changes: 1 addition & 1 deletion pkg/common/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func GetPullPolicyFromDockerImage(dockerImage string) string {
return "IfNotPresent"
}

func GetMap(value map[string]string, defaultValue map[string]string) map[string]string {
func GetMapOrDefault(value map[string]string, defaultValue map[string]string) map[string]string {
ret := value
if len(value) < 1 {
ret = defaultValue
Expand Down
70 changes: 50 additions & 20 deletions pkg/deploy/configmap.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2023 Red Hat, Inc.
// Copyright (c) 2019-2025 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
Expand Down Expand Up @@ -29,13 +29,35 @@ var ConfigMapDiffOpts = cmp.Options{
}),
}

// InitConfigMap returns a new ConfigMap Kubernetes object.
func InitConfigMap(
ctx *chetypes.DeployContext,
name string,
data map[string]string,
component string) *corev1.ConfigMap {

return &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ctx.CheCluster.Namespace,
Labels: GetLabels(component),
Annotations: map[string]string{},
},
Data: data,
}
}

func SyncConfigMapDataToCluster(
deployContext *chetypes.DeployContext,
name string,
data map[string]string,
component string) (bool, error) {

configMapSpec := GetConfigMapSpec(deployContext, name, data, component)
configMapSpec := InitConfigMap(deployContext, name, data, component)
return Sync(deployContext, configMapSpec, ConfigMapDiffOpts)
}

Expand All @@ -46,25 +68,33 @@ func SyncConfigMapSpecToCluster(
return Sync(deployContext, configMapSpec, ConfigMapDiffOpts)
}

func GetConfigMapSpec(
deployContext *chetypes.DeployContext,
name string,
data map[string]string,
component string) *corev1.ConfigMap {
// SyncConfigMap synchronizes the ConfigMap with the cluster.
func SyncConfigMap(
ctx *chetypes.DeployContext,
cm *corev1.ConfigMap,
ensuredLabels []string,
ensuredAnnotations []string) (bool, error) {

configMap := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: deployContext.CheCluster.Namespace,
Labels: GetLabels(component),
Annotations: map[string]string{},
},
Data: data,
diffs := cmp.Options{
cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"),
GetLabelsAndAnnotationsComparator(ensuredLabels, ensuredAnnotations),
}

return Sync(ctx, cm, diffs)
}

// SyncConfigMapIgnoreData synchronizes the ConfigMap with the cluster ignoring the data field.
func SyncConfigMapIgnoreData(
ctx *chetypes.DeployContext,
cm *corev1.ConfigMap,
ensuredLabels []string,
ensuredAnnotations []string) (bool, error) {

diffs := cmp.Options{
cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta"),
cmpopts.IgnoreFields(corev1.ConfigMap{}, "Data"),
GetLabelsAndAnnotationsComparator(ensuredLabels, ensuredAnnotations),
}

return configMap
return Sync(ctx, cm, diffs)
}
4 changes: 2 additions & 2 deletions pkg/deploy/configmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ func TestSyncConfigMapSpecDataToCluster(t *testing.T) {
},
}

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

// check if labels
spec = GetConfigMapSpec(deployContext, "test", map[string]string{"a": "b"}, "che")
spec = InitConfigMap(deployContext, "test", map[string]string{"a": "b"}, "che")
spec.ObjectMeta.Labels = map[string]string{"l": "v"}
_, err = SyncConfigMapSpecToCluster(deployContext, spec)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/deploy/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func GetConfigmapForGatewayConfig(
Namespace: deployContext.CheCluster.Namespace,
Labels: labels.Merge(
deploy.GetLabels(gatewayConfigComponentName),
utils.GetMap(deployContext.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)),
utils.GetMapOrDefault(deployContext.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)),
},
Data: map[string]string{
componentName + ".yml": string(gatewayConfigContent),
Expand Down Expand Up @@ -499,7 +499,7 @@ func getGatewayDeploymentSpec(ctx *chetypes.DeployContext) (*appsv1.Deployment,
}

func getContainersSpec(ctx *chetypes.DeployContext) []corev1.Container {
configLabelsMap := utils.GetMap(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)
configLabelsMap := utils.GetMapOrDefault(ctx.CheCluster.Spec.Networking.Auth.Gateway.ConfigLabels, constants.DefaultSingleHostGatewayConfigMapLabels)
gatewayImage := defaults.GetGatewayImage(ctx.CheCluster)
configSidecarImage := defaults.GetGatewayConfigSidecarImage(ctx.CheCluster)
configLabels := labels.FormatLabels(configLabelsMap)
Expand Down
15 changes: 14 additions & 1 deletion pkg/deploy/labels.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2023 Red Hat, Inc.
// Copyright (c) 2019-2025 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
Expand Down Expand Up @@ -27,6 +27,19 @@ func GetLabels(component string) map[string]string {
}
}

func GetDefaultKubernetesLabelsWith(labels ...string) []string {
defaultK8sLabels := append(
[]string{
constants.KubernetesNameLabelKey,
constants.KubernetesInstanceLabelKey,
constants.KubernetesPartOfLabelKey,
constants.KubernetesComponentLabelKey,
constants.KubernetesManagedByLabelKey,
}, labels...)

return defaultK8sLabels
}

func GetManagedByLabel() string {
return defaults.GetCheFlavor() + "-operator"
}
Expand Down
21 changes: 21 additions & 0 deletions pkg/deploy/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,27 @@ func DeleteIgnoreIfNotFound(
return err
}

func GetLabelsAndAnnotationsComparator(
ensuredLabels []string,
ensuredAnnotations []string) cmp.Option {

return cmp.Comparer(func(x, y metav1.ObjectMeta) bool {
for _, label := range ensuredLabels {
if x.Labels[label] != y.Labels[label] {
return false
}
}

for _, annotation := range ensuredAnnotations {
if x.Annotations[annotation] != y.Annotations[annotation] {
return false
}
}

return true
})
}

// doCreate creates object.
// Return error if object cannot be created otherwise returns nil.
func doCreate(
Expand Down
Loading