Skip to content

Commit db03355

Browse files
committed
Introduce oc-cli for e2e tests
Most of e2e tests for CVO use `oc` cmds that are not trivial to implement or import. See the introduced `oc/api/api.go`. This pull introduces the implementation by spawning processes to execute `oc` cmd. The advantages are: - We are ready to add more e2e tests that need `oc` cmds. - We do not need to vendor any new pkgs into CVO, e.g., `k8s.io/kubernetes/test/e2e/framework` which might give us headaches when bumping its version. - The implementation can be easily replaced when needed in the future with a better one without oc-cli because the `test/oc/cli` pkg stays only in the `test/oc` pkg, and the tests in a pkg such as `/test/cvo` does not know the existence of `cli`.
1 parent 60cff2f commit db03355

File tree

6 files changed

+160
-1
lines changed

6 files changed

+160
-1
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,15 @@
88
"source": "openshift:payload:cluster-version-operator",
99
"lifecycle": "blocking",
1010
"environmentSelector": {}
11+
},
12+
{
13+
"name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator-tests can use oc to get the client version",
14+
"labels": {},
15+
"resources": {
16+
"isolation": {}
17+
},
18+
"source": "openshift:payload:cluster-version-operator",
19+
"lifecycle": "blocking",
20+
"environmentSelector": {}
1121
}
1222
]

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.24.0
55
require (
66
github.com/blang/semver/v4 v4.0.0
77
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
8+
github.com/go-logr/logr v1.4.2
89
github.com/google/go-cmp v0.7.0
910
github.com/google/uuid v1.6.0
1011
github.com/onsi/ginkgo/v2 v2.21.0
@@ -40,7 +41,6 @@ require (
4041
github.com/cespare/xxhash/v2 v2.3.0 // indirect
4142
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
4243
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
43-
github.com/go-logr/logr v1.4.2 // indirect
4444
github.com/go-openapi/jsonpointer v0.21.0 // indirect
4545
github.com/go-openapi/jsonreference v0.20.2 // indirect
4646
github.com/go-openapi/swag v0.23.0 // indirect

test/cvo/cvo.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,25 @@ package cvo
33
import (
44
. "github.com/onsi/ginkgo/v2"
55
. "github.com/onsi/gomega"
6+
7+
"github.com/openshift/cluster-version-operator/test/oc"
8+
ocapi "github.com/openshift/cluster-version-operator/test/oc/api"
69
)
710

11+
var logger = GinkgoLogr.WithName("cluster-version-operator-tests")
12+
813
var _ = Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator-tests`, func() {
914
It("should support passing tests", func() {
1015
Expect(true).To(BeTrue())
1116
})
17+
18+
It("can use oc to get the version information", func() {
19+
ocClient, err := oc.NewOC(logger)
20+
Expect(err).NotTo(HaveOccurred())
21+
Expect(ocClient).NotTo(BeNil())
22+
23+
output, err := ocClient.Version(ocapi.VersionOptions{Client: true})
24+
Expect(err).NotTo(HaveOccurred())
25+
Expect(output).To(ContainSubstring("Client Version:"))
26+
})
1227
})

test/oc/api/api.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package api
2+
3+
type ReleaseExtractOptions struct {
4+
To string
5+
}
6+
7+
type VersionOptions struct {
8+
Client bool
9+
}
10+
11+
type OC interface {
12+
AdmReleaseExtract(o ReleaseExtractOptions) error
13+
Version(o VersionOptions) (string, error)
14+
}

test/oc/cli/cli.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"os/exec"
9+
"strings"
10+
"time"
11+
12+
"github.com/go-logr/logr"
13+
14+
"github.com/openshift/cluster-version-operator/test/oc/api"
15+
)
16+
17+
type client struct {
18+
logger logr.Logger
19+
executor executor
20+
}
21+
22+
type executor interface {
23+
Run(args ...string) ([]byte, error)
24+
}
25+
26+
type ocExecutor struct {
27+
// logger is used to log oc commands
28+
logger logr.Logger
29+
// oc is the path to the oc binary
30+
oc string
31+
// execute executes a command
32+
execute func(dir, command string, args ...string) ([]byte, error)
33+
}
34+
35+
func (e *ocExecutor) Run(args ...string) ([]byte, error) {
36+
logger := e.logger.WithValues("cmd", e.oc, "args", strings.Join(args, " "))
37+
b, err := e.execute("", e.oc, args...)
38+
if err != nil {
39+
logger.Error(err, "Running command failed", "output", string(b))
40+
} else {
41+
logger.Info("Running command succeeded.")
42+
}
43+
return b, err
44+
}
45+
46+
func newOCExecutor(oc string, timeout time.Duration, logger logr.Logger) (executor, error) {
47+
return &ocExecutor{
48+
logger: logger,
49+
oc: oc,
50+
execute: func(dir, command string, args ...string) ([]byte, error) {
51+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
52+
defer cancel()
53+
c := exec.CommandContext(ctx, command, args...)
54+
c.Dir = dir
55+
o, err := c.CombinedOutput()
56+
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
57+
return o, fmt.Errorf("execution timed out after %s: %w", timeout.String(), ctx.Err())
58+
}
59+
60+
return o, err
61+
},
62+
}, nil
63+
}
64+
65+
// NewOCCli return a client for oc-cli.
66+
func NewOCCli(logger logr.Logger) (api.OC, error) {
67+
oc, err := exec.LookPath("oc")
68+
if err != nil {
69+
return nil, err
70+
}
71+
timeout := 30 * time.Second
72+
timeoutStr := os.Getenv("OC_CLI_TIMEOUT")
73+
if timeoutStr != "" {
74+
timeout, err = time.ParseDuration(timeoutStr)
75+
if err != nil {
76+
return nil, err
77+
}
78+
}
79+
80+
executor, err := newOCExecutor(oc, timeout, logger)
81+
if err != nil {
82+
return nil, err
83+
}
84+
ret := client{
85+
logger: logger,
86+
executor: executor,
87+
}
88+
return &ret, nil
89+
}
90+
91+
func (c *client) AdmReleaseExtract(o api.ReleaseExtractOptions) error {
92+
args := []string{"adm", "release", "extract", fmt.Sprintf("--to=%s", o.To)}
93+
_, err := c.executor.Run(args...)
94+
if err != nil {
95+
return err
96+
}
97+
return nil
98+
}
99+
100+
func (c *client) Version(o api.VersionOptions) (string, error) {
101+
args := []string{"version", fmt.Sprintf("--client=%t", o.Client)}
102+
output, err := c.executor.Run(args...)
103+
if err != nil {
104+
return "", err
105+
}
106+
return string(output), nil
107+
}

test/oc/oc.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package oc
2+
3+
import (
4+
"github.com/go-logr/logr"
5+
6+
"github.com/openshift/cluster-version-operator/test/oc/api"
7+
"github.com/openshift/cluster-version-operator/test/oc/cli"
8+
)
9+
10+
// NewOC returns OC that provides utility functions used by the e2e tests
11+
func NewOC(logger logr.Logger) (api.OC, error) {
12+
return cli.NewOCCli(logger)
13+
}

0 commit comments

Comments
 (0)