diff --git a/cmd/main.go b/cmd/main.go index 1dc0143a8e..6c721986ba 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -299,9 +299,16 @@ func main() { } //+kubebuilder:scaffold:builder - // Add CLI download setup runnable - // Only add if watchNamespace is set (skip for cluster-scoped mode) - if watchNamespace != "" { + // Add CLI/VMDP download setup runnables. + // Skip when namespace-scoped mode is off or the ConsoleCLIDownload CRD + // is absent (clusters without Console capability, e.g. SNO). + if watchNamespace == "" { + setupLog.Info("Skipping CLI and VMDP download setup - watchNamespace not set") + } else if available, err := controller.IsConsoleCRDAvailable(mgr.GetRESTMapper(), setupLog); !available { + if err != nil { + setupLog.Error(err, "unable to check ConsoleCLIDownload CRD availability, skipping CLI/VMDP download setup") + } + } else { if err := mgr.Add(&controller.CLIDownloadSetup{ Client: mgr.GetClient(), Namespace: watchNamespace, @@ -320,8 +327,6 @@ func main() { setupLog.Error(err, "unable to add VMDP download setup") os.Exit(1) } - } else { - setupLog.Info("Skipping CLI and VMDP download setup - watchNamespace not set") } if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/internal/controller/console.go b/internal/controller/console.go new file mode 100644 index 0000000000..0eb98625ef --- /dev/null +++ b/internal/controller/console.go @@ -0,0 +1,27 @@ +package controller + +import ( + "fmt" + + "github.com/go-logr/logr" + consolev1 "github.com/openshift/api/console/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// IsConsoleCRDAvailable checks whether the ConsoleCLIDownload CRD is registered +// with the API server. Returns false when the Console capability is not enabled +// (e.g. SNO clusters). +func IsConsoleCRDAvailable(mapper meta.RESTMapper, log logr.Logger) (bool, error) { + _, err := mapper.RESTMapping( + schema.GroupKind{Group: consolev1.GroupVersion.Group, Kind: "ConsoleCLIDownload"}, + ) + if err != nil { + if meta.IsNoMatchError(err) { + log.Info("ConsoleCLIDownload CRD not available (Console capability not enabled), skipping setup") + return false, nil + } + return false, fmt.Errorf("failed to check ConsoleCLIDownload CRD availability: %w", err) + } + return true, nil +} diff --git a/internal/controller/console_test.go b/internal/controller/console_test.go new file mode 100644 index 0000000000..7f097cd281 --- /dev/null +++ b/internal/controller/console_test.go @@ -0,0 +1,50 @@ +package controller + +import ( + "testing" + + consolev1 "github.com/openshift/api/console/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" + ctrl "sigs.k8s.io/controller-runtime" +) + +func TestIsConsoleCRDAvailable(t *testing.T) { + log := ctrl.Log.WithName("test") + + tests := []struct { + name string + includeConsoleCRD bool + wantAvailable bool + }{ + { + name: "returns false when ConsoleCLIDownload CRD is not registered", + includeConsoleCRD: false, + wantAvailable: false, + }, + { + name: "returns true when ConsoleCLIDownload CRD is registered", + includeConsoleCRD: true, + wantAvailable: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var groupVersions []schema.GroupVersion + if tt.includeConsoleCRD { + groupVersions = append(groupVersions, consolev1.GroupVersion) + } + mapper := meta.NewDefaultRESTMapper(groupVersions) + if tt.includeConsoleCRD { + mapper.Add(consolev1.GroupVersion.WithKind("ConsoleCLIDownload"), meta.RESTScopeRoot) + } + + available, err := IsConsoleCRDAvailable(mapper, log) + require.NoError(t, err) + assert.Equal(t, tt.wantAvailable, available) + }) + } +}