Skip to content
Draft
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
16 changes: 16 additions & 0 deletions api/v2/checluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"strconv"
"strings"

"github.com/eclipse-che/che-operator/pkg/common/utils"
"sigs.k8s.io/controller-runtime/pkg/webhook"

"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
Expand Down Expand Up @@ -124,6 +125,11 @@ func (r *CheClusterValidator) ValidateCreate(_ context.Context, obj runtime.Obje
if err := r.ensureSingletonCheCluster(); err != nil {
return []string{}, err
}

if err := r.ensureDevWorkspaceOperatorConfigApiExist(); err != nil {
return []string{}, err
}

return []string{}, r.validate(cheCluster)
}

Expand Down Expand Up @@ -162,6 +168,16 @@ func (r *CheClusterValidator) ensureSingletonCheCluster() error {
return nil
}

func (r *CheClusterValidator) ensureDevWorkspaceOperatorConfigApiExist() error {
k8sHelper := k8shelper.New()

if !utils.IsK8SResourceServed(k8sHelper.GetClientset().Discovery(), constants.DevWorkspaceOperatorConfigPlural) {
return fmt.Errorf(constants.DevWorkspaceOperatorNotExistsErrorMsg)
}

return nil
}

func (r *CheClusterValidator) validate(checluster *CheCluster) error {
for _, github := range checluster.Spec.GitServices.GitHub {
if err := r.validateOAuthSecret(github.SecretName, "github", github.Endpoint, github.DisableSubdomainIsolation, checluster.Namespace); err != nil {
Expand Down
18 changes: 9 additions & 9 deletions build/scripts/olm/test-catalog-from-sources.sh
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ run() {
make create-operatorgroup NAME="eclipse-che" NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}

# Install Dev Workspace operator next version
make install-devworkspace CHANNEL="next" VERBOSE=${VERBOSE} OPERATOR_NAMESPACE="${NAMESPACE}"
# make install-devworkspace CHANNEL="next" VERBOSE=${VERBOSE} OPERATOR_NAMESPACE="${NAMESPACE}"

exposeOpenShiftRegistry
createEclipseCheCatalogFromSources
Expand All @@ -166,14 +166,14 @@ run() {
VERBOSE=${VERBOSE}
make wait-pod-running NAMESPACE="${NAMESPACE}" SELECTOR="app.kubernetes.io/component=che-operator"

if [[ $(oc get checluster -n "${NAMESPACE}" --no-headers | wc -l) == 0 ]]; then
getCheClusterCRFromInstalledCSV | oc apply -n "${NAMESPACE}" -f -
if [[ -n ${CR_PATCH_YAML} ]]; then
patch=$(yq -r "." "${CR_PATCH_YAML}" | tr -d "\n" )
oc patch checluster eclipse-che -n "${NAMESPACE}" --type='merge' -p "${patch}"
fi
fi
make wait-eclipseche-version VERSION="$(getCheVersionFromInstalledCSV)" NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
# if [[ $(oc get checluster -n "${NAMESPACE}" --no-headers | wc -l) == 0 ]]; then
# getCheClusterCRFromInstalledCSV | oc apply -n "${NAMESPACE}" -f -
# if [[ -n ${CR_PATCH_YAML} ]]; then
# patch=$(yq -r "." "${CR_PATCH_YAML}" | tr -d "\n" )
# oc patch checluster eclipse-che -n "${NAMESPACE}" --type='merge' -p "${patch}"
# fi
# fi
# make wait-eclipseche-version VERSION="$(getCheVersionFromInstalledCSV)" NAMESPACE="${NAMESPACE}" VERBOSE=${VERBOSE}
}

init "$@"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ metadata:
categories: Developer Tools
certified: "false"
containerImage: quay.io/eclipse/che-operator:next
createdAt: "2026-01-16T09:21:06Z"
createdAt: "2026-01-21T12:02:21Z"
description: A Kube-native development solution that delivers portable and collaborative
developer workspaces.
features.operators.openshift.io/cnf: "false"
Expand All @@ -108,7 +108,7 @@ metadata:
operatorframework.io/arch.amd64: supported
operatorframework.io/arch.arm64: supported
operatorframework.io/os.linux: supported
name: eclipse-che.v7.114.0-951.next
name: eclipse-che.v7.114.0-952.next
namespace: placeholder
spec:
apiservicedefinitions: {}
Expand Down Expand Up @@ -906,6 +906,14 @@ spec:
verbs:
- get
- create
- apiGroups:
- operators.coreos.com
resources:
- subscriptions
verbs:
- get
- watch
- list
serviceAccountName: che-operator
deployments:
- label:
Expand Down Expand Up @@ -1141,7 +1149,7 @@ spec:
name: gateway-authorization-sidecar-k8s
- image: quay.io/che-incubator/header-rewrite-proxy:latest
name: gateway-header-sidecar
version: 7.114.0-951.next
version: 7.114.0-952.next
webhookdefinitions:
- admissionReviewVersions:
- v1
Expand Down
17 changes: 0 additions & 17 deletions bundle/next/eclipse-che/metadata/dependencies.yaml

This file was deleted.

99 changes: 53 additions & 46 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package main

import (
"context"
"flag"
"os"
"time"
Expand Down Expand Up @@ -154,6 +155,7 @@ func init() {
utilruntime.Must(projectv1.AddToScheme(scheme))
utilruntime.Must(securityv1.Install(scheme))
utilruntime.Must(templatev1.Install(scheme))
utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme))
}
}

Expand Down Expand Up @@ -187,28 +189,7 @@ func printVersion(logger logr.Logger) {
logger.Info("Operator is running on ", "Infrastructure", infra)
}

// getWatchNamespace returns the Namespace the operator should be watching for changes
func getWatchNamespace() (string, error) {
// WatchNamespaceEnvVar is the constant for env variable WATCH_NAMESPACE
// which specifies the Namespace to watch.
// An empty value means the operator is running with cluster scope.
var watchNamespaceEnvVar = "WATCH_NAMESPACE"

ns, found := os.LookupEnv(watchNamespaceEnvVar)
if !found {
return "", fmt.Errorf("%s must be set", watchNamespaceEnvVar)
}

return ns, nil
}

func main() {
watchNamespace, err := getWatchNamespace()
if err != nil {
setupLog.Error(err, "unable to get WatchNamespace, "+
"the manager will watch and manage resources in all namespaces")
}

config := ctrl.GetConfigOrDie()

discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
Expand Down Expand Up @@ -252,12 +233,6 @@ func main() {
LeaseDuration: &leaseDuration,
RenewDeadline: &renewDeadline,
NewCache: cacheFunction,

// NOTE: We CANNOT limit the manager to a single namespace, because that would limit the
// devworkspace routing reconciler to a single namespace, which would make it totally unusable.
// Instead, if some controller wants to limit itself to single namespace, it can do it
// for example using an event filter, as checontroller does.
// Namespace: watchNamespace,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
Expand All @@ -270,22 +245,43 @@ func main() {
os.Exit(1)
}

terminationPeriod := int64(20)
if !test.IsTestMode() {
namespace, err := infrastructure.GetOperatorNamespace()
if err == nil {
terminationPeriod = signal.GetTerminationGracePeriodSeconds(mgr.GetAPIReader(), namespace)
}
}
sigHandler := signal.SetupSignalHandler(terminationPeriod)

// Setup all Controllers
cheReconciler := checontroller.NewReconciler(mgr.GetClient(), nonCachingClient, discoveryClient, mgr.GetScheme(), watchNamespace)
cheReconciler := checontroller.NewReconciler(mgr.GetClient(), nonCachingClient, discoveryClient, mgr.GetScheme())
if err = cheReconciler.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to set up controller", "controller", "CheCluster")
os.Exit(1)
}

routing := dwr.DevWorkspaceRoutingReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("DevWorkspaceRouting"),
Scheme: mgr.GetScheme(),
SolverGetter: solver.Getter(mgr.GetScheme()),
}
if err := routing.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to set up controller", "controller", "DevWorkspaceRouting")
os.Exit(1)
if utils.IsK8SResourceServed(discoveryClient, constants.DevWorkspaceRoutingPlural) {
routing := dwr.DevWorkspaceRoutingReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("DevWorkspaceRouting"),
Scheme: mgr.GetScheme(),
SolverGetter: solver.Getter(mgr.GetScheme()),
}
if err := routing.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to set up controller", "controller", "DevWorkspaceRouting")
os.Exit(1)
}
} else {
setupLog.Info("DevWorkspaceRouting API is not available.")
go waitUntilDevWorkspaceRoutingApiExists(
discoveryClient,
sigHandler,
func() {
setupLog.Info("DevWorkspaceRouting API is available. Restarting operator ...")
os.Exit(0)
},
)
}

namespacecache := namespacecache.NewNamespaceCache(nonCachingClient)
Expand All @@ -302,15 +298,6 @@ func main() {
os.Exit(1)
}

terminationPeriod := int64(20)
if !test.IsTestMode() {
namespace, err := infrastructure.GetOperatorNamespace()
if err == nil {
terminationPeriod = signal.GetTerminationGracePeriodSeconds(mgr.GetAPIReader(), namespace)
}
}
sigHandler := signal.SetupSignalHandler(terminationPeriod)

// we install the devworkspace CheCluster reconciler even if dw is not supported so that it
// can write meaningful status messages into the CheCluster CRs.
dwChe := devworkspace.CheClusterReconciler{}
Expand Down Expand Up @@ -413,3 +400,23 @@ func getCacheFunc() (cache.NewCacheFunc, error) {
return cache.New(config, opts)
}, nil
}

func waitUntilDevWorkspaceRoutingApiExists(
discoveryClient discovery.DiscoveryInterface,
context context.Context,
callback func(),
) {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()

for {
select {
case <-ticker.C:
if utils.IsK8SResourceServed(discoveryClient, constants.DevWorkspaceRoutingPlural) {
callback()
}
case <-context.Done():
return
}
}
}
10 changes: 9 additions & 1 deletion config/rbac/cluster_role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,12 @@ rules:
- servicemonitors
verbs:
- get
- create
- create
- apiGroups:
- operators.coreos.com
resources:
- subscriptions
verbs:
- get
- watch
- list
17 changes: 5 additions & 12 deletions controllers/che/checluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/eclipse-che/che-operator/pkg/common/constants"
k8sclient "github.com/eclipse-che/che-operator/pkg/common/k8s-client"
"github.com/eclipse-che/che-operator/pkg/common/reconciler"
"github.com/eclipse-che/che-operator/pkg/deploy/devworkspace"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/controller"

Expand All @@ -33,7 +34,6 @@ import (

"github.com/devfile/devworkspace-operator/pkg/infrastructure"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
"github.com/eclipse-che/che-operator/pkg/common/utils"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/consolelink"
"github.com/eclipse-che/che-operator/pkg/deploy/dashboard"
Expand Down Expand Up @@ -80,17 +80,14 @@ type CheClusterReconciler struct {
// in the API Server
discoveryClient discovery.DiscoveryInterface
reconcilerManager *reconciler.ReconcilerManager
// the namespace to which to limit the reconciliation. If empty, all namespaces are considered
namespace string
}

// NewReconciler returns a new CheClusterReconciler
func NewReconciler(
k8sclient client.Client,
noncachedClient client.Client,
discoveryClient discovery.DiscoveryInterface,
scheme *k8sruntime.Scheme,
namespace string) *CheClusterReconciler {
scheme *k8sruntime.Scheme) *CheClusterReconciler {

reconcilerManager := reconciler.NewReconcilerManager()

Expand All @@ -99,6 +96,7 @@ func NewReconciler(
reconcilerManager.AddReconciler(migration.NewMigrator())
reconcilerManager.AddReconciler(migration.NewCheClusterDefaultsCleaner())
reconcilerManager.AddReconciler(NewCheClusterValidator())
reconcilerManager.AddReconciler(devworkspace.NewDevWorkspaceVersionValidator(constants.MinimumDevWorkspaceVersion))
}

reconcilerManager.AddReconciler(tls.NewCertificatesReconciler())
Expand Down Expand Up @@ -133,23 +131,22 @@ func NewReconciler(
client: k8sclient,
nonCachedClient: noncachedClient,
discoveryClient: discoveryClient,
namespace: namespace,
reconcilerManager: reconcilerManager,
}
}

// SetupWithManager sets up the controller with the Manager.
func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
var toTrustedBundleConfigMapRequestMapper handler.MapFunc = func(ctx context.Context, obj client.Object) []reconcile.Request {
isTrusted, reconcileRequest := IsTrustedBundleConfigMap(r.client, r.namespace, obj)
isTrusted, reconcileRequest := IsTrustedBundleConfigMap(r.client, obj)
if isTrusted {
return []reconcile.Request{reconcileRequest}
}
return []reconcile.Request{}
}

var toEclipseCheRelatedObjRequestMapper handler.MapFunc = func(ctx context.Context, obj client.Object) []reconcile.Request {
isEclipseCheRelatedObj, reconcileRequest := IsEclipseCheRelatedObj(r.client, r.namespace, obj)
isEclipseCheRelatedObj, reconcileRequest := IsEclipseCheRelatedObj(r.client, obj)
if isEclipseCheRelatedObj {
return []reconcile.Request{reconcileRequest}
}
Expand Down Expand Up @@ -181,10 +178,6 @@ func (r *CheClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
bld.Owns(&networking.Ingress{})
}

if r.namespace != "" {
bld = bld.WithEventFilter(utils.InNamespaceEventFilter(r.namespace))
}

// Use controller.TypedOptions to allow to configure 2 controllers for same object being reconciled
return bld.WithOptions(
controller.TypedOptions[reconcile.Request]{
Expand Down
Loading
Loading