Skip to content

Commit 8ea9560

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 8ea9560

File tree

6 files changed

+151
-1
lines changed

6 files changed

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

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)