Skip to content

Commit f175131

Browse files
committed
Automate OCP-42543: should not install resources annotated with release.openshift.io/delete=true
1 parent 95c76c7 commit f175131

5 files changed

Lines changed: 116 additions & 6 deletions

File tree

.openshift-tests-extension/openshift_payload_cluster-version-operator.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,19 @@
2828
"source": "openshift:payload:cluster-version-operator",
2929
"lifecycle": "blocking",
3030
"environmentSelector": {}
31+
},
32+
{
33+
"name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should not install resources annotated with release.openshift.io/delete=true",
34+
"labels": {
35+
"42543": {},
36+
"Conformance": {},
37+
"High": {}
38+
},
39+
"resources": {
40+
"isolation": {}
41+
},
42+
"source": "openshift:payload:cluster-version-operator",
43+
"lifecycle": "blocking",
44+
"environmentSelector": {}
3145
}
3246
]

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
golang.org/x/net v0.47.0
2626
golang.org/x/time v0.9.0
2727
gopkg.in/fsnotify.v1 v1.4.7
28+
gopkg.in/yaml.v3 v3.0.1
2829
k8s.io/api v0.34.1
2930
k8s.io/apiextensions-apiserver v0.34.1
3031
k8s.io/apimachinery v0.34.1
@@ -81,7 +82,6 @@ require (
8182
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
8283
gopkg.in/inf.v0 v0.9.1 // indirect
8384
gopkg.in/yaml.v2 v2.4.0 // indirect
84-
gopkg.in/yaml.v3 v3.0.1 // indirect
8585
k8s.io/apiserver v0.34.1 // indirect
8686
k8s.io/component-base v0.34.1 // indirect
8787
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect

test/cvo/cvo.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ package cvo
22

33
import (
44
"context"
5+
"fmt"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
"strings"
510

11+
yamlv3 "gopkg.in/yaml.v3"
612
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
713
"k8s.io/client-go/kubernetes"
814
"k8s.io/client-go/rest"
@@ -80,4 +86,79 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`,
8086
sccAnnotation := cvoPod.Annotations["openshift.io/scc"]
8187
o.Expect(sccAnnotation).To(o.Equal("hostaccess"), "Expected the annotation 'openshift.io/scc annotation' on pod %s to have the value 'hostaccess', but got %s", cvoPod.Name, sccAnnotation)
8288
})
89+
90+
g.It(`should not install resources annotated with release.openshift.io/delete=true`, g.Label("Conformance", "High", "42543"), func() {
91+
// Initialize the ocapi.OC instance
92+
g.By("Setup ocapi.OC")
93+
err := os.Setenv("OC_CLI_TIMEOUT", "90s")
94+
o.Expect(err).NotTo(o.HaveOccurred(), "Setup environment variable OC_CLI_TIMEOUT failed")
95+
ocClient, err := oc.NewOC(logger)
96+
o.Expect(err).NotTo(o.HaveOccurred())
97+
o.Expect(ocClient).NotTo(o.BeNil())
98+
defer func() {
99+
err = os.Unsetenv("OC_CLI_TIMEOUT")
100+
o.Expect(err).NotTo(o.HaveOccurred(), "Unset environment variable OC_CLI_TIMEOUT failed")
101+
}()
102+
103+
g.By("Extract manifests")
104+
annotation := "release.openshift.io/delete"
105+
manifestDir := ocapi.ReleaseExtractOptions{To: "/tmp/OTA-42543-manifest"}
106+
logger.Info(fmt.Sprintf("Extract manifests to: %s", manifestDir.To))
107+
defer func() { _ = os.RemoveAll(manifestDir.To) }()
108+
err = ocClient.AdmReleaseExtract(manifestDir)
109+
o.Expect(err).NotTo(o.HaveOccurred(), "extract manifests failed")
110+
111+
entries, err := os.ReadDir(manifestDir.To)
112+
o.Expect(err).NotTo(o.HaveOccurred())
113+
g.By("Start to iterate all manifests")
114+
var closeFilePass = true
115+
for _, entry := range entries {
116+
nameLower := strings.ToLower(entry.Name())
117+
if strings.Contains(nameLower, "cleanup") {
118+
logger.Info(fmt.Sprintf("Skipping file %s because it matches cleanup filter", entry.Name()))
119+
continue
120+
}
121+
filePath := filepath.Join(manifestDir.To, entry.Name())
122+
file, err := os.Open(filePath)
123+
o.Expect(err).NotTo(o.HaveOccurred())
124+
defer func() {
125+
if !closeFilePass {
126+
// Close the file again
127+
if err = file.Close(); err != nil {
128+
o.Expect(err).NotTo(o.HaveOccurred(), "close file failed")
129+
}
130+
}
131+
}()
132+
decoder := yamlv3.NewDecoder(file)
133+
for {
134+
var doc map[string]interface{}
135+
if err := decoder.Decode(&doc); err != nil {
136+
if err == io.EOF {
137+
break
138+
}
139+
continue
140+
}
141+
meta, _ := doc["metadata"].(map[string]interface{})
142+
ann, _ := meta["annotations"].(map[string]interface{})
143+
if ann == nil || ann[annotation] != "true" {
144+
continue
145+
}
146+
kind, _ := doc["kind"].(string)
147+
name, _ := meta["name"].(string)
148+
namespace, _ := meta["namespace"].(string)
149+
args := []string{"get", kind, name}
150+
if namespace != "" {
151+
args = append(args, "-n", namespace)
152+
}
153+
_, err := ocClient.Run(args...)
154+
o.Expect(err).To(o.HaveOccurred(), "The deleted manifest should not be installed, but actually installed")
155+
}
156+
// close each file
157+
err = file.Close()
158+
if err != nil {
159+
closeFilePass = false
160+
o.Expect(err).NotTo(o.HaveOccurred(), "close file failed")
161+
}
162+
}
163+
})
83164
})

test/oc/api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ type VersionOptions struct {
1111
type OC interface {
1212
AdmReleaseExtract(o ReleaseExtractOptions) error
1313
Version(o VersionOptions) (string, error)
14+
Run(args ...string) ([]byte, error)
1415
}

test/oc/cli/cli.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type client struct {
2020
}
2121

2222
type executor interface {
23-
Run(args ...string) ([]byte, error)
23+
run(args ...string) ([]byte, error)
2424
}
2525

2626
type ocExecutor struct {
@@ -32,7 +32,7 @@ type ocExecutor struct {
3232
execute func(dir, command string, args ...string) ([]byte, error)
3333
}
3434

35-
func (e *ocExecutor) Run(args ...string) ([]byte, error) {
35+
func (e *ocExecutor) run(args ...string) ([]byte, error) {
3636
logger := e.logger.WithValues("cmd", e.oc, "args", strings.Join(args, " "))
3737
b, err := e.execute("", e.oc, args...)
3838
if err != nil {
@@ -70,12 +70,13 @@ func NewOCCli(logger logr.Logger) (api.OC, error) {
7070
timeout := 30 * time.Second
7171
timeoutStr := os.Getenv("OC_CLI_TIMEOUT")
7272
if timeoutStr != "" {
73+
logger.Info(fmt.Sprintf("will use the environment timeout variable to run command: %s", timeoutStr))
7374
timeout, err = time.ParseDuration(timeoutStr)
7475
if err != nil {
7576
return nil, err
7677
}
7778
}
78-
79+
logger.Info(fmt.Sprintf("timeout is: %s", timeout))
7980
executor, err := newOCExecutor(oc, timeout, logger)
8081
if err != nil {
8182
return nil, err
@@ -88,8 +89,17 @@ func NewOCCli(logger logr.Logger) (api.OC, error) {
8889
}
8990

9091
func (c *client) AdmReleaseExtract(o api.ReleaseExtractOptions) error {
92+
_, err := os.Stat(o.To)
93+
if errors.Is(err, os.ErrNotExist) {
94+
c.logger.Info(fmt.Sprintf("the output directory does not exist, will create it: %s", o.To))
95+
if err = os.Mkdir(o.To, 0755); err != nil {
96+
err = fmt.Errorf("failed to create directory: %v", err)
97+
return err
98+
}
99+
c.logger.Info(fmt.Sprintf("the output directory has been created: %s", o.To))
100+
}
91101
args := []string{"adm", "release", "extract", fmt.Sprintf("--to=%s", o.To)}
92-
_, err := c.executor.Run(args...)
102+
_, err = c.executor.run(args...)
93103
if err != nil {
94104
return err
95105
}
@@ -98,9 +108,13 @@ func (c *client) AdmReleaseExtract(o api.ReleaseExtractOptions) error {
98108

99109
func (c *client) Version(o api.VersionOptions) (string, error) {
100110
args := []string{"version", fmt.Sprintf("--client=%t", o.Client)}
101-
output, err := c.executor.Run(args...)
111+
output, err := c.executor.run(args...)
102112
if err != nil {
103113
return "", err
104114
}
105115
return string(output), nil
106116
}
117+
118+
func (c *client) Run(args ...string) ([]byte, error) {
119+
return c.executor.run(args...)
120+
}

0 commit comments

Comments
 (0)