@@ -186,7 +186,9 @@ func RegisterSteps(sc *godog.ScenarioContext) {
186186 sc .Step (`^(?i)ValidatingAdmissionPolicy "([^"]+)" is active$` , ValidatingAdmissionPolicyIsActive )
187187
188188 sc .Step (`^(?i)namespace "([^"]+)" has labels$` , NamespaceHasLabels )
189+ sc .Step (`^(?i)namespace "([^"]+)" is managed by OLM$` , NamespaceIsManagedByOLM )
189190 sc .Step (`^(?i)namespace "([^"]+)" does not have label "([^"]+)"$` , NamespaceDoesNotHaveLabel )
191+ sc .Step (`^(?i)namespace "([^"]+)" has no owner references$` , NamespaceHasNoOwnerReferences )
190192
191193 sc .Step (`^(?i)operator "([^"]+)" target namespace is "([^"]+)"$` , OperatorTargetNamespace )
192194 sc .Step (`^(?i)Prometheus metrics are returned in the response$` , PrometheusMetricsAreReturned )
@@ -2513,6 +2515,56 @@ func NamespaceDoesNotHaveLabel(ctx context.Context, nsName string, labelKey stri
25132515 return nil
25142516}
25152517
2518+ // NamespaceIsManagedByOLM waits for a namespace to be owned by the extension's active ClusterObjectSet.
2519+ func NamespaceIsManagedByOLM (ctx context.Context , nsName string ) error {
2520+ sc := scenarioCtx (ctx )
2521+ nsName = substituteScenarioVars (nsName , sc )
2522+
2523+ waitFor (ctx , func () bool {
2524+ revision , err := latestActiveRevisionForExtension (sc .clusterExtensionName )
2525+ if err != nil {
2526+ logger .V (1 ).Error (err , "failed to get active revision" , "extension" , sc .clusterExtensionName )
2527+ return false
2528+ }
2529+ out , err := k8sClient (ctx , "get" , "namespace" , nsName , "-o" , "json" )
2530+ if err != nil {
2531+ return false
2532+ }
2533+ var ns corev1.Namespace
2534+ if err := json .Unmarshal ([]byte (out ), & ns ); err != nil {
2535+ return false
2536+ }
2537+ owner := metav1 .GetControllerOf (& ns )
2538+ if owner == nil || owner .APIVersion != ocv1 .GroupVersion .String () ||
2539+ owner .Kind != ocv1 .ClusterObjectSetKind || owner .Name != revision .Name || owner .UID != revision .UID {
2540+ logger .V (1 ).Info ("Namespace is not yet owned by the extension's active revision" ,
2541+ "namespace" , nsName , "revision" , revision .Name , "owner" , owner )
2542+ return false
2543+ }
2544+ return true
2545+ })
2546+ return nil
2547+ }
2548+
2549+ // NamespaceHasNoOwnerReferences verifies a namespace remains unmanaged after installation.
2550+ func NamespaceHasNoOwnerReferences (ctx context.Context , nsName string ) error {
2551+ sc := scenarioCtx (ctx )
2552+ nsName = substituteScenarioVars (nsName , sc )
2553+
2554+ out , err := k8sClient (ctx , "get" , "namespace" , nsName , "-o" , "json" )
2555+ if err != nil {
2556+ return fmt .Errorf ("failed to get namespace %q: %w" , nsName , err )
2557+ }
2558+ var obj unstructured.Unstructured
2559+ if err := json .Unmarshal ([]byte (out ), & obj ); err != nil {
2560+ return fmt .Errorf ("failed to unmarshal namespace: %w" , err )
2561+ }
2562+ if refs := obj .GetOwnerReferences (); len (refs ) != 0 {
2563+ return fmt .Errorf ("namespace %q has unexpected owner references: %v" , nsName , refs )
2564+ }
2565+ return nil
2566+ }
2567+
25162568// nestedString traverses a nested map[string]interface{} by the given keys
25172569// and returns the leaf value as a string.
25182570func nestedString (obj map [string ]interface {}, keys ... string ) (string , bool ) {
0 commit comments