Skip to content

Commit 604a938

Browse files
committed
pty for int tests
1 parent bdf2ea6 commit 604a938

File tree

10 files changed

+333
-101
lines changed

10 files changed

+333
-101
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ tests-e2e: oc-commands ## Run e2e tests using kind cluster
118118
$(OCI_BIN) save -o cli-e2e-img.tar ${IMAGE}
119119
GOOS=$(GOOS) go test -p 1 -timeout 30m -v -mod vendor -tags e2e ./e2e/...
120120

121+
.PHONY: tests-int
122+
tests-int: ## Run e2e integration tests. You need a running cluster connected
123+
GOOS= go test -p 1 -timeout 30m -v -mod vendor -tags int ./e2e/integration-tests/...
124+
121125
.PHONY: coverage-report
122126
coverage-report: ## Generate coverage report
123127
@echo "### Generating coverage report"

commands/netobserv

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,12 @@ case "$1" in
179179
;;
180180
esac
181181

182-
trap cleanup EXIT
182+
function onExit() {
183+
cleanup
184+
exit 0
185+
}
186+
187+
trap onExit EXIT
183188

184189
setup
185190

e2e/capture_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestFlowCapture(t *testing.T) {
5151
})
5252
defer timer.Stop()
5353

54-
output, err := RunCommand(clog, "oc-netobserv", "flows", "--log-level=trace")
54+
output, err := RunCommand(clog, "commands/oc-netobserv", "flows", "--log-level=trace")
5555
// TODO: find a way to avoid error here; this is probably related to SIGTERM instead of CTRL + C call
5656
//assert.Nil(t, err)
5757

@@ -124,7 +124,7 @@ func TestPacketCapture(t *testing.T) {
124124
})
125125
defer timer.Stop()
126126

127-
output, err := RunCommand(clog, "oc-netobserv", "packets", "--log-level=trace", "--protocol=TCP", "--port=6443")
127+
output, err := RunCommand(clog, "commands/oc-netobserv", "packets", "--log-level=trace", "--protocol=TCP", "--port=6443")
128128
// TODO: find a way to avoid error here; this is probably related to SIGTERM instead of CTRL + C call
129129
//assert.Nil(t, err)
130130

e2e/common.go

Lines changed: 117 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bufio"
55
"io"
66
"os/exec"
7-
"path"
87
"strings"
98
"syscall"
109
"time"
@@ -14,27 +13,123 @@ import (
1413
)
1514

1615
const (
17-
CommandTimeout = 60 * time.Second
16+
StartCommandWait = 30 * time.Second
17+
RunCommandTimeout = 60 * time.Second
1818
)
1919

2020
var (
2121
StartupDate = time.Now().Format("20060102-150405")
2222
)
2323

24-
// run command with tty support
24+
// start command with tty support and wait for some time before returning output
25+
// the command will keep running after this call
26+
func StartCommand(log *logrus.Entry, commandName string, arg ...string) (string, error) {
27+
log.WithFields(logrus.Fields{"cmd": commandName, "arg": arg}).Info("Starting command")
28+
29+
cmd := exec.Command(commandName, arg...)
30+
cmd.Env = append(cmd.Environ(), "isE2E=true")
31+
32+
outPipe, _ := cmd.StdoutPipe()
33+
errPipe, _ := cmd.StderrPipe()
34+
35+
var sb strings.Builder
36+
go func(_ io.ReadCloser) {
37+
reader := bufio.NewReader(errPipe)
38+
line, err := reader.ReadString('\n')
39+
for err == nil {
40+
sb.WriteString(line)
41+
line, err = reader.ReadString('\n')
42+
}
43+
}(errPipe)
44+
45+
go func(_ io.ReadCloser) {
46+
reader := bufio.NewReader(outPipe)
47+
line, err := reader.ReadString('\n')
48+
for err == nil {
49+
sb.WriteString(line)
50+
line, err = reader.ReadString('\n')
51+
}
52+
}(outPipe)
53+
54+
// start async
55+
go func() {
56+
log.Debug("Starting async ...")
57+
_, err := pty.Start(cmd)
58+
if err != nil {
59+
log.Errorf("Start returned error: %v", err)
60+
}
61+
}()
62+
63+
log.Debugf("Waiting %v ...", StartCommandWait)
64+
time.Sleep(StartCommandWait)
65+
66+
log.Debug("Returning result while command still running")
67+
return sb.String(), nil
68+
}
69+
70+
// run command with tty support and wait for stop
2571
func RunCommand(log *logrus.Entry, commandName string, arg ...string) (string, error) {
26-
cmdStr := path.Join("commands", commandName)
27-
log.WithFields(logrus.Fields{"cmd": cmdStr, "arg": arg}).Info("running command")
72+
log.WithFields(logrus.Fields{"cmd": commandName, "arg": arg}).Info("Running command")
2873

29-
log.Print("Executing command...")
30-
cmd := exec.Command(cmdStr, arg...)
74+
cmd := exec.Command(commandName, arg...)
3175
cmd.Env = append(cmd.Environ(), "isE2E=true")
3276

3377
outPipe, _ := cmd.StdoutPipe()
3478
errPipe, _ := cmd.StderrPipe()
3579

36-
timer := time.AfterFunc(CommandTimeout, func() {
37-
log.Print("Terminating command...")
80+
var sb strings.Builder
81+
go func(_ io.ReadCloser) {
82+
reader := bufio.NewReader(errPipe)
83+
line, err := reader.ReadString('\n')
84+
for err == nil {
85+
sb.WriteString(line)
86+
line, err = reader.ReadString('\n')
87+
}
88+
}(errPipe)
89+
90+
go func(_ io.ReadCloser) {
91+
reader := bufio.NewReader(outPipe)
92+
line, err := reader.ReadString('\n')
93+
for err == nil {
94+
sb.WriteString(line)
95+
line, err = reader.ReadString('\n')
96+
}
97+
}(outPipe)
98+
99+
log.Debug("Starting ...")
100+
_, err := pty.Start(cmd)
101+
if err != nil {
102+
log.Errorf("Start returned error: %v", err)
103+
return "", err
104+
}
105+
106+
log.Debug("Waiting ...")
107+
err = cmd.Wait()
108+
if err != nil {
109+
log.Errorf("Wait returned error: %v", err)
110+
}
111+
112+
// TODO: find why this returns -1. That may be related to pty implementation
113+
/*if cmd.ProcessState.ExitCode() != 0 {
114+
return sb.String(), fmt.Errorf("Cmd returned code %d", cmd.ProcessState.ExitCode())
115+
}*/
116+
117+
return sb.String(), nil
118+
}
119+
120+
// run command with tty support and terminate it after timeout
121+
// it will also simulate a keyboard input during the run
122+
func RunCommandAndTerminate(log *logrus.Entry, commandName string, arg ...string) (string, error) {
123+
log.WithFields(logrus.Fields{"cmd": commandName, "arg": arg}).Info("Running command and terminate")
124+
125+
cmd := exec.Command(commandName, arg...)
126+
cmd.Env = append(cmd.Environ(), "isE2E=true")
127+
128+
outPipe, _ := cmd.StdoutPipe()
129+
errPipe, _ := cmd.StderrPipe()
130+
131+
timer := time.AfterFunc(RunCommandTimeout, func() {
132+
log.Debug("Terminating command...")
38133
err := cmd.Process.Signal(syscall.SIGTERM)
39134
if err != nil {
40135
log.Error(err)
@@ -61,23 +156,32 @@ func RunCommand(log *logrus.Entry, commandName string, arg ...string) (string, e
61156
}
62157
}(outPipe)
63158

159+
log.Debug("Starting ...")
64160
in, err := pty.Start(cmd)
65161
if err != nil {
66-
panic(err)
162+
log.Errorf("Start returned error: %v", err)
163+
return "", err
67164
}
68165

69166
timer = time.AfterFunc(30*time.Second, func() {
70-
log.Print("Simulating keyboard typing...")
167+
log.Debug("Simulating keyboard typing...")
71168
_, err := in.Write([]byte("netobserv"))
72169
if err != nil {
73170
log.Error(err)
74171
}
75172
})
76173
defer timer.Stop()
77174

78-
if err := cmd.Wait(); err != nil {
79-
return sb.String(), err
175+
log.Debug("Waiting ...")
176+
err = cmd.Wait()
177+
if err != nil {
178+
log.Errorf("Wait returned error: %v", err)
80179
}
81180

181+
// TODO: find why this returns -1. That may be related to pty implementation
182+
/*if cmd.ProcessState.ExitCode() != 0 {
183+
return sb.String(), fmt.Errorf("Cmd returned code %d", cmd.ProcessState.ExitCode())
184+
}*/
185+
82186
return sb.String(), nil
83187
}

e2e/integration-tests/cli.go

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,59 @@
1+
//go:build int
2+
13
package integrationtests
24

35
import (
46
"context"
57
"io/fs"
6-
"log"
78
"os"
89
"time"
910

11+
"k8s.io/apimachinery/pkg/api/errors"
1012
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1113
"k8s.io/apimachinery/pkg/util/wait"
1214
"k8s.io/client-go/kubernetes"
15+
16+
"github.com/sirupsen/logrus"
17+
)
18+
19+
const (
20+
PollInterval = 5 * time.Second
21+
PollTimeout = 10 * time.Minute
1322
)
1423

15-
func isCollectorReady(clientset *kubernetes.Clientset, cliNS string) (bool, error) {
16-
err := wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 300*time.Second, false, func(context.Context) (done bool, err error) {
24+
var (
25+
clog = logrus.WithField("component", "cli")
26+
)
27+
28+
func isNamespace(clientset *kubernetes.Clientset, cliNS string, exists bool) (bool, error) {
29+
err := wait.PollUntilContextTimeout(context.Background(), PollInterval, PollTimeout, true, func(context.Context) (done bool, err error) {
30+
namespace, err := getNamespace(clientset, cliNS)
31+
if exists {
32+
if err != nil {
33+
return false, err
34+
}
35+
return namespace != nil, err
36+
} else if errors.IsNotFound(err) {
37+
return true, nil
38+
}
39+
return false, err
40+
})
41+
if err != nil {
42+
return false, err
43+
}
44+
return true, nil
45+
}
46+
47+
func isCollector(clientset *kubernetes.Clientset, cliNS string, ready bool) (bool, error) {
48+
err := wait.PollUntilContextTimeout(context.Background(), PollInterval, PollTimeout, true, func(context.Context) (done bool, err error) {
1749
collectorPod, err := getNamespacePods(clientset, cliNS, &metav1.ListOptions{FieldSelector: "status.phase=Running", LabelSelector: "run=collector"})
1850
if err != nil {
1951
return false, err
2052
}
21-
return len(collectorPod) > 0, nil
53+
if ready {
54+
return len(collectorPod) > 0, nil
55+
}
56+
return len(collectorPod) == 0, nil
2257
})
2358
if err != nil {
2459
return false, err
@@ -27,8 +62,7 @@ func isCollectorReady(clientset *kubernetes.Clientset, cliNS string) (bool, erro
2762
}
2863

2964
func isDaemonsetReady(clientset *kubernetes.Clientset, daemonsetName string, cliNS string) (bool, error) {
30-
err := wait.PollUntilContextTimeout(context.Background(), 10*time.Second, 300*time.Second, false, func(context.Context) (done bool, err error) {
31-
65+
err := wait.PollUntilContextTimeout(context.Background(), PollInterval, PollTimeout, true, func(context.Context) (done bool, err error) {
3266
cliDaemonset, err := getDaemonSet(clientset, daemonsetName, cliNS)
3367
if err != nil {
3468
return false, err
@@ -41,19 +75,35 @@ func isDaemonsetReady(clientset *kubernetes.Clientset, daemonsetName string, cli
4175
return true, nil
4276
}
4377

44-
func isCLIReady(clientset *kubernetes.Clientset, cliNS string) (bool, error) {
45-
collectorReady, err := isCollectorReady(clientset, cliNS)
78+
func isCLIRuning(clientset *kubernetes.Clientset, cliNS string) (bool, error) {
79+
namespaceCreated, err := isNamespace(clientset, cliNS, true)
4680
if err != nil {
4781
return false, err
4882
}
49-
log.Printf("Collector ready: %v", collectorReady)
83+
clog.Debugf("Namespace created: %v", namespaceCreated)
84+
5085
daemonsetReady, err := isDaemonsetReady(clientset, "netobserv-cli", cliNS)
5186
if err != nil {
5287
return false, err
5388
}
54-
log.Printf("Daemonset ready: %v", daemonsetReady)
89+
clog.Debugf("Daemonset ready: %v", daemonsetReady)
5590

56-
return collectorReady && daemonsetReady, nil
91+
collectorReady, err := isCollector(clientset, cliNS, true)
92+
if err != nil {
93+
return false, err
94+
}
95+
clog.Debugf("Collector ready: %v", collectorReady)
96+
97+
return namespaceCreated && daemonsetReady && collectorReady, nil
98+
}
99+
100+
func isCLIDone(clientset *kubernetes.Clientset, cliNS string) (bool, error) {
101+
collectorDone, err := isCollector(clientset, cliNS, false)
102+
if err != nil {
103+
return false, err
104+
}
105+
clog.Debugf("Collector done: %v", collectorDone)
106+
return collectorDone, nil
57107
}
58108

59109
// get latest flows.json file

e2e/integration-tests/cluster.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build int
2+
13
package integrationtests
24

35
import (
@@ -56,6 +58,14 @@ func getDaemonSet(clientset *kubernetes.Clientset, daemonset string, ns string)
5658
return ds, nil
5759
}
5860

61+
func getNamespace(clientset *kubernetes.Clientset, name string) (*corev1.Namespace, error) {
62+
namespace, err := clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{})
63+
if err != nil {
64+
return nil, err
65+
}
66+
return namespace, nil
67+
}
68+
5969
func getNamespacePods(clientset *kubernetes.Clientset, namespace string, options *metav1.ListOptions) ([]string, error) {
6070
pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), *options)
6171
var allPods []string

0 commit comments

Comments
 (0)