diff --git a/pkg/console/subresource/configmap/brand_ocp.go b/pkg/console/subresource/configmap/brand_ocp.go index ec62a6a7f..d9e6b97b1 100644 --- a/pkg/console/subresource/configmap/brand_ocp.go +++ b/pkg/console/subresource/configmap/brand_ocp.go @@ -3,7 +3,13 @@ package configmap -const ( - DEFAULT_BRAND = "ocp" - DEFAULT_DOC_URL = "https://access.redhat.com/documentation/en-us/openshift_container_platform/5.0/" -) +import "os" + +const DEFAULT_BRAND = "ocp" + +// DefaultDocURL returns the documentation base URL for OCP, dynamically +// deriving the version from the OPERATOR_IMAGE_VERSION environment variable. +// Falls back to "latest" if the version is unavailable or cannot be parsed. +func DefaultDocURL() string { + return formatOCPDocURL(os.Getenv("OPERATOR_IMAGE_VERSION")) +} diff --git a/pkg/console/subresource/configmap/brand_okd.go b/pkg/console/subresource/configmap/brand_okd.go index 24be5c9f7..ee7d65fb6 100644 --- a/pkg/console/subresource/configmap/brand_okd.go +++ b/pkg/console/subresource/configmap/brand_okd.go @@ -7,3 +7,9 @@ const ( DEFAULT_BRAND = "okd" DEFAULT_DOC_URL = "https://docs.okd.io/latest/" ) + +// DefaultDocURL returns the documentation base URL for OKD. +// OKD always uses "latest", so no version derivation is needed. +func DefaultDocURL() string { + return DEFAULT_DOC_URL +} diff --git a/pkg/console/subresource/configmap/configmap.go b/pkg/console/subresource/configmap/configmap.go index eecff0757..b204a83e4 100644 --- a/pkg/console/subresource/configmap/configmap.go +++ b/pkg/console/subresource/configmap/configmap.go @@ -61,7 +61,7 @@ func DefaultConfigMap( defaultConfig, err := defaultBuilder.Host(consoleHost). LogoutURL(defaultLogoutURL). Brand(DEFAULT_BRAND). - DocURL(DEFAULT_DOC_URL). + DocURL(DefaultDocURL()). APIServerURL(apiServerURL). Monitoring(monitoringSharedConfig). InactivityTimeout(inactivityTimeoutSeconds). diff --git a/pkg/console/subresource/configmap/doc_url.go b/pkg/console/subresource/configmap/doc_url.go new file mode 100644 index 000000000..8623befac --- /dev/null +++ b/pkg/console/subresource/configmap/doc_url.go @@ -0,0 +1,38 @@ +package configmap + +import ( + "fmt" + "strconv" + "strings" +) + +const ocpDocURLFormat = "https://access.redhat.com/documentation/en-us/openshift_container_platform/%s/" + +// formatOCPDocURL returns the OCP documentation base URL for the given version. +// Extracts major.minor from version (e.g., "5.0.3" → "5.0"). Falls back to +// "latest" when the version is empty or cannot be parsed. +func formatOCPDocURL(version string) string { + return fmt.Sprintf(ocpDocURLFormat, extractMajorMinor(version)) +} + +// extractMajorMinor extracts the major.minor portion from a version string. +// Returns "latest" if the version is empty, does not contain a dot separator, +// or if the major/minor components are not valid decimal numbers. +func extractMajorMinor(version string) string { + if version == "" { + return "latest" + } + parts := strings.SplitN(version, ".", 3) + if len(parts) < 2 { + return "latest" + } + major := parts[0] + minor := parts[1] + if _, err := strconv.Atoi(major); err != nil { + return "latest" + } + if _, err := strconv.Atoi(minor); err != nil { + return "latest" + } + return major + "." + minor +} diff --git a/pkg/console/subresource/configmap/doc_url_okd_test.go b/pkg/console/subresource/configmap/doc_url_okd_test.go new file mode 100644 index 000000000..3757e2549 --- /dev/null +++ b/pkg/console/subresource/configmap/doc_url_okd_test.go @@ -0,0 +1,36 @@ +//go:build !ocp +// +build !ocp + +package configmap + +import "testing" + +func TestDefaultDocURL(t *testing.T) { + // OKD build: DefaultDocURL always returns the static OKD docs URL, + // regardless of OPERATOR_IMAGE_VERSION. + // The OCP code path is covered by TestFormatOCPDocURL and + // TestExtractMajorMinor in doc_url_test.go. + const expectedOKDDocURL = "https://docs.okd.io/latest/" + + tests := []struct { + name string + operatorImageVersion string + }{ + {name: "returns expected OKD documentation URL"}, + { + name: "OPERATOR_IMAGE_VERSION does not affect OKD build", + operatorImageVersion: "5.0.3", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.operatorImageVersion != "" { + t.Setenv("OPERATOR_IMAGE_VERSION", tt.operatorImageVersion) + } + got := DefaultDocURL() + if got != expectedOKDDocURL { + t.Errorf("DefaultDocURL() = %q, want %q", got, expectedOKDDocURL) + } + }) + } +} diff --git a/pkg/console/subresource/configmap/doc_url_test.go b/pkg/console/subresource/configmap/doc_url_test.go new file mode 100644 index 000000000..dc75adfc1 --- /dev/null +++ b/pkg/console/subresource/configmap/doc_url_test.go @@ -0,0 +1,137 @@ +package configmap + +import "testing" + +func TestFormatOCPDocURL(t *testing.T) { + tests := []struct { + name string + version string + want string + }{ + { + name: "full release version", + version: "5.0.3", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/5.0/", + }, + { + name: "two-part version", + version: "4.21", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/4.21/", + }, + { + name: "nightly pre-release version", + version: "5.1.0-0.nightly-2026-01-01-000000", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/5.1/", + }, + { + name: "rc version", + version: "4.19.0-rc.1", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/4.19/", + }, + { + name: "empty version falls back to latest", + version: "", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/latest/", + }, + { + name: "single number without dot falls back to latest", + version: "5", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/latest/", + }, + { + name: "non-numeric dotted version falls back to latest", + version: "invalid.version", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/latest/", + }, + { + name: "trailing dot falls back to latest", + version: "5.", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/latest/", + }, + { + name: "non-numeric minor falls back to latest", + version: "5.x", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/latest/", + }, + { + name: "leading dot falls back to latest", + version: ".5", + want: "https://access.redhat.com/documentation/en-us/openshift_container_platform/latest/", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := formatOCPDocURL(tt.version) + if got != tt.want { + t.Errorf("formatOCPDocURL(%q) = %q, want %q", tt.version, got, tt.want) + } + }) + } +} + +func TestExtractMajorMinor(t *testing.T) { + tests := []struct { + name string + version string + want string + }{ + { + name: "standard three-part version", + version: "5.0.3", + want: "5.0", + }, + { + name: "two-part version", + version: "4.21", + want: "4.21", + }, + { + name: "nightly build version", + version: "5.1.0-0.nightly-2026-01-01-000000", + want: "5.1", + }, + { + name: "release candidate", + version: "4.19.0-rc.1", + want: "4.19", + }, + { + name: "empty string", + version: "", + want: "latest", + }, + { + name: "single number without dot", + version: "5", + want: "latest", + }, + { + name: "non-numeric dotted version", + version: "invalid.version", + want: "latest", + }, + { + name: "trailing dot with empty minor", + version: "5.", + want: "latest", + }, + { + name: "non-numeric minor component", + version: "5.x", + want: "latest", + }, + { + name: "leading dot with empty major", + version: ".5", + want: "latest", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := extractMajorMinor(tt.version) + if got != tt.want { + t.Errorf("extractMajorMinor(%q) = %q, want %q", tt.version, got, tt.want) + } + }) + } +}