Skip to content
Open
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
2 changes: 1 addition & 1 deletion controllers/backupcronjob/backupcronjob_controller.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down
17 changes: 6 additions & 11 deletions controllers/workspace/devworkspace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ import (
"strings"
"time"

"github.com/devfile/devworkspace-operator/pkg/library/initcontainers"
"github.com/devfile/devworkspace-operator/pkg/library/overrides"
"github.com/devfile/devworkspace-operator/pkg/library/ssh"

dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
devfilevalidation "github.com/devfile/api/v2/pkg/validation"
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
Expand All @@ -35,15 +31,19 @@ import (
wkspConfig "github.com/devfile/devworkspace-operator/pkg/config"
"github.com/devfile/devworkspace-operator/pkg/constants"
"github.com/devfile/devworkspace-operator/pkg/dwerrors"
"github.com/devfile/devworkspace-operator/pkg/httpfactory"
"github.com/devfile/devworkspace-operator/pkg/library/annotate"
containerlib "github.com/devfile/devworkspace-operator/pkg/library/container"
wsDefaults "github.com/devfile/devworkspace-operator/pkg/library/defaults"
"github.com/devfile/devworkspace-operator/pkg/library/env"
"github.com/devfile/devworkspace-operator/pkg/library/flatten"
"github.com/devfile/devworkspace-operator/pkg/library/home"
"github.com/devfile/devworkspace-operator/pkg/library/initcontainers"
kubesync "github.com/devfile/devworkspace-operator/pkg/library/kubernetes"
"github.com/devfile/devworkspace-operator/pkg/library/overrides/restrictions"
"github.com/devfile/devworkspace-operator/pkg/library/projects"
"github.com/devfile/devworkspace-operator/pkg/library/restore"
"github.com/devfile/devworkspace-operator/pkg/library/ssh"
"github.com/devfile/devworkspace-operator/pkg/library/status"
"github.com/devfile/devworkspace-operator/pkg/provision/automount"
"github.com/devfile/devworkspace-operator/pkg/provision/metadata"
Expand Down Expand Up @@ -258,7 +258,7 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request
return reconcile.Result{Requeue: true}, err
}

httpClient := httpClientsFactory.GetHttpClient(ctx, config.Routing)
httpClient := httpfactory.HttpFactory.GetHttpClient(ctx, config.Routing)

flattenHelpers := flatten.ResolverTools{
WorkspaceNamespace: workspace.Namespace,
Expand Down Expand Up @@ -339,7 +339,7 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request
workspace.Config.Workspace.DefaultContainerResources,
workspace.Config.Workspace.ContainerResourceCaps,
workspace.Config.Workspace.PostStartTimeout,
overrides.GetRestrictedContainerOverrideFields(workspace),
restrictions.GetRestrictedContainerFields(workspace),
postStartDebugTrapSleepDuration,
)
if err != nil {
Expand Down Expand Up @@ -789,11 +789,6 @@ func (r *DevWorkspaceReconciler) getWorkspaceId(ctx context.Context, workspace *
}

func (r *DevWorkspaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
err := SetupHttpClientsFactory(mgr.GetClient(), mgr.GetLogger())
if err != nil {
return err
}

maxConcurrentReconciles, err := wkspConfig.GetMaxConcurrentReconciles()
if err != nil {
return err
Expand Down
32 changes: 16 additions & 16 deletions controllers/workspace/devworkspace_controller_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand All @@ -22,12 +22,12 @@ import (

dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
controllerv1alpha1 "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
workspacecontroller "github.com/devfile/devworkspace-operator/controllers/workspace"
"github.com/devfile/devworkspace-operator/controllers/workspace/internal/testutil"
"github.com/devfile/devworkspace-operator/pkg/common"
"github.com/devfile/devworkspace-operator/pkg/conditions"
"github.com/devfile/devworkspace-operator/pkg/config"
"github.com/devfile/devworkspace-operator/pkg/constants"
"github.com/devfile/devworkspace-operator/pkg/httpfactory"
"github.com/devfile/devworkspace-operator/pkg/library/projects"
"github.com/devfile/devworkspace-operator/pkg/library/restore"
. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -184,7 +184,7 @@ var _ = Describe("DevWorkspace Controller", func() {

AfterEach(func() {
deleteDevWorkspace(devWorkspaceName)
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())
})

It("Creates roles and rolebindings", func() {
Expand Down Expand Up @@ -317,7 +317,7 @@ var _ = Describe("DevWorkspace Controller", func() {
devworkspace := getExistingDevWorkspace(devWorkspaceName)
workspaceID := devworkspace.Status.DevWorkspaceId

workspacecontroller.SetupHttpClientsForTesting(&http.Client{
httpfactory.SetupHttpClientsForTesting(&http.Client{
Transport: &testutil.TestRoundTripper{
Data: map[string]testutil.TestResponse{
"test-url/healthz": {
Expand Down Expand Up @@ -355,7 +355,7 @@ var _ = Describe("DevWorkspace Controller", func() {
const testURL = "test-url"

BeforeEach(func() {
workspacecontroller.SetupHttpClientsForTesting(&http.Client{
httpfactory.SetupHttpClientsForTesting(&http.Client{
Transport: &testutil.TestRoundTripper{
Data: map[string]testutil.TestResponse{
fmt.Sprintf("%s/healthz", testURL): {
Expand All @@ -368,7 +368,7 @@ var _ = Describe("DevWorkspace Controller", func() {

AfterEach(func() {
deleteDevWorkspace(devWorkspaceName)
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())
})

It("Mounts image pull secrets to the DevWorkspace Deployment", func() {
Expand Down Expand Up @@ -954,7 +954,7 @@ var _ = Describe("DevWorkspace Controller", func() {
const testURL = "test-url"

BeforeEach(func() {
workspacecontroller.SetupHttpClientsForTesting(&http.Client{
httpfactory.SetupHttpClientsForTesting(&http.Client{
Transport: &testutil.TestRoundTripper{
Data: map[string]testutil.TestResponse{
fmt.Sprintf("%s/healthz", testURL): {
Expand All @@ -967,7 +967,7 @@ var _ = Describe("DevWorkspace Controller", func() {

AfterEach(func() {
deleteDevWorkspace(devWorkspaceName)
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())
})

It("Sets the runtimeClassName from the DWOC", func() {
Expand Down Expand Up @@ -1033,7 +1033,7 @@ var _ = Describe("DevWorkspace Controller", func() {
const testURL = "test-url"

BeforeEach(func() {
workspacecontroller.SetupHttpClientsForTesting(&http.Client{
httpfactory.SetupHttpClientsForTesting(&http.Client{
Transport: &testutil.TestRoundTripper{
Data: map[string]testutil.TestResponse{
fmt.Sprintf("%s/healthz", testURL): {
Expand All @@ -1047,7 +1047,7 @@ var _ = Describe("DevWorkspace Controller", func() {

AfterEach(func() {
deleteDevWorkspace(devWorkspaceName)
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())
})

It("Stops workspaces and scales deployment to zero", func() {
Expand Down Expand Up @@ -1224,7 +1224,7 @@ var _ = Describe("DevWorkspace Controller", func() {

BeforeEach(func() {
By("Setting up HTTP client")
workspacecontroller.SetupHttpClientsForTesting(&http.Client{
httpfactory.SetupHttpClientsForTesting(&http.Client{
Transport: &testutil.TestRoundTripper{
Data: map[string]testutil.TestResponse{
fmt.Sprintf("%s/healthz", testURL): {
Expand All @@ -1241,7 +1241,7 @@ var _ = Describe("DevWorkspace Controller", func() {
deleteDevWorkspace(altDevWorkspaceName)
cleanupPVC("claim-devworkspace")
By("Resetting HTTP client")
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())
})

It("Cleans up workspace PVC storage when other workspaces exist", func() {
Expand Down Expand Up @@ -1361,7 +1361,7 @@ var _ = Describe("DevWorkspace Controller", func() {
const testURL = "test-url"

BeforeEach(func() {
workspacecontroller.SetupHttpClientsForTesting(&http.Client{
httpfactory.SetupHttpClientsForTesting(&http.Client{
Transport: &testutil.TestRoundTripper{
Data: map[string]testutil.TestResponse{
fmt.Sprintf("%s/healthz", testURL): {
Expand All @@ -1374,7 +1374,7 @@ var _ = Describe("DevWorkspace Controller", func() {

AfterEach(func() {
deleteDevWorkspace(devWorkspaceName)
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())
})

It("Restores workspace from backup with common PVC", func() {
Expand Down Expand Up @@ -1563,7 +1563,7 @@ var _ = Describe("DevWorkspace Controller", func() {
devworkspace := getExistingDevWorkspace(devWorkspaceName)
workspaceID := devworkspace.Status.DevWorkspaceId

workspacecontroller.SetupHttpClientsForTesting(&http.Client{
httpfactory.SetupHttpClientsForTesting(&http.Client{
Transport: &testutil.TestRoundTripper{
Data: map[string]testutil.TestResponse{
"test-url/healthz": {
Expand Down Expand Up @@ -1595,7 +1595,7 @@ var _ = Describe("DevWorkspace Controller", func() {
Expect(runningCondition.Status).Should(Equal(corev1.ConditionTrue))

// Clean up
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())
})

It("Ensures preStart initContainers are run after project-clone", func() {
Expand Down
3 changes: 2 additions & 1 deletion controllers/workspace/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/devworkspace-operator/pkg/common"
"github.com/devfile/devworkspace-operator/pkg/dwerrors"
"github.com/devfile/devworkspace-operator/pkg/httpfactory"
"github.com/devfile/devworkspace-operator/pkg/provision/sync"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -210,7 +211,7 @@ func checkServerStatus(workspace *common.DevWorkspaceWithConfig) (ok bool, respo
}
healthz.Path = path.Join(healthz.Path, "healthz")

healthCheckHttpClient := httpClientsFactory.GetHealthCheckHttpClient()
healthCheckHttpClient := httpfactory.HttpFactory.GetHealthCheckHttpClient()
resp, err := healthCheckHttpClient.Get(healthz.String())
if err != nil {
return false, nil, &dwerrors.RetryError{Err: err, Message: "Failed to check server status", RequeueAfter: 1 * time.Second}
Expand Down
5 changes: 3 additions & 2 deletions controllers/workspace/suite_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand All @@ -22,6 +22,7 @@ import (
"runtime"
"testing"

"github.com/devfile/devworkspace-operator/pkg/httpfactory"
"sigs.k8s.io/controller-runtime/pkg/webhook"

dwv1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha1"
Expand Down Expand Up @@ -147,7 +148,7 @@ var _ = BeforeSuite(func() {
Expect(err).NotTo(HaveOccurred())

// Set HTTP client to fail all requests by default; tests that require HTTP must set this up directly
workspacecontroller.SetupHttpClientsForTesting(getBasicTestHttpClient())
httpfactory.SetupHttpClientsForTesting(getBasicTestHttpClient())

// Skip trying to set up / test webhooks for now

Expand Down
9 changes: 8 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2025 Red Hat, Inc.
// Copyright (c) 2019-2026 Red Hat, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand All @@ -22,6 +22,7 @@ import (
"os"
"runtime"

"github.com/devfile/devworkspace-operator/pkg/httpfactory"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"

"github.com/devfile/devworkspace-operator/controllers/controller/devworkspacerouting"
Expand Down Expand Up @@ -146,6 +147,12 @@ func main() {
os.Exit(1)
}

err = httpfactory.SetupHttpClientsFactory(mgr.GetClient(), mgr.GetLogger())
if err != nil {
setupLog.Error(err, "Failed to setup Http clients factory")
os.Exit(1)
}

nonCachingClient, err := client.New(mgr.GetConfig(), client.Options{Scheme: scheme})
if err != nil {
setupLog.Error(err, "unable to initialize non-caching client")
Expand Down
12 changes: 10 additions & 2 deletions pkg/constants/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ const (
// can either be specified as a decimal (e.g. '416') or as an octal by prefixing the number with zero (e.g. '0640')
DevWorkspaceMountAccessModeAnnotation = "controller.devfile.io/mount-access-mode"

// DevWorkspaceValidatedK8sResourcesAnnotation is the annotation key used to store the list of
// Kubernetes resources that have been validated by the mutating webhook via SubjectAccessReview.
// The controller uses this to determine which objects the workspace creator is authorized to create.
DevWorkspaceValidatedK8sResourcesAnnotation = "controller.devfile.io/validated-kubernetes-resources"
// DevWorkspaceValidatedSCCAnnotation is the annotation key used to store the SecurityContextConstraints
// that have been validated by the mutating webhook via SubjectAccessReview.
DevWorkspaceValidatedSCCAnnotation = "controller.devfile.io/validated-scc"

// DevWorkspaceGitCredentialLabel is the label key to specify if the secret is a git credential. All secrets who
// specify this label in a namespace will consolidate into one secret before mounting into a devworkspace.
// Only secret data with the credentials key will be used and credentials must be the base64 encoded version
Expand Down Expand Up @@ -161,10 +169,10 @@ const (
// fails to start (i.e. enters the "Failed" phase), its deployment will not be scaled down in order to allow viewing logs, etc.
DevWorkspaceDebugStartAnnotation = "controller.devfile.io/debug-start"

// WebhookRestartedAtAnnotation holds the the time (unixnano) of when the webhook server was forced to restart by controller
// WebhookRestartedAtAnnotation holds the time (unixnano) of when the webhook server was forced to restart by controller
WebhookRestartedAtAnnotation = "controller.devfile.io/restarted-at"

// DevWorkspaceStartedAtAnnotation holds the the time (unixnano) of when the devworkspace was started
// DevWorkspaceStartedAtAnnotation holds the time (unixnano) of when the devworkspace was started
DevWorkspaceStartedAtAnnotation = "controller.devfile.io/started-at"

// RoutingAnnotationInfix is the infix of the annotations of DevWorkspace that are passed down as annotation to the DevWorkspaceRouting objects.
Expand Down
6 changes: 3 additions & 3 deletions controllers/workspace/http.go → pkg/httpfactory/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package controllers
package httpfactory

import (
"context"
Expand All @@ -35,7 +35,7 @@ import (
"golang.org/x/net/http/httpproxy"
)

var httpClientsFactory HttpClientsFactory
var HttpFactory HttpClientsFactory

type HttpClientsFactory interface {
// GetHttpClient returns an HTTP client configured with proxy, TLS, and custom CA certificates
Expand Down Expand Up @@ -84,7 +84,7 @@ func SetupHttpClientsFactory(k8s client.Client, logger logr.Logger) error {
InsecureSkipVerify: true,
}

httpClientsFactory = &DefaultHttpClientsFactory{
HttpFactory = &DefaultHttpClientsFactory{
k8s: k8s,
logger: logger,
systemCertPool: systemCertPool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package controllers
package httpfactory

import (
"context"
Expand Down Expand Up @@ -40,26 +40,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)

type TestHttpClientsFactory struct {
client *http.Client
healthCheckHttpClient *http.Client
}

func (t *TestHttpClientsFactory) GetHttpClient(_ context.Context, _ *controller.RoutingConfig) *http.Client {
return t.client
}

func (t *TestHttpClientsFactory) GetHealthCheckHttpClient() *http.Client {
return t.healthCheckHttpClient
}

func SetupHttpClientsForTesting(client *http.Client) {
httpClientsFactory = &TestHttpClientsFactory{
client: client,
healthCheckHttpClient: client,
}
}

func TestHealthCheckHttpClient(t *testing.T) {
t.Run("returns non-nil client", func(t *testing.T) {
factory := newTestFactory(t)
Expand Down
Loading
Loading