Skip to content

Commit 20cf71d

Browse files
author
Per Goncalves da Silva
committed
Add unintall feature test
Signed-off-by: Per Goncalves da Silva <pegoncal@redhat.com>
1 parent 8167ff8 commit 20cf71d

File tree

2 files changed

+96
-14
lines changed

2 files changed

+96
-14
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
Feature: Uninstall ClusterExtension
2+
3+
As an OLM user I would like to uninstall a cluster extension.
4+
5+
Background:
6+
Given OLM is available
7+
And ClusterCatalog "test" serves bundles
8+
And ServiceAccount "olm-sa" with needed permissions is available in ${TEST_NAMESPACE}
9+
And ClusterExtension is applied
10+
"""
11+
apiVersion: olm.operatorframework.io/v1
12+
kind: ClusterExtension
13+
metadata:
14+
name: ${NAME}
15+
spec:
16+
namespace: ${TEST_NAMESPACE}
17+
serviceAccount:
18+
name: olm-sa
19+
source:
20+
sourceType: Catalog
21+
catalog:
22+
packageName: test
23+
selector:
24+
matchLabels:
25+
"olm.operatorframework.io/metadata.name": test-catalog
26+
"""
27+
And bundle "test-operator.1.2.0" is installed in version "1.2.0"
28+
# Ensure the bundle resources exist before initiating the deletion and process and checking
29+
# that they no longer exist to avoid false positives (i.e. if they never existed, checking that they don't exist
30+
# will succeed)
31+
And resource "networkpolicy/test-operator-network-policy" exists
32+
And resource "configmap/test-configmap" exists
33+
And resource "deployment/test-operator" exists
34+
# Note: The names of these resources are derived from the permissions contained in the clusterroles
35+
# If those permissions change due to changed in the bundle, the names of these resources will also change
36+
# causing a failure here
37+
And resource "clusterrole/testoperator.v1.2.-37mym6pni2xxmai9n7fmhtbn9i348lx7o619rmf3ypio" exists
38+
And resource "clusterrole/testoperator.v1.2.0-t88i5epjh8oxp4klplhjyrsekwcp92b27w03ayr1ku5" exists
39+
And resource "clusterrolebinding/testoperator.v1.2.-37mym6pni2xxmai9n7fmhtbn9i348lx7o619rmf3ypio" exists
40+
And resource "clusterrolebinding/testoperator.v1.2.0-t88i5epjh8oxp4klplhjyrsekwcp92b27w03ayr1ku5" exists
41+
42+
Scenario: Uninstall ClusterExtension
43+
When resource "clusterextension/${NAME}" is removed
44+
Then resource "networkpolicy/test-operator-network-policy" is eventually not found
45+
And resource "configmap/test-configmap" is eventually not found
46+
And resource "deployment/test-operator" is eventually not found
47+
And resource "clusterrole/testoperator.v1.2.-37mym6pni2xxmai9n7fmhtbn9i348lx7o619rmf3ypio" is eventually not found
48+
And resource "clusterrole/testoperator.v1.2.0-t88i5epjh8oxp4klplhjyrsekwcp92b27w03ayr1ku5" is eventually not found
49+
And resource "clusterrolebinding/testoperator.v1.2.-37mym6pni2xxmai9n7fmhtbn9i348lx7o619rmf3ypio" is eventually not found
50+
And resource "clusterrolebinding/testoperator.v1.2.0-t88i5epjh8oxp4klplhjyrsekwcp92b27w03ayr1ku5" is eventually not found
51+
52+
Scenario: Uninstall ClusterExtension when ServiceAccount has already been deleted
53+
When resource "serviceaccount/olm-sa" is removed
54+
# Ensure service account is gone before checking to ensure resources are cleaned up whether the service account
55+
# and its permissions are present on the cluster or not
56+
And resource "serviceaccount/olm-sa" is eventually not found
57+
And resource "clusterextension/${NAME}" is removed
58+
Then resource "networkpolicy/test-operator-network-policy" is eventually not found
59+
And resource "configmap/test-configmap" is eventually not found
60+
And resource "deployment/test-operator" is eventually not found
61+
And resource "clusterrole/testoperator.v1.2.-37mym6pni2xxmai9n7fmhtbn9i348lx7o619rmf3ypio" is eventually not found
62+
And resource "clusterrole/testoperator.v1.2.0-t88i5epjh8oxp4klplhjyrsekwcp92b27w03ayr1ku5" is eventually not found
63+
And resource "clusterrolebinding/testoperator.v1.2.-37mym6pni2xxmai9n7fmhtbn9i348lx7o619rmf3ypio" is eventually not found
64+
And resource "clusterrolebinding/testoperator.v1.2.0-t88i5epjh8oxp4klplhjyrsekwcp92b27w03ayr1ku5" is eventually not found

test/e2e/steps/steps.go

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func RegisterSteps(sc *godog.ScenarioContext) {
6868
sc.Step(`^(?i)resource "([^"]+)" is installed$`, ResourceAvailable)
6969
sc.Step(`^(?i)resource "([^"]+)" is available$`, ResourceAvailable)
7070
sc.Step(`^(?i)resource "([^"]+)" is removed$`, ResourceRemoved)
71+
sc.Step(`^(?i)resource "([^"]+)" is eventually not found$`, ResourceEventuallyNotFound)
7172
sc.Step(`^(?i)resource "([^"]+)" exists$`, ResourceAvailable)
7273
sc.Step(`^(?i)resource is applied$`, ResourceIsApplied)
7374
sc.Step(`^(?i)resource "deployment/test-operator" reports as (not ready|ready)$`, MarkTestOperatorNotReady)
@@ -398,24 +399,25 @@ func ClusterExtensionRevisionIsArchived(ctx context.Context, revisionName string
398399
func ResourceAvailable(ctx context.Context, resource string) error {
399400
sc := scenarioCtx(ctx)
400401
resource = substituteScenarioVars(resource, sc)
401-
rtype, name, found := strings.Cut(resource, "/")
402+
kind, name, found := strings.Cut(resource, "/")
402403
if !found {
403-
return fmt.Errorf("resource %s is not in the format <type>/<name>", resource)
404+
return fmt.Errorf("resource %s is not in the format <kind>/<name>", resource)
404405
}
405406
waitFor(ctx, func() bool {
406-
_, err := k8sClient("get", rtype, name, "-n", sc.namespace)
407+
_, err := k8sClient("get", kind, name, "-n", sc.namespace)
407408
return err == nil
408409
})
409410
return nil
410411
}
411412

412413
func ResourceRemoved(ctx context.Context, resource string) error {
413414
sc := scenarioCtx(ctx)
414-
rtype, name, found := strings.Cut(resource, "/")
415+
resource = substituteScenarioVars(resource, sc)
416+
kind, name, found := strings.Cut(resource, "/")
415417
if !found {
416-
return fmt.Errorf("resource %s is not in the format <type>/<name>", resource)
418+
return fmt.Errorf("resource %s is not in the format <kind>/<name>", resource)
417419
}
418-
yaml, err := k8sClient("get", rtype, name, "-n", sc.namespace, "-o", "yaml")
420+
yaml, err := k8sClient("get", kind, name, "-n", sc.namespace, "-o", "yaml")
419421
if err != nil {
420422
return err
421423
}
@@ -424,23 +426,38 @@ func ResourceRemoved(ctx context.Context, resource string) error {
424426
return err
425427
}
426428
sc.removedResources = append(sc.removedResources, *obj)
427-
_, err = k8sClient("delete", rtype, name, "-n", sc.namespace)
429+
_, err = k8sClient("delete", kind, name, "-n", sc.namespace)
428430
return err
429431
}
430432

433+
func ResourceEventuallyNotFound(ctx context.Context, resource string) error {
434+
sc := scenarioCtx(ctx)
435+
resource = substituteScenarioVars(resource, sc)
436+
kind, name, found := strings.Cut(resource, "/")
437+
if !found {
438+
return fmt.Errorf("resource %s is not in the format <kind>/<name>", resource)
439+
}
440+
441+
require.Eventually(godog.T(ctx), func() bool {
442+
obj, err := k8sClient("get", kind, name, "-n", sc.namespace, "--ignore-not-found", "-o", "yaml")
443+
return err == nil && obj == ""
444+
}, timeout, tick)
445+
return nil
446+
}
447+
431448
func ResourceMatches(ctx context.Context, resource string, requiredContentTemplate *godog.DocString) error {
432449
sc := scenarioCtx(ctx)
433450
resource = substituteScenarioVars(resource, sc)
434-
rtype, name, found := strings.Cut(resource, "/")
451+
kind, name, found := strings.Cut(resource, "/")
435452
if !found {
436-
return fmt.Errorf("resource %s is not in the format <type>/<name>", resource)
453+
return fmt.Errorf("resource %s is not in the format <kind>/<name>", resource)
437454
}
438455
requiredContent, err := toUnstructured(substituteScenarioVars(requiredContentTemplate.Content, sc))
439456
if err != nil {
440457
return fmt.Errorf("failed to parse required resource yaml: %v", err)
441458
}
442459
waitFor(ctx, func() bool {
443-
objJson, err := k8sClient("get", rtype, name, "-n", sc.namespace, "-o", "json")
460+
objJson, err := k8sClient("get", kind, name, "-n", sc.namespace, "-o", "json")
444461
if err != nil {
445462
return false
446463
}
@@ -468,12 +485,13 @@ func ResourceMatches(ctx context.Context, resource string, requiredContentTempla
468485

469486
func ResourceRestored(ctx context.Context, resource string) error {
470487
sc := scenarioCtx(ctx)
471-
rtype, name, found := strings.Cut(resource, "/")
488+
resource = substituteScenarioVars(resource, sc)
489+
kind, name, found := strings.Cut(resource, "/")
472490
if !found {
473-
return fmt.Errorf("resource %s is not in the format <type>/<name>", resource)
491+
return fmt.Errorf("resource %s is not in the format <kind>/<name>", resource)
474492
}
475493
waitFor(ctx, func() bool {
476-
yaml, err := k8sClient("get", rtype, name, "-n", sc.namespace, "-o", "yaml")
494+
yaml, err := k8sClient("get", kind, name, "-n", sc.namespace, "-o", "yaml")
477495
if err != nil {
478496
return false
479497
}
@@ -486,7 +504,7 @@ func ResourceRestored(ctx context.Context, resource string) error {
486504
for i, removed := range sc.removedResources {
487505
rct := removed.GetCreationTimestamp()
488506
if removed.GetName() == obj.GetName() && removed.GetKind() == obj.GetKind() && rct.Before(&ct) {
489-
switch rtype {
507+
switch kind {
490508
case "configmap":
491509
if !reflect.DeepEqual(removed.Object["data"], obj.Object["data"]) {
492510
return false

0 commit comments

Comments
 (0)