Skip to content

Commit 4cdfde9

Browse files
committed
Add Marketplace e2e tests
-Add script to run tests -Update makefile to add e2e tests -Update .gitignore to ignore temp test files -Update README.md with section on running tests
1 parent 605fae6 commit 4cdfde9

File tree

7 files changed

+256
-0
lines changed

7 files changed

+256
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ build/_test
66
# Generated mock go files used for testing
77
pkg/mocks
88

9+
# Generated yaml files for operator-sdk test framework
10+
deploy/test
11+
912
# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
1013

1114
### Emacs ###

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,6 @@ clean-mocks:
4646
rm -rf $(MOCKS_DIR)
4747

4848
clean: clean-mocks
49+
50+
e2e-test:
51+
./scripts/e2e-tests.sh

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,22 @@ $ oc create -f your-operator-source.yaml
7777
```
7878

7979
Once created, the Marketplace operator will use the `OperatorSource` to download your operator artifact from the app registry and display your operator offering in the Marketplace UI.
80+
81+
## Running End to End (e2e) Tests
82+
83+
To run the e2e tests defined in test/e2e that were created using the operator-sdk, first ensure that you have the following additional prerequisites:
84+
85+
1. The operator-sdk binary installed on your environment. You can get it by either downloading a released binary on the sdk release page here (https://github.com/operator-framework/operator-sdk/releases/) or by pulling down the source and compiling it locally (https://github.com/operator-framework/operator-sdk).
86+
2. A namespace on your cluster to run the tests on, e.g.
87+
```bash
88+
$ oc create namespace test-namespace
89+
```
90+
3. A Kubeconfig file that points to the cluster you want to run the tests on.
91+
92+
To run the tests, just call operator-sdk test and point to the test directory:
93+
94+
```bash
95+
operator-sdk test local ./test/e2e --up-local --kubeconfig=$KUBECONFIG --namespace $TEST_NAMESPACE
96+
```
97+
98+
You can also run the tests with `make e2e-test`.

scripts/e2e-tests.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/bash
2+
set -e
3+
4+
TEST_NAMESPACE="openshift-marketplace"
5+
MANIFEST_FOLDER="./test/e2e/environment/"
6+
NAMESPACED_MANIFEST="./${MANIFEST_FOLDER}/namespaced-manifest.yaml"
7+
GLOBAL_MANIFEST="./${MANIFEST_FOLDER}/global-manifest.yaml"
8+
OPERATOR_SOURCE_CRD="./deploy/crds/operatorsource.crd.yaml"
9+
CATALOG_SOURCE_CONFIG_CRD="./deploy/crds/catalogsourceconfig.crd.yaml"
10+
11+
# Create openshift resources if they don't exist
12+
echo "Creating openshift resources"
13+
kubectl apply -f $OPERATOR_SOURCE_CRD
14+
kubectl apply -f $CATALOG_SOURCE_CONFIG_CRD
15+
if ! kubectl get namespace $TEST_NAMESPACE; then
16+
kubectl create namespace $TEST_NAMESPACE
17+
fi
18+
19+
# Run the tests through the operator-sdk
20+
echo "Running operator-sdk test"
21+
operator-sdk test local ./test/e2e --up-local --kubeconfig=$KUBECONFIG --namespace $TEST_NAMESPACE

test/e2e/helpers.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/operator-framework/operator-sdk/pkg/test"
9+
10+
"k8s.io/apimachinery/pkg/api/errors"
11+
"k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/apimachinery/pkg/types"
13+
"k8s.io/apimachinery/pkg/util/wait"
14+
)
15+
16+
const (
17+
retryInterval = time.Second * 5
18+
timeout = time.Second * 60
19+
)
20+
21+
// This function polls the cluster for a particular resource name and namespace
22+
// If the request fails because of an IsNotFound error it retries until the specified timeout
23+
// If it succeeds it sets the result runtime.Object to the requested object
24+
func WaitForResult(t *testing.T, f *test.Framework, result runtime.Object, namespace, name string) error {
25+
namespacedName := types.NamespacedName{Name: name, Namespace: namespace}
26+
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) {
27+
err = f.Client.Get(context.TODO(), namespacedName, result)
28+
if err != nil {
29+
if errors.IsNotFound(err) {
30+
t.Logf("Waiting for creation of %s runtime object\n", name)
31+
return false, nil
32+
}
33+
return false, err
34+
}
35+
return true, nil
36+
})
37+
if err != nil {
38+
return err
39+
}
40+
t.Logf("Runtime object %s has been created\n", name)
41+
return nil
42+
}

test/e2e/main_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package e2e
2+
3+
import (
4+
"testing"
5+
6+
f "github.com/operator-framework/operator-sdk/pkg/test"
7+
)
8+
9+
func TestMain(m *testing.M) {
10+
f.MainEntry(m)
11+
}

test/e2e/marketplace_test.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
9+
"github.com/operator-framework/operator-marketplace/pkg/apis"
10+
operator "github.com/operator-framework/operator-marketplace/pkg/apis/marketplace/v1alpha1"
11+
12+
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
13+
14+
"github.com/operator-framework/operator-sdk/pkg/test"
15+
16+
corev1 "k8s.io/api/core/v1"
17+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
)
19+
20+
const (
21+
cleanupRetryInterval = time.Second * 1
22+
cleanupTimeout = time.Second * 5
23+
)
24+
25+
// Test marketplace is the root function that triggers the set of e2e tests
26+
func TestMarketplace(t *testing.T) {
27+
// Add marketplace types to test framework scheme
28+
operatorsource := &operator.OperatorSource{
29+
TypeMeta: metav1.TypeMeta{
30+
Kind: operator.OperatorSourceKind,
31+
APIVersion: fmt.Sprintf("%s/%s",
32+
operator.SchemeGroupVersion.Group, operator.SchemeGroupVersion.Version),
33+
},
34+
}
35+
catalogsourceconfig := &operator.CatalogSourceConfig{
36+
TypeMeta: metav1.TypeMeta{
37+
Kind: operator.CatalogSourceConfigKind,
38+
APIVersion: fmt.Sprintf("%s/%s",
39+
operator.SchemeGroupVersion.Group, operator.SchemeGroupVersion.Version),
40+
},
41+
}
42+
err := test.AddToFrameworkScheme(apis.AddToScheme, operatorsource)
43+
if err != nil {
44+
t.Fatalf("failed to add operatorsource custom resource scheme to framework: %v", err)
45+
}
46+
err = test.AddToFrameworkScheme(apis.AddToScheme, catalogsourceconfig)
47+
if err != nil {
48+
t.Fatalf("failed to add catalogsourceconfig custom resource scheme to framework: %v", err)
49+
}
50+
// Add (olm) catalog sources to framework scheme
51+
catalogsource := &olm.CatalogSource{
52+
TypeMeta: metav1.TypeMeta{
53+
Kind: olm.CatalogSourceKind,
54+
APIVersion: olm.CatalogSourceCRDAPIVersion,
55+
},
56+
}
57+
err = test.AddToFrameworkScheme(olm.AddToScheme, catalogsource)
58+
if err != nil {
59+
t.Fatalf("failed to add catalogsource custom resource scheme to framework: %v", err)
60+
}
61+
// run subtests
62+
t.Run("marketplace-group", func(t *testing.T) {
63+
t.Run("Cluster", MarketplaceCluster)
64+
})
65+
}
66+
67+
// This method initializes the environment and triggers the test
68+
func MarketplaceCluster(t *testing.T) {
69+
ctx := test.NewTestCtx(t)
70+
defer ctx.Cleanup()
71+
err := ctx.InitializeClusterResources(&test.CleanupOptions{TestContext: ctx, Timeout: cleanupTimeout, RetryInterval: cleanupRetryInterval})
72+
if err != nil {
73+
t.Fatalf("failed to initialize cluster resources: %v", err)
74+
}
75+
t.Log("Initialized cluster resources")
76+
// get global framework variables
77+
f := test.Global
78+
79+
if err = defaultCreateTest(t, f, ctx); err != nil {
80+
t.Fatal(err)
81+
}
82+
}
83+
84+
// This function runs a basic happy case end to end workflow for marketplace
85+
// First create an operatorsource which points to external app registry on quay
86+
// Check that the catalogsourceconfig was created
87+
// Then check the configmap and catalogsource were created from the catalogsourceconfig
88+
func defaultCreateTest(t *testing.T, f *test.Framework, ctx *test.TestCtx) error {
89+
namespace, err := ctx.GetNamespace()
90+
if err != nil {
91+
return fmt.Errorf("could not get namespace: %v", err)
92+
}
93+
94+
testOperatorSource := &operator.OperatorSource{
95+
TypeMeta: metav1.TypeMeta{
96+
Kind: operator.OperatorSourceKind,
97+
},
98+
ObjectMeta: metav1.ObjectMeta{
99+
Name: "global-operators",
100+
Namespace: namespace,
101+
},
102+
Spec: operator.OperatorSourceSpec{
103+
Type: "appregistry",
104+
Endpoint: "https://quay.io/cnr",
105+
RegistryNamespace: "marketplace_e2e",
106+
},
107+
}
108+
109+
catalogSourceConfigName := "opsrc-global-operators"
110+
configMapName := "csc-cm-opsrc-global-operators"
111+
catalogSourceName := "csc-cs-opsrc-global-operators"
112+
113+
// Create the operatorsource to download the manifests.
114+
err = f.Client.Create(
115+
context.TODO(),
116+
testOperatorSource,
117+
&test.CleanupOptions{
118+
TestContext: ctx,
119+
Timeout: cleanupTimeout,
120+
RetryInterval: cleanupRetryInterval,
121+
})
122+
if err != nil {
123+
return err
124+
}
125+
126+
// Check that we created the catalogsourceconfig.
127+
resultCatalogSourceConfig := &operator.CatalogSourceConfig{}
128+
err = WaitForResult(t, f, resultCatalogSourceConfig, namespace, catalogSourceConfigName)
129+
if err != nil {
130+
return err
131+
}
132+
133+
// Check for the config map created from the catalogsourceconfig.
134+
resultConfigMap := &corev1.ConfigMap{}
135+
err = WaitForResult(t, f, resultConfigMap, namespace, configMapName)
136+
if err != nil {
137+
return err
138+
}
139+
140+
// Then check for the catalog source.
141+
resultCatalogSource := &olm.CatalogSource{}
142+
err = WaitForResult(t, f, resultCatalogSource, namespace, catalogSourceName)
143+
if err != nil {
144+
return err
145+
}
146+
147+
// Assert that the catalogsource spec properly references the configmap.
148+
if resultCatalogSource.Spec.ConfigMap != resultConfigMap.Name {
149+
t.Errorf(
150+
"The created catalogsource %s was not properly associated with the created configmap %s",
151+
resultCatalogSource.Name,
152+
resultConfigMap.Name,
153+
)
154+
}
155+
156+
return nil
157+
}

0 commit comments

Comments
 (0)