@@ -72,26 +72,46 @@ func (d *Deployer) deployOperatorViaOLM(ctx context.Context) error {
7272 return nil
7373}
7474
75- // checkOLMInstalled checks if OLM is installed in the cluster.
75+ // checkOLMInstalled checks if OLM is installed in the cluster by verifying
76+ // the API server is ready to serve the required OLM resource types.
7677func (d * Deployer ) checkOLMInstalled (ctx context.Context ) error {
77- // Check for OLM CRDs
78- requiredCRDs := []string {
78+ requiredResources := []string {
7979 "catalogsources.operators.coreos.com" ,
8080 "subscriptions.operators.coreos.com" ,
8181 "installplans.operators.coreos.com" ,
8282 "clusterserviceversions.operators.coreos.com" ,
8383 }
8484
85- for _ , crd := range requiredCRDs {
86- // TODO(ROX-34499): actually this is not the right way to check whether it's safe to create a resource of a given kind.
87- // A CRD can be present, but still being loaded or end up not accepted by the API server.
88- // Instead we should use the `kubectl api-resources` subcommand which exposes the status we're looking for.
89- _ , err := d .runKubectl (ctx , k8s.KubectlOptions {
90- Args : []string {"get" , "crd" , crd },
91- })
92- if err != nil {
93- return fmt .Errorf ("OLM not installed: CRD %s not found. Please install OLM first" , crd )
85+ result , err := d .runKubectl (ctx , k8s.KubectlOptions {
86+ Args : []string {"api-resources" , "--api-group=operators.coreos.com" , "-o" , "name" },
87+ })
88+ if err != nil {
89+ if result .Stderr != "" {
90+ d .logger .Error ("kubectl stderr:" )
91+ for stderrLine := range strings .SplitSeq (result .Stderr , "\n " ) {
92+ d .logger .Errorf ("stderr: %s" , stderrLine )
93+ }
94+ }
95+ return fmt .Errorf ("failed to query api-group operators.coreos.com: %w" , err )
96+ }
97+
98+ available := make (map [string ]bool )
99+ for line := range strings .SplitSeq (strings .TrimSpace (result .Stdout ), "\n " ) {
100+ name := strings .TrimSpace (line )
101+ available [name ] = true
102+ }
103+
104+ var missingResources []string
105+ for _ , resource := range requiredResources {
106+ if ! available [resource ] {
107+ missingResources = append (missingResources , resource )
108+ }
109+ }
110+ if len (missingResources ) > 0 {
111+ for _ , resource := range missingResources {
112+ d .logger .Errorf ("OLM resource not served by the API server: %s" , resource )
94113 }
114+ return fmt .Errorf ("OLM is not properly installed, %d required resource(s) missing" , len (missingResources ))
95115 }
96116
97117 d .logger .Success ("✓ OLM detected in cluster" )
@@ -335,35 +355,34 @@ func (d *Deployer) waitForCSVSuccess(ctx context.Context) error {
335355
336356// detectOperatorDeploymentMode detects how the operator is currently deployed.
337357// Returns (operatorExists bool, isOLM OperatorDeploymentMode)
338- func (d * Deployer ) detectOperatorDeploymentMode (ctx context.Context ) (bool , OperatorDeploymentMode ) {
358+ func (d * Deployer ) detectOperatorDeploymentMode (ctx context.Context ) (bool , OperatorDeploymentMode , error ) {
359+ const olmOwnerLabel = "olm.owner"
360+
339361 // First, check if a Subscription exists (OLM-specific resource)
340362 _ , err := d .runKubectl (ctx , k8s.KubectlOptions {
341363 Args : []string {"get" , "subscription" , subscriptionName , "-n" , operatorNamespace },
342364 })
343365 if err == nil {
344- return true , OperatorModeOLM
366+ return true , OperatorModeOLM , nil
345367 }
346368
347- // If no subscription, check if operator deployment exists.
348- _ , err = d .runKubectl (ctx , k8s.KubectlOptions {
349- Args : []string {"get" , "deployment" , operatorDeploymentName , "-n" , operatorNamespace },
350- })
351- if err == nil {
352- // Deployment exists - check if it has OLM owner labels.
353- result , err := d .runKubectl (ctx , k8s.KubectlOptions {
354- Args : []string {"get" , "deployment" , operatorDeploymentName , "-n" , operatorNamespace , "-o" , "jsonpath={.metadata.labels}" },
355- })
356- // TODO(ROX-34499): This is not very robust. Better retrieve a specific label in the `get`
357- // command?
358- if err == nil && strings .Contains (result .Stdout , "olm.owner" ) {
359- return true , OperatorModeOLM
360- }
361- // Deployment exists without OLM labels = non-OLM deployment.
362- return true , OperatorModeNonOLM
369+ // If no subscription, check if operator deployment exists/if it has the expected OLM label.
370+ labelValue , err := k8s .RetrieveClusterResourceLabel (ctx , d .logger , operatorNamespace , "deployment" , operatorDeploymentName , olmOwnerLabel )
371+ if k8s .IsResourceNotFound (err ) {
372+ // No operator deployment found.
373+ return false , OperatorModeNonOLM , nil
374+ }
375+ if err != nil {
376+ return false , OperatorModeNonOLM , err
377+ }
378+
379+ if labelValue == "" {
380+ // Deployment exists without OLM labels -> non-OLM deployment.
381+ return true , OperatorModeNonOLM , nil
363382 }
364383
365- // No operator found .
366- return false , OperatorModeNonOLM
384+ // Label set -> OLM deployment .
385+ return true , OperatorModeOLM , nil
367386}
368387
369388// teardownOperatorOLM removes the operator when installed via OLM.
0 commit comments