Skip to content

Commit 0417591

Browse files
Jakub Sliacanopenshift-merge-robot
authored andcommitted
e2e: use podman-remote in story_openshift scenarios
Involves moving podman command code (test side) from test/integration to test/extended. Now it can be shared between e2e and integration tests. This only affects `test/integration/podman_test.go` on the integration side. Former feature file tags @story_registry, @story_health, and @story_marketplace now tag respective scenarios inside @story_openshift scope.
1 parent e106cbe commit 0417591

File tree

6 files changed

+261
-176
lines changed

6 files changed

+261
-176
lines changed

test/e2e/features/story_openshift.feature

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
@story_openshift
2-
Feature: 3 Openshift stories
2+
Feature: 4 Openshift stories
33

44
Background:
55
Given ensuring CRC cluster is running succeeds
66
And ensuring user is logged in succeeds
77

88
# End-to-end health check
99

10-
@darwin @linux @windows @startstop @testdata
10+
@darwin @linux @windows @startstop @testdata @story_health
1111
Scenario: Overall cluster health
1212
#Given ensuring CRC cluster is running succeeds
1313
#And ensuring user is logged in succeeds
@@ -38,8 +38,8 @@ Feature: 3 Openshift stories
3838

3939
# Local image to image-registry feature
4040

41-
@darwin @linux @windows
42-
Scenario: Mirror image to OpenShift image registry (via oc registry login)
41+
@darwin @linux @windows @story_registry
42+
Scenario: Mirror image to OpenShift image registry
4343
# mirror
4444
When executing "oc new-project testproj-img" succeeds
4545
And executing "oc registry login --insecure=true" succeeds
@@ -56,14 +56,11 @@ Feature: 3 Openshift stories
5656
# cleanup
5757
And executing "oc delete project testproj-img" succeeds
5858

59-
@linux
60-
Scenario: Create local image, push to registry, deploy (via podman login)
61-
Given checking that CRC is running
62-
And executing "podman pull quay.io/centos7/httpd-24-centos7" succeeds
63-
When executing "oc new-project testproj-img" succeeds
64-
And executing "podman login -u kubeadmin -p $(oc whoami -t) default-route-openshift-image-registry.apps-crc.testing --tls-verify=false" succeeds
65-
And executing "podman tag quay.io/centos7/httpd-24-centos7 default-route-openshift-image-registry.apps-crc.testing/testproj-img/hello:test" succeeds
66-
And executing "podman push default-route-openshift-image-registry.apps-crc.testing/testproj-img/hello:test --tls-verify=false" succeeds
59+
@darwin @linux @windows @local-img-to-registry @testdata @story_registry
60+
Scenario: Pull image locally, push to registry, deploy
61+
Given podman command is available
62+
And executing "oc new-project testproj-img" succeeds
63+
When pulling image "quay.io/centos7/httpd-24-centos7", logging in, and pushing local image to internal registry succeeds
6764
And executing "oc apply -f hello.yaml" succeeds
6865
When executing "oc rollout status deployment hello" succeeds
6966
Then stdout should contain "successfully rolled out"
@@ -75,7 +72,7 @@ Feature: 3 Openshift stories
7572

7673
# Operator from marketplace
7774

78-
@darwin @linux @windows @testdata
75+
@darwin @linux @windows @testdata @story_marketplace
7976
Scenario: Install new operator
8077
When executing "oc apply -f redis-sub.yaml" succeeds
8178
Then with up to "20" retries with wait period of "30s" command "oc get csv" output matches ".*redis-operator\.(.*)Succeeded$"

test/e2e/testsuite/testsuite.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/crc-org/crc/pkg/crc/constants"
1616
"github.com/crc-org/crc/pkg/crc/preset"
17+
"github.com/crc-org/crc/test/extended/crc/cmd"
1718
crcCmd "github.com/crc-org/crc/test/extended/crc/cmd"
1819
"github.com/crc-org/crc/test/extended/util"
1920
"github.com/cucumber/godog"
@@ -324,12 +325,18 @@ func InitializeScenario(s *godog.ScenarioContext) {
324325
ExecuteCommandWithExpectedExitStatus)
325326
s.Step(`^execut(?:e|ing) single crc (.*) command (.*)$`,
326327
ExecuteSingleCommandWithExpectedExitStatus)
328+
s.Step(`^execut(?:e|ing) podman command (.*) (succeeds|fails)$`,
329+
ExecutingPodmanCommandSucceedsFails)
327330
s.Step(`^ensuring CRC cluster is running (succeeds|fails)$`,
328331
EnsureCRCIsRunningSucceedsOrFails)
329-
s.Step(`ensuring user is logged in (succeeds|fails)`,
332+
s.Step(`^ensuring user is logged in (succeeds|fails)`,
330333
EnsureUserIsLoggedIntoClusterSucceedsOrFails)
334+
s.Step(`^podman command is available$`,
335+
PodmanCommandIsAvailable)
331336
s.Step(`^deleting a pod (succeeds|fails)$`,
332337
DeletingPodSucceedsOrFails)
338+
s.Step(`^pulling image "(.*)", logging in, and pushing local image to internal registry succeeds$`,
339+
PullLoginTagPushImageSucceeds)
333340

334341
// CRC file operations
335342
s.Step(`^file "([^"]*)" exists in CRC home folder$`,
@@ -663,3 +670,73 @@ func DeletingPodSucceedsOrFails(expected string) error {
663670
}
664671
return err
665672
}
673+
674+
func PodmanCommandIsAvailable() error {
675+
676+
// Do what 'eval $(crc podman-env) would do
677+
path := os.ExpandEnv("${HOME}/.crc/bin/oc:$PATH")
678+
csshk := os.ExpandEnv("${HOME}/.crc/machines/crc/id_ecdsa")
679+
dh := os.ExpandEnv("unix:///${HOME}/.crc/machines/crc/docker.sock")
680+
ch := "ssh://[email protected]:2222/run/user/1000/podman/podman.sock"
681+
if runtime.GOOS == "windows" {
682+
userHomeDir, _ := os.UserHomeDir()
683+
unexpandedPath := filepath.Join(userHomeDir, ".crc/bin/oc;${PATH}")
684+
path = os.ExpandEnv(unexpandedPath)
685+
csshk = filepath.Join(userHomeDir, ".crc/machines/crc/id_ecdsa")
686+
dh = "npipe:////./pipe/rc-podman"
687+
}
688+
if runtime.GOOS == "linux" {
689+
ch = "ssh://[email protected]:22/run/user/1000/podman/podman.sock"
690+
}
691+
692+
os.Setenv("PATH", path)
693+
os.Setenv("CONTAINER_SSHKEY", csshk)
694+
os.Setenv("CONTAINER_HOST", ch)
695+
os.Setenv("DOCKER_HOST", dh)
696+
697+
return nil
698+
699+
}
700+
701+
func ExecutingPodmanCommandSucceedsFails(command string, expected string) error {
702+
703+
var err error
704+
if expected == "succeeds" {
705+
_, err = cmd.RunPodmanExpectSuccess(strings.Split(command[1:len(command)-1], " ")...)
706+
} else if expected == "fails" {
707+
_, err = cmd.RunPodmanExpectFail(strings.Split(command[1:len(command)-1], " ")...)
708+
}
709+
710+
return err
711+
}
712+
713+
func PullLoginTagPushImageSucceeds(image string) error {
714+
_, err := cmd.RunPodmanExpectSuccess("pull", image)
715+
if err != nil {
716+
return err
717+
}
718+
719+
err = util.ExecuteCommand("oc whoami -t")
720+
if err != nil {
721+
return err
722+
}
723+
724+
token := util.GetLastCommandOutput("stdout")
725+
fmt.Println(token)
726+
_, err = cmd.RunPodmanExpectSuccess("login", "-u", "kubeadmin", "-p", token, "default-route-openshift-image-registry.apps-crc.testing", "--tls-verify=false") // $(oc whoami -t)
727+
if err != nil {
728+
return err
729+
}
730+
731+
_, err = cmd.RunPodmanExpectSuccess("tag", "quay.io/centos7/httpd-24-centos7", "default-route-openshift-image-registry.apps-crc.testing/testproj-img/hello:test")
732+
if err != nil {
733+
return err
734+
}
735+
736+
_, err = cmd.RunPodmanExpectSuccess("push", "default-route-openshift-image-registry.apps-crc.testing/testproj-img/hello:test", "--tls-verify=false")
737+
if err != nil {
738+
return err
739+
}
740+
741+
return nil
742+
}

test/extended/crc/cmd/cmd.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package cmd
22

33
import (
4+
"bytes"
45
"fmt"
6+
"io"
57
"os/exec"
68
"runtime"
79
"strings"
10+
"syscall"
11+
"time"
812

913
"github.com/crc-org/crc/pkg/crc/logging"
1014
"github.com/crc-org/crc/test/extended/util"
15+
"github.com/sirupsen/logrus"
1116
)
1217

1318
const (
@@ -189,3 +194,157 @@ func (c Command) ExecuteSingleWithExpectedExit(expectedExit string) error {
189194
}
190195
return fmt.Errorf("%s is a valid expected exit status", expectedExit)
191196
}
197+
198+
// PODMAN
199+
200+
// PodmanBuilder is used to build, customize, and execute a podman-remote command.
201+
type PodmanBuilder struct {
202+
cmd *exec.Cmd
203+
timeout <-chan time.Time
204+
}
205+
206+
// NewPodmanCommand returns a PodmanBuilder for running CRC.
207+
func NewPodmanCommand(args ...string) *PodmanBuilder {
208+
209+
cmd := exec.Command("podman", args...)
210+
211+
switch runtime.GOOS {
212+
case "linux":
213+
cmd = exec.Command("podman-remote", args...)
214+
case "windows":
215+
cmd = exec.Command("podman.exe", args...)
216+
}
217+
218+
return &PodmanBuilder{
219+
cmd: cmd,
220+
}
221+
}
222+
223+
// WithTimeout sets the given timeout and returns itself.
224+
func (b *PodmanBuilder) WithTimeout(t <-chan time.Time) *PodmanBuilder {
225+
b.timeout = t
226+
return b
227+
}
228+
229+
// WithStdinData sets the given data to stdin and returns itself.
230+
func (b PodmanBuilder) WithStdinData(data string) *PodmanBuilder {
231+
b.cmd.Stdin = strings.NewReader(data)
232+
return &b
233+
}
234+
235+
// WithStdinReader sets the given reader and returns itself.
236+
func (b PodmanBuilder) WithStdinReader(reader io.Reader) *PodmanBuilder {
237+
b.cmd.Stdin = reader
238+
return &b
239+
}
240+
241+
// ExecOrDie runs the executable or dies if error occurs.
242+
func (b PodmanBuilder) ExecOrDie() (string, error) {
243+
stdout, err := b.Exec()
244+
return stdout, err
245+
}
246+
247+
// ExecOrDieWithLogs runs the executable or dies if error occurs.
248+
func (b PodmanBuilder) ExecOrDieWithLogs() (string, string, error) {
249+
stdout, stderr, err := b.ExecWithFullOutput()
250+
return stdout, stderr, err
251+
}
252+
253+
// Exec runs the executable.
254+
func (b PodmanBuilder) Exec() (string, error) {
255+
stdout, _, err := b.ExecWithFullOutput()
256+
return stdout, err
257+
}
258+
259+
// ExecWithFullOutput runs the executable and returns the stdout and stderr.
260+
func (b PodmanBuilder) ExecWithFullOutput() (string, string, error) {
261+
return Exec(b.cmd, b.timeout)
262+
}
263+
264+
func Exec(cmd *exec.Cmd, timeout <-chan time.Time) (string, string, error) {
265+
var stdout, stderr bytes.Buffer
266+
cmd.Stdout, cmd.Stderr = &stdout, &stderr
267+
268+
logrus.Infof("Running '%s %s'", cmd.Path, strings.Join(cmd.Args[1:], " ")) // skip arg[0] as it is printed separately
269+
if err := cmd.Start(); err != nil {
270+
return "", "", fmt.Errorf("error starting %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v", cmd, cmd.Stdout, cmd.Stderr, err)
271+
}
272+
errCh := make(chan error, 1)
273+
go func() {
274+
errCh <- cmd.Wait()
275+
}()
276+
select {
277+
case err := <-errCh:
278+
if err != nil {
279+
var rc = 127
280+
if ee, ok := err.(*exec.ExitError); ok {
281+
rc = int(ee.Sys().(syscall.WaitStatus).ExitStatus())
282+
logrus.Infof("rc: %d", rc)
283+
}
284+
return stdout.String(), stderr.String(), CodeExitError{
285+
Err: fmt.Errorf("error running %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v", cmd, cmd.Stdout, cmd.Stderr, err),
286+
Code: rc,
287+
}
288+
}
289+
case <-timeout:
290+
_ = cmd.Process.Kill()
291+
return "", "", fmt.Errorf("timed out waiting for command %v:\nCommand stdout:\n%v\nstderr:\n%v", cmd, cmd.Stdout, cmd.Stderr)
292+
}
293+
logrus.Infof("stderr: %q", stderr.String())
294+
logrus.Infof("stdout: %q", stdout.String())
295+
return stdout.String(), stderr.String(), nil
296+
}
297+
298+
// RunPodmanExpectSuccess is a convenience wrapper over podman-remote
299+
func RunPodmanExpectSuccess(args ...string) (string, error) {
300+
return NewPodmanCommand(args...).ExecOrDie()
301+
}
302+
303+
// RunPodmanExpectFail is a convenience wrapper over PodmanBuilder
304+
// if err != nil: return stderr, nil
305+
// if err == nil: return stdout, err
306+
func RunPodmanExpectFail(args ...string) (string, error) {
307+
stdout, stderr, err := NewPodmanCommand(args...).ExecWithFullOutput()
308+
309+
if err == nil {
310+
err = fmt.Errorf("Expected error but exited without error")
311+
return stdout, err
312+
}
313+
314+
return stderr, nil
315+
}
316+
317+
// ExitError is an interface that presents an API similar to os.ProcessState, which is
318+
// what ExitError from os/exec is. This is designed to make testing a bit easier and
319+
// probably loses some of the cross-platform properties of the underlying library.
320+
type ExitError interface {
321+
String() string
322+
Error() string
323+
Exited() bool
324+
ExitStatus() int
325+
}
326+
327+
// CodeExitError is an implementation of ExitError consisting of an error object
328+
// and an exit code (the upper bits of os.exec.ExitStatus).
329+
type CodeExitError struct {
330+
Err error
331+
Code int
332+
}
333+
334+
var _ ExitError = CodeExitError{}
335+
336+
func (e CodeExitError) Error() string {
337+
return e.Err.Error()
338+
}
339+
340+
func (e CodeExitError) String() string {
341+
return e.Err.Error()
342+
}
343+
344+
func (e CodeExitError) Exited() bool {
345+
return true
346+
}
347+
348+
func (e CodeExitError) ExitStatus() int {
349+
return e.Code
350+
}

test/extended/util/shell.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ func CommandReturnShouldNotContainContent(commandField string, notexpected *mess
304304
return CompareExpectedWithActualNotContains(notexpected.Content, shell.GetLastCmdOutput(commandField))
305305
}
306306

307+
func GetLastCommandOutput(commandField string) string {
308+
return shell.GetLastCmdOutput(commandField)
309+
}
310+
307311
func CommandReturnShouldBeEmpty(commandField string) error {
308312
return CompareExpectedWithActualEquals("", shell.GetLastCmdOutput(commandField))
309313
}

test/integration/podman_test.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"path/filepath"
66
"runtime"
77

8+
"github.com/crc-org/crc/test/extended/crc/cmd"
89
. "github.com/onsi/ginkgo/v2"
910
. "github.com/onsi/gomega"
1011
)
@@ -49,15 +50,19 @@ var _ = Describe("podman preset", Label("podman-preset"), func() {
4950
})
5051

5152
It("version", func() {
52-
Expect(RunPodmanExpectSuccess("version")).Should(MatchRegexp(`Version:[\s]*\d+\.\d+\.\d+`))
53+
out, err := cmd.RunPodmanExpectSuccess("version")
54+
Expect(err).NotTo(HaveOccurred())
55+
Expect(out).Should(MatchRegexp(`Version:[\s]*\d+\.\d+\.\d+`))
5356
})
5457

5558
It("pull image", func() {
56-
RunPodmanExpectSuccess("pull", "fedora")
59+
_, err := cmd.RunPodmanExpectSuccess("pull", "fedora")
60+
Expect(err).NotTo(HaveOccurred())
5761
})
5862

5963
It("run image", func() {
60-
RunPodmanExpectSuccess("run", "fedora")
64+
_, err := cmd.RunPodmanExpectSuccess("run", "fedora")
65+
Expect(err).NotTo(HaveOccurred())
6166
})
6267

6368
It("cleanup CRC", func() {

0 commit comments

Comments
 (0)