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
4 changes: 2 additions & 2 deletions build/scripts/olm/release-catalog.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ build () {
CHANNEL="${CHANNEL}" \
BUNDLE_IMG="${BUNDLE_IMAGE}" \
IMAGE_TOOL="${IMAGE_TOOL}" \
ARCHS="linux/arm64,linux/amd64"
ARCHS="linux/arm64,linux/amd64,linux/s390x,linux/ppc64le"
else
make bundle-build bundle-push \
CHANNEL="${CHANNEL}" \
Expand Down Expand Up @@ -116,7 +116,7 @@ build () {
CHANNEL="${CHANNEL}" \
CATALOG_IMG="${CATALOG_IMAGE}" \
IMAGE_TOOL="${IMAGE_TOOL}" \
ARCHS="linux/arm64,linux/amd64"
ARCHS="linux/arm64,linux/amd64,linux/s390x,linux/ppc64le"
else
make catalog-build catalog-push \
CHANNEL="${CHANNEL}" \
Expand Down
59 changes: 57 additions & 2 deletions pkg/deploy/gateway/oauth_proxy.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-2026 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 @@ -13,6 +13,7 @@
package gateway

import (
"context"
"fmt"
"strings"

Expand All @@ -31,6 +32,9 @@ import (
"github.com/eclipse-che/che-operator/pkg/deploy"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
)

func getGatewayOauthProxyConfigSpec(ctx *chetypes.DeployContext, cookieSecret string) corev1.ConfigMap {
Expand Down Expand Up @@ -175,6 +179,52 @@ func oauthScopeConfig(instance *chev2.CheCluster) string {
return ""
}

// resolveOpenShiftOAuthProxyImage returns the oauth-proxy image from the cluster's own release
// payload via the openshift/oauth-proxy ImageStream, which is maintained by the Cluster Version
// Operator and always carries an architecture-native, digest-pinned reference. Returns an empty
// string when the ImageStream is unavailable so the caller can fall back to the operator default.
func resolveOpenShiftOAuthProxyImage(ctx *chetypes.DeployContext) string {
imageStream := &unstructured.Unstructured{}
imageStream.SetGroupVersionKind(schema.GroupVersionKind{
Group: "image.openshift.io",
Version: "v1",
Kind: "ImageStream",
})

if err := ctx.ClusterAPI.NonCachingClient.Get(
context.TODO(),
types.NamespacedName{Name: "oauth-proxy", Namespace: "openshift"},
imageStream,
); err != nil {
logrus.Warnf("Failed to resolve oauth-proxy image from cluster release payload, using default: %v", err)
return ""
}

tags, _, _ := unstructured.NestedSlice(imageStream.Object, "status", "tags")
for _, tag := range tags {
tagMap, ok := tag.(map[string]interface{})
if !ok {
continue
}
items, _, _ := unstructured.NestedSlice(tagMap, "items")
if len(items) == 0 {
continue
}
firstItem, ok := items[0].(map[string]interface{})
if !ok {
continue
}
ref, _, _ := unstructured.NestedString(firstItem, "dockerImageReference")
if ref != "" {
logrus.Infof("Resolved oauth-proxy image from cluster release payload: %s", ref)
return ref
}
}

logrus.Warn("openshift/oauth-proxy ImageStream found but contains no image reference, using default")
return ""
}

func getOauthProxyContainerSpec(ctx *chetypes.DeployContext) corev1.Container {
// append env var with ConfigMap revision to restore pod automatically when config has been changed
cm := &corev1.ConfigMap{}
Expand All @@ -184,7 +234,12 @@ func getOauthProxyContainerSpec(ctx *chetypes.DeployContext) corev1.Container {
var image, probePath string
var args = []string{"--config=/etc/oauth-proxy/oauth-proxy.cfg"}
if infrastructure.IsOpenShiftOAuthEnabled() {
image = defaults.GetGatewayOpenShiftAuthenticationSidecarImage(ctx.CheCluster)
// Prefer the architecture-native image from the cluster's release payload;
// fall back to RELATED_IMAGE_gateway_authentication_sidecar when unavailable.
image = resolveOpenShiftOAuthProxyImage(ctx)
if image == "" {
image = defaults.GetGatewayOpenShiftAuthenticationSidecarImage(ctx.CheCluster)
}
probePath = "/oauth/healthz"
} else {
image = defaults.GetGatewayKubernetesAuthenticationSidecarImage(ctx.CheCluster)
Expand Down
48 changes: 47 additions & 1 deletion pkg/deploy/gateway/oauth_proxy_test.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.
// 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 @@ -21,6 +21,8 @@ import (
"github.com/eclipse-che/che-operator/pkg/common/infrastructure"
"github.com/eclipse-che/che-operator/pkg/common/test"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func TestCookieExpireForOpenShiftOauthProxyConfig(t *testing.T) {
Expand Down Expand Up @@ -122,3 +124,47 @@ func TestAccessTokenDefinedForKubernetesOauthProxyConfig(t *testing.T) {
assert.Contains(t, config, "pass_access_token = true")
assert.NotContains(t, config, "pass_authorization_header = true")
}

// TestResolveOpenShiftOAuthProxyImage_ImageStreamPresent verifies that when the
// openshift/oauth-proxy ImageStream is present the architecture-native digest-pinned
// image from the cluster's release payload is returned.
func TestResolveOpenShiftOAuthProxyImage_ImageStreamPresent(t *testing.T) {
infrastructure.InitializeForTesting(infrastructure.OpenShiftV4)

expectedImage := "quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:503de130e594b7864ab9b63b910d313b4e17cdd09ddd59323729fa221ba1b39c"

imageStream := &unstructured.Unstructured{}
imageStream.SetGroupVersionKind(schema.GroupVersionKind{
Group: "image.openshift.io",
Version: "v1",
Kind: "ImageStream",
})
imageStream.SetName("oauth-proxy")
imageStream.SetNamespace("openshift")
_ = unstructured.SetNestedSlice(imageStream.Object, []interface{}{
map[string]interface{}{
"tag": "v4.4",
"items": []interface{}{
map[string]interface{}{
"dockerImageReference": expectedImage,
},
},
},
}, "status", "tags")

ctx := test.NewCtxBuilder().WithObjects(imageStream).Build()

resolved := resolveOpenShiftOAuthProxyImage(ctx)
assert.Equal(t, expectedImage, resolved)
}

// TestResolveOpenShiftOAuthProxyImage_ImageStreamAbsent verifies that an empty string
// is returned when the ImageStream is not present so the caller falls back to the default.
func TestResolveOpenShiftOAuthProxyImage_ImageStreamAbsent(t *testing.T) {
infrastructure.InitializeForTesting(infrastructure.OpenShiftV4)

ctx := test.NewCtxBuilder().Build()

resolved := resolveOpenShiftOAuthProxyImage(ctx)
assert.Equal(t, "", resolved)
}