Skip to content
This repository was archived by the owner on Oct 3, 2019. It is now read-only.

Commit 4f66fb4

Browse files
author
Pratik Jagrut
committed
[WIP] Replace oc commands in OperatorSource automated test with client-go
Signed-off-by: Pratik Jagrut <pjagrut@redhat.com>
1 parent fdeb4b9 commit 4f66fb4

3 files changed

Lines changed: 209 additions & 96 deletions

File tree

make/test.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ test-operator-source: push-operator-app-registry
107107
$(Q)sed -e "s,REPLACE_NAMESPACE,$(DEVCONSOLE_APPR_NAMESPACE)," ./$(OPSRC_DIR)/operatorsource.yaml | sed -e "s,REPLACE_OPERATOR_SOURCE_NAME,$(OPSRC_NAME)," | oc apply -f -
108108
$(Q)sed -e "s,REPLACE_APPR_REPOSITORY,$(DEVCONSOLE_APPR_REPOSITORY)," ./$(OPSRC_DIR)/catalogsourceconfig.yaml | oc apply -f -
109109
$(Q)sed -e "s,REPLACE_APPR_REPOSITORY,$(DEVCONSOLE_APPR_REPOSITORY)," ./$(OPSRC_DIR)/subscription.yaml | oc apply -f -
110-
$(Q)sleep 30
110+
$(Q)sleep 15
111111
$(Q)OPSRC_NAME=$(OPSRC_NAME) \
112112
DEVCONSOLE_OPERATOR_VERSION=$(DEVCONSOLE_OPERATOR_VERSION) \
113113
go test -vet off ${V_FLAG} $(shell go list ./... | grep $(OPSRC_DIR)) -failfast

test/operatorsource/basic_test.go

Lines changed: 57 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,108 @@
11
package operatorsource
22

33
import (
4-
"bytes"
54
"fmt"
5+
"github.com/stretchr/testify/require"
6+
corev1 "k8s.io/api/core/v1"
67
"os"
7-
"os/exec"
8-
"strings"
98
"testing"
10-
11-
"github.com/stretchr/testify/require"
9+
"time"
1210
)
1311

14-
const ShellToUse = "bash"
15-
16-
func Shellout(command string) (string, string, error) {
17-
var stdout bytes.Buffer
18-
var stderr bytes.Buffer
19-
cmd := exec.Command(ShellToUse, "-c", command)
20-
cmd.Stdout = &stdout
21-
cmd.Stderr = &stderr
22-
err := cmd.Run()
23-
return stdout.String(), stderr.String(), err
24-
}
25-
26-
func Test_OperatorSource_oc_commands(t *testing.T) {
12+
var (
13+
Client = NewTestClient()
14+
namespace = "openshift-operators"
15+
subName = "devconsole"
16+
label = "name=devconsole-operator"
17+
subscription, suberr = Client.GetSubscription(subName, namespace)
18+
)
2719

28-
defer CleanUp(t)
20+
func Test_OperatorSource(t *testing.T) {
2921

30-
t.Run("login", func(t *testing.T) { Login(t) })
31-
t.Run("subscription", func(t *testing.T) { Subscription(t) })
32-
t.Run("install plan", func(t *testing.T) { InstallPlan(t) })
33-
t.Run("operator pod", func(t *testing.T) { OperatorPod(t) })
34-
}
22+
pod, err := Client.GetPodByLabel(label, namespace)
23+
if err != nil {
24+
t.Fatal(err)
25+
}
26+
defer CleanUp(t, pod)
27+
retryInterval := time.Second * 10
28+
timeout := time.Second * 120
3529

36-
func Login(t *testing.T) {
37-
// Start - Login to oc
38-
out, _, err := Shellout("oc login -u " + os.Getenv("OC_LOGIN_USERNAME") + " -p " + os.Getenv("OC_LOGIN_PASSWORD"))
30+
err = Client.WaitForOperatorDeployment(t, pod.Name, namespace, retryInterval, timeout)
3931
if err != nil {
40-
t.Fatalf("error: %v\n", err)
32+
t.Fatal(err)
4133
} else {
42-
require.True(t, strings.Contains(out, "Login successful."), "Expecting successful login")
34+
t.Run("subscription", func(t *testing.T) { Subscription(t) })
35+
t.Run("install plan", func(t *testing.T) { InstallPlan(t) })
36+
t.Run("operator pod", func(t *testing.T) { OperatorPod(t) })
4337
}
4438
}
4539

4640
func Subscription(t *testing.T) {
4741
// 1) Verify that the subscription was created
48-
out, errout, err := Shellout("oc get sub devconsole -n openshift-operators")
49-
if err != nil {
50-
t.Logf("stdout: %s\n", out)
51-
t.Logf("stderr: %s\n", errout)
52-
t.Fatalf("error: %v\n", err)
53-
} else {
54-
require.True(t, strings.Contains(out, "devconsole"), "Expecting the subscription name to be found")
55-
require.True(t, strings.Contains(out, "installed-custom-openshift-operators"), "Expecting the subscription namespace to be found")
42+
if suberr != nil {
43+
t.Fatal(suberr)
5644
}
45+
fmt.Printf("Subscription Name: %s\nCatalog Source: %s\n", subscription.Name, subscription.Spec.CatalogSource)
46+
require.Equal(t, subName, subscription.Name)
47+
require.Equal(t, "installed-custom-openshift-operators", subscription.Spec.CatalogSource)
5748
}
5849

5950
func InstallPlan(t *testing.T) {
6051
// 2) Find the name of the install plan
61-
out, errout, err := Shellout("oc get sub devconsole -n openshift-operators -o jsonpath='{.status.installplan.name}'")
62-
var installPlan string
52+
installPlanName := subscription.Status.Install.Name
53+
fmt.Printf("Install Plan Name: %s\n", installPlanName)
54+
installPlan, err := Client.GetInstallPlan(installPlanName, namespace)
6355
if err != nil {
64-
t.Logf("stdout: %s\n", out)
65-
t.Logf("stderr: %s\n", errout)
66-
t.Fatalf("error: %v\n", err)
67-
} else {
68-
installPlan = out
56+
t.Fatal(err)
6957
}
70-
71-
// 3) Verify the install plan
72-
out, errout, err = Shellout(fmt.Sprintf("oc get installplan %s -n openshift-operators", installPlan))
73-
if err != nil {
74-
t.Logf("stdout: %s\n", out)
75-
t.Logf("stderr: %s\n", errout)
76-
t.Fatalf("error: %v\n", err)
77-
} else {
78-
require.True(t, strings.Contains(out, installPlan), "Expecting the Install Plan name to be found")
79-
require.True(t, strings.Contains(out, "devconsole-operator.v0.1.0"), "Expecting the Operator release to be found")
80-
require.True(t, strings.Contains(out, "Automatic"), "Expecting the approval method to be found")
81-
require.True(t, strings.Contains(out, "true"), "Expecting the approved state to be found")
58+
fmt.Printf("CSV: %v\n", installPlan.Spec.ClusterServiceVersionNames[0])
59+
fmt.Printf("Install Plan Approval: %v\n", installPlan.Spec.Approval)
60+
fmt.Printf("Install Plan Approved: %v\n", installPlan.Spec.Approved)
61+
62+
require.Equal(t, "devconsole-operator.v0.1.0", installPlan.Spec.ClusterServiceVersionNames[0])
63+
require.Equal(t, "Automatic", string(installPlan.Spec.Approval))
64+
if !installPlan.Spec.Approved {
65+
require.FailNow(t, "Install plan approved is false")
8266
}
8367
}
8468

8569
func OperatorPod(t *testing.T) {
86-
// Verify that the operator's pod is running
87-
out, errout, err := Shellout("oc get pods -l name=devconsole-operator -n openshift-operators -o jsonpath='{.items[*].status.phase}'")
70+
// 3) Check operator pod status, fail status != Running
71+
pod, err := Client.GetPodByLabel(label, namespace)
8872
if err != nil {
89-
t.Logf("stdout: %s\n", out)
90-
t.Logf("stderr: %s\n", errout)
91-
t.Fatalf("error: %v\n", err)
92-
} else {
93-
require.True(t, strings.Contains(out, "Running"), "Expecting the state of the Operator pod to be running")
73+
t.Fatal(err)
9474
}
75+
fmt.Printf("Pod Name: %v\nPod status: %v\n", pod.Name, pod.Status.Phase)
76+
require.Equal(t, pod.Status.Phase, corev1.PodRunning)
9577
}
9678

97-
func CleanUp(t *testing.T) {
79+
func CleanUp(t *testing.T, pod *corev1.Pod) {
9880
// Clean up resources
99-
operatorSourceName := os.Getenv("OPSRC_NAME")
10081
operatorVersion := os.Getenv("DEVCONSOLE_OPERATOR_VERSION")
10182

102-
out, errout, err := Shellout(fmt.Sprintf("oc delete opsrc %s -n openshift-marketplace", operatorSourceName))
83+
err := Client.Delete("installplan", subscription.Status.Install.Name, namespace)
10384
if err != nil {
104-
t.Logf("stdout: %s\n", out)
105-
t.Logf("stderr: %s\n", errout)
106-
t.Logf("error: %v\n", err)
107-
} else {
108-
t.Logf(out)
85+
t.Logf("Error: %v\n", err)
10986
}
11087

111-
out, errout, err = Shellout("oc delete sub devconsole -n openshift-operators")
88+
err = Client.Delete("catsrc", subscription.Spec.CatalogSource, namespace)
11289
if err != nil {
113-
t.Logf("stdout: %s\n", out)
114-
t.Logf("stderr: %s\n", errout)
115-
t.Logf("error: %v\n", err)
116-
} else {
117-
t.Logf(out)
90+
t.Logf("Error: %v\n", err)
11891
}
11992

120-
out, errout, err = Shellout("oc delete catsrc installed-custom-openshift-operators -n openshift-operators")
93+
err = Client.Delete("sub", subName, namespace)
12194
if err != nil {
122-
t.Logf("stdout: %s\n", out)
123-
t.Logf("stderr: %s\n", errout)
124-
t.Logf("error: %v\n", err)
125-
} else {
126-
t.Logf(out)
95+
t.Logf("Error: %v\n", err)
12796
}
12897

129-
out, errout, err = Shellout("oc delete csc installed-custom-openshift-operators -n openshift-marketplace")
98+
csv := fmt.Sprintf("devconsole-operator.v%s", operatorVersion)
99+
err = Client.Delete("csv", csv, namespace)
130100
if err != nil {
131-
t.Logf("stdout: %s\n", out)
132-
t.Logf("stderr: %s\n", errout)
133-
t.Logf("error: %v\n", err)
134-
} else {
135-
t.Logf(out)
101+
t.Logf("Error: %v\n", err)
136102
}
137103

138-
out, errout, err = Shellout(fmt.Sprintf("oc delete csv devconsole-operator.v%s -n openshift-operators", operatorVersion))
104+
err = Client.Delete("pod", pod.Name, namespace)
139105
if err != nil {
140-
t.Logf("stdout: %s\n", out)
141-
t.Logf("stderr: %s\n", errout)
142-
t.Logf("error: %v\n", err)
143-
} else {
144-
t.Logf(out)
106+
t.Logf("Error: %v\n", err)
145107
}
146108
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package operatorsource
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
apis_v1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
7+
client "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client"
8+
v1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/typed/operators/v1alpha1"
9+
corev1 "k8s.io/api/core/v1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/apimachinery/pkg/util/wait"
12+
"k8s.io/client-go/kubernetes"
13+
"k8s.io/client-go/tools/clientcmd"
14+
"log"
15+
"os"
16+
"testing"
17+
"time"
18+
)
19+
20+
//ClientSetK8sCoreAPI returns new Clientset for the given config, use to interact with K8s resources like pods
21+
func ClientSetK8sCoreAPI(kubeconfig string) *kubernetes.Clientset {
22+
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
23+
if err != nil {
24+
log.Fatal(err)
25+
}
26+
clientset, err := kubernetes.NewForConfig(config)
27+
if err != nil {
28+
log.Fatal(err)
29+
}
30+
return clientset
31+
}
32+
33+
//ClientSet Creates clientset for given config, use to interact with custom resources
34+
func ClientSet(kubeconfig string) v1alpha1.OperatorsV1alpha1Interface {
35+
client, err := client.NewClient(kubeconfig)
36+
if err != nil {
37+
log.Fatal(err)
38+
}
39+
return client.OperatorsV1alpha1()
40+
}
41+
42+
// TestClient wraps all the clientsets required while testing
43+
type TestClient struct {
44+
K8sClient *kubernetes.Clientset
45+
OperatorClient v1alpha1.OperatorsV1alpha1Interface
46+
}
47+
48+
//NewTestClient initialises the TestClient
49+
func NewTestClient() *TestClient {
50+
kubeconfig := os.Getenv("KUBECONFIG")
51+
52+
return &TestClient{
53+
K8sClient: ClientSetK8sCoreAPI(kubeconfig),
54+
OperatorClient: ClientSet(kubeconfig),
55+
}
56+
}
57+
58+
// GetPodByLabel is a function that takes label and namespace and returns the pod and error
59+
func (tc *TestClient) GetPodByLabel(label string, namespace string) (*corev1.Pod, error) {
60+
61+
pods, err := tc.K8sClient.CoreV1().Pods(namespace).List(metav1.ListOptions{LabelSelector: label})
62+
if err != nil {
63+
return nil, err
64+
}
65+
if len(pods.Items) == 0 {
66+
return nil, nil
67+
}
68+
return &pods.Items[0], nil
69+
}
70+
71+
//GetSubscription returns subscription struct
72+
func (tc *TestClient) GetSubscription(subName, namespace string) (*apis_v1alpha1.Subscription, error) {
73+
subscription, err := tc.OperatorClient.Subscriptions(namespace).Get(subName, metav1.GetOptions{})
74+
if err != nil {
75+
return nil, err
76+
}
77+
78+
return subscription, nil
79+
}
80+
81+
//GetInstallPlan returns install plan struct
82+
func (tc *TestClient) GetInstallPlan(installPlanName, namespace string) (*apis_v1alpha1.InstallPlan, error) {
83+
84+
installPlan, err := tc.OperatorClient.InstallPlans(namespace).Get(installPlanName, metav1.GetOptions{})
85+
if err != nil {
86+
return nil, err
87+
}
88+
return installPlan, nil
89+
}
90+
91+
//Delete takes kind, name and namespace of the resource and deletes it. Returns an error if one occurs.
92+
func (tc *TestClient) Delete(resource, name, namespace string) error {
93+
switch resource {
94+
case "subscription", "sub":
95+
err := tc.OperatorClient.Subscriptions(namespace).Delete(name, &metav1.DeleteOptions{})
96+
if err != nil {
97+
return err
98+
}
99+
return nil
100+
case "installplan":
101+
err := tc.OperatorClient.InstallPlans(namespace).Delete(name, &metav1.DeleteOptions{})
102+
if err != nil {
103+
return err
104+
}
105+
return nil
106+
case "catalogsource", "catsrc":
107+
err := tc.OperatorClient.CatalogSources(namespace).Delete(name, &metav1.DeleteOptions{})
108+
if err != nil {
109+
return err
110+
}
111+
return nil
112+
case "clusterserviceversion", "csv":
113+
err := tc.OperatorClient.ClusterServiceVersions(namespace).Delete(name, &metav1.DeleteOptions{})
114+
if err != nil {
115+
return err
116+
}
117+
return nil
118+
case "pod":
119+
err := tc.K8sClient.CoreV1().Pods(namespace).Delete(name, &metav1.DeleteOptions{})
120+
if err != nil {
121+
return err
122+
}
123+
return nil
124+
default:
125+
option := fmt.Sprintf("Invalid resource: %s", resource)
126+
return errors.New(option)
127+
128+
}
129+
}
130+
131+
//WaitForOperatorDeployment takes pod struct and wait till pods gets in runnig state
132+
func (tc *TestClient) WaitForOperatorDeployment(t *testing.T, name, namespace string, retryInterval, timeout time.Duration) error {
133+
err := wait.Poll(retryInterval, timeout, func() (bool, error) {
134+
pod, err := tc.K8sClient.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
135+
if err != nil {
136+
return false, err
137+
}
138+
if pod == nil {
139+
return false, errors.New("pod returned empty")
140+
}
141+
if pod.Status.Phase == corev1.PodRunning {
142+
return true, nil
143+
}
144+
t.Logf("Pod %s Status: %s", pod.Name, pod.Status.Phase)
145+
return false, nil
146+
})
147+
if err != nil {
148+
return err
149+
}
150+
return nil
151+
}

0 commit comments

Comments
 (0)