Enable olm and Run operatorhub tests on kubernetes cluster (#4754)

* Running operatorhub tests on kubernetes cluster

* Fixing script path

* Align spec field in service binding subscription yaml

* Adding debugging steps for Finding ImagePullBackOff Reason

* Adding mongodb operator and modifying test accordingly to resolve https://github.com/openshift/odo/issues/4651#issuecomment-851269315

* Fixing command typo for kubernetes

* Fixing regex issue

* Fixing mongodb subscription yaml format

* Injecting docker account secrets into kubernetes cluster for etcd services

* remove setup-kube-operators file

* Separate setup for kubernetes operator

* Adding operators common for both ocp and minikube

* sync function for ocp and minikube

* Adding some tests refactor

* refactoring operator hub tests for redis operator

* Finding reason for the pending state of redis pod and fix regex format

* Fixing regex format

* Using redis instance CR definition from example file

* Modifying test to use RedisCluster operator

* Fixing redis-secret failure for specific namespace in the test

* removing not used function for copying example

* Refactoring operator test

* redis pod name are changing so frequently so updating it once again

* Refactored operator link test

* Skipping postgresql test for kubernetes

* Fixing invalid devfile with link

* Fixing file not found error

* Address review comments

* Run all tests on ocp and kubernetes

* Shifting to Redis CRD instead of RedisCluster and review comments

* Fixing failure related to no name

* Address cosmetic changes as per review

* olm installation on minikube
This commit is contained in:
Priti Kumari
2021-07-06 19:35:27 +05:30
committed by GitHub
parent 08272a2bb6
commit 9dba98a9e2
12 changed files with 357 additions and 169 deletions

50
scripts/configure-cluster/common/setup-operators.sh Normal file → Executable file
View File

@@ -1,46 +1,46 @@
#!/bin/bash
set -x
install_etcd_operator(){
# Create subscription
oc create -f - <<EOF
install_redis_operator(){
$1 create -f - <<EOF
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: etcd
namespace: openshift-operators
name: my-redis-operator
namespace: $2
spec:
channel: clusterwide-alpha
installPlanApproval: Automatic
name: etcd
source: community-operators
sourceNamespace: openshift-marketplace
channel: stable
name: redis-operator
source: $3
sourceNamespace: $4
EOF
}
install_service_binding_operator(){
oc create -f - <<EOF
$1 create -f - <<EOF
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
labels:
operators.coreos.com/rh-service-binding-operator.openshift-operators: ""
name: rh-service-binding-operator
namespace: openshift-operators
name: my-service-binding-operator
namespace: $2
spec:
channel: beta
installPlanApproval: Automatic
name: rh-service-binding-operator
source: redhat-operators
sourceNamespace: openshift-marketplace
name: $3
source: $4
sourceNamespace: $5
EOF
}
if [ $KUBERNETES == "true" ]; then
# install "redis-oprator" using "kubectl" in "operators" namespace; use "operatorhubio-catalog" catalog soure from "olm" namespace
install_redis_operator kubectl operators operatorhubio-catalog olm
# install etcd operator
# install "service-binding-operator" using "kubectl" in "operators" namespace; use "operatorhubio-catalog" catalog soure from "olm" namespace
install_service_binding_operator kubectl operators service-binding-operator operatorhubio-catalog olm
else
# install "redis-oprator" using "oc" in "openshift-operators" namespace; use "community-operators" catalog soure from "openshift-marketplace" namespace
install_redis_operator oc openshift-operators community-operators openshift-marketplace
install_etcd_operator
# install service-binding-operator
install_service_binding_operator
# install "service-binding-operator" using "oc" in "openshift-operators" namespace; use "redhat-operators" catalog soure from "openshift-marketplace" namespace
install_service_binding_operator oc openshift-operators rh-service-binding-operator redhat-operators openshift-marketplace
fi

View File

@@ -8,8 +8,6 @@ SETUP_OPERATORS="$LIBCOMMON/setup-operators.sh"
SETUP_POSTGRES_OPERATOR="$LIBCOMMON/setup-postgres-operator.sh"
AUTH_SCRIPT="$LIBCOMMON/auth.sh"
KUBEADMIN_SCRIPT="$LIBCOMMON/kubeconfigandadmin.sh"
CI_OPERATOR_HUB_PROJECT="ci-operator-hub-project"
POSTGRES_OPERATOR_PROJECT="odo-operator-test"
# list of namespace to create
@@ -20,12 +18,6 @@ IMAGE_TEST_NAMESPACES="openjdk-11-rhel8 nodejs-12-rhel7 nodejs-12 openjdk-11 nod
. $SETUP_POSTGRES_OPERATOR
# Setup the cluster for Operator tests
# Create a new namesapce which will be used for OperatorHub checks
oc new-project $CI_OPERATOR_HUB_PROJECT
# Let developer user have access to the project
oc adm policy add-role-to-user edit developer
sh $SETUP_OPERATORS
oc new-project $POSTGRES_OPERATOR_PROJECT

View File

@@ -15,6 +15,7 @@ case ${1} in
minikube)
# Integration tests
shout "| Running integration Tests on MiniKube"
make test-operator-hub
make test-cmd-project
make test-integration-devfile

View File

@@ -62,13 +62,25 @@ case ${1} in
fi
minikube version
# Setup to find nessasary data from cluster setup
## Constants
SETUP_OPERATORS="./scripts/configure-cluster/common/setup-operators.sh"
# The OLM Version
export OLM_VERSION="v0.17.0"
# Enable OLM for running operator tests
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/$OLM_VERSION/install.sh | bash -s $OLM_VERSION
set +x
# Get kubectl cluster info
kubectl cluster-info
set -x
# Set kubernetes env var as true, to distinguish the platform inside the tests
export KUBERNETES=true
# Create Operators for Operator tests
sh $SETUP_OPERATORS
;;
*)
echo "<<< Need parameter set to minikube or minishift >>>"

View File

@@ -0,0 +1,39 @@
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: Redis
metadata:
name: redis-standalone
spec:
redisExporter:
enabled: true
image: 'quay.io/opstree/redis-exporter:1.0'
imagePullPolicy: Always
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 100m
memory: 128Mi
kubernetesConfig:
image: 'quay.io/opstree/redis:v6.2'
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 101m
memory: 128Mi
limits:
cpu: 101m
memory: 128Mi
redisSecret:
name: redis-secret
key: password
serviceType: LoadBalancer
redisConfig: {}
storage:
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

View File

@@ -43,23 +43,53 @@ components:
name: runtime
- kubernetes:
inlined: |
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdCluster
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: Redis
metadata:
annotations:
etcd.database.coreos.com/scope: clusterwide
name: myetcd
name: myredis
spec:
size: 1
version: 3.2.13
name: myetcd
redisExporter:
enabled: true
image: 'quay.io/opstree/redis-exporter:1.0'
imagePullPolicy: Always
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 100m
memory: 128Mi
kubernetesConfig:
image: 'quay.io/opstree/redis:v6.2'
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 101m
memory: 128Mi
limits:
cpu: 101m
memory: 128Mi
redisSecret:
name: redis-secret
key: password
serviceType: LoadBalancer
redisConfig: {}
storage:
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
name: myredis
- kubernetes:
inlined: |
apiVersion: binding.operators.coreos.com/v1alpha1
kind: ServiceBinding
metadata:
creationTimestamp: null
name: etcd-link
name: redis-link
spec:
application:
group: apps
@@ -69,13 +99,13 @@ components:
bindAsFiles: true
detectBindingResources: true
services:
- group: etcd.database.coreos.com
kind: EtcdCluster
name: myetcd
version: v1beta2
- group: redis.redis.opstreelabs.in
kind: Redis
name: myredis
version: v1beta1
status:
secret: ""
name: etcd-link
name: redis-link
metadata:
description: Stack with Node.js 14
displayName: Node.js Runtime

View File

@@ -27,4 +27,8 @@ type CliRunner interface {
VerifyResourceDeleted(ri ResourceInfo)
VerifyResourceToBeDeleted(ri ResourceInfo)
GetAnnotationsDeployment(cmpName, appName, projectName string) map[string]string
GetAllPodsInNs(namespace string) string
WaitForRunnerCmdOut(args []string, timeout int, errOnFail bool, check func(output string) bool, includeStdErr ...bool) bool
PodsShouldBeRunning(project string, regex string)
CreateSecret(secretName, secretPass, project string)
}

View File

@@ -111,6 +111,22 @@ func Getwd() string {
return dir
}
// CopyExampleFile copies an example file from tests/examples/<file-path>
// into targetDst
func CopyExampleFile(filePath, targetDst string) {
// filename of this file
_, filename, _, _ := runtime.Caller(0)
// path to the examples directory
examplesDir := filepath.Join(filepath.Dir(filename), "..", "examples")
src := filepath.Join(examplesDir, filePath)
info, err := os.Stat(src)
Expect(err).NotTo(HaveOccurred())
err = util.CopyFile(src, targetDst, info)
Expect(err).NotTo(HaveOccurred())
}
// CopyExample copies an example from tests/examples/<binaryOrSource>/<componentName>/<exampleName> into targetDir
func CopyExample(exampleName string, targetDir string) {
// filename of this file

View File

@@ -2,6 +2,7 @@ package helper
import (
"fmt"
"regexp"
"strings"
"time"
@@ -251,3 +252,70 @@ func (kubectl KubectlRunner) VerifyResourceToBeDeleted(ri ResourceInfo) {
func (kubectl KubectlRunner) GetAnnotationsDeployment(componentName, appName, projectName string) map[string]string {
return GetAnnotationsDeployment(kubectl.path, componentName, appName, projectName)
}
//GetAllPodsInNs gets the list of pods in given namespace. It waits for reasonable amount of time for pods to come up
func (kubectl KubectlRunner) GetAllPodsInNs(namespace string) string {
args := []string{"get", "pods", "-n", namespace}
noResourcesMsg := fmt.Sprintf("No resources found in %s namespace", namespace)
kubectl.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return !strings.Contains(output, noResourcesMsg)
}, true)
return Cmd(kubectl.path, args...).ShouldPass().Out()
}
func (kubectl KubectlRunner) PodsShouldBeRunning(project string, regex string) {
// now verify if the pods for the operator have started
pods := kubectl.GetAllPodsInNs(project)
// Look for pods with specified regex
pod := regexp.MustCompile(regex).FindString(pods)
args := []string{"get", "pods", pod, "-o", "template=\"{{.status.phase}}\"", "-n", project}
kubectl.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return strings.Contains(output, "Running")
})
}
// WaitForCmdOut runs "kubectl" command until it gets
// the expected output.
// It accepts 4 arguments
// args (arguments to the program)
// timeout (the time to wait for the output)
// errOnFail (flag to set if test should fail if command fails)
// check (function with output check logic)
// It times out if the command doesn't fetch the
// expected output within the timeout period.
func (kubectl KubectlRunner) WaitForRunnerCmdOut(args []string, timeout int, errOnFail bool, check func(output string) bool, includeStdErr ...bool) bool {
pingTimeout := time.After(time.Duration(timeout) * time.Minute)
// this is a test package so time.Tick() is acceptable
// nolint
tick := time.Tick(time.Second)
for {
select {
case <-pingTimeout:
Fail(fmt.Sprintf("Timeout after %v minutes", timeout))
case <-tick:
session := CmdRunner(kubectl.path, args...)
if errOnFail {
Eventually(session).Should(gexec.Exit(0), runningCmd(session.Command))
} else {
Eventually(session).Should(gexec.Exit(), runningCmd(session.Command))
}
session.Wait()
output := string(session.Out.Contents())
if len(includeStdErr) > 0 && includeStdErr[0] {
output += "\n"
output += string(session.Err.Contents())
}
if check(strings.TrimSpace(string(output))) {
return true
}
}
}
}
// CreateSecret takes secret name, password and the namespace where we want to create the specific secret into the cluster
func (kubectl KubectlRunner) CreateSecret(secretName, secretPass, project string) {
Cmd(kubectl.path, "create", "secret", "generic", secretName, "--from-literal=password="+secretPass, "-n", project).ShouldPass()
}

View File

@@ -661,12 +661,12 @@ func (oc OcRunner) DeletePod(podName string, namespace string) {
//GetAllPodsInNs gets the list of pods in given namespace. It waits for reasonable amount of time for pods to come up
func (oc OcRunner) GetAllPodsInNs(namespace string) string {
ocArgs := []string{"get", "pods", "-n", namespace}
args := []string{"get", "pods", "-n", namespace}
noResourcesMsg := fmt.Sprintf("No resources found in %s namespace", namespace)
WaitForCmdOut(oc.path, ocArgs, 1, true, func(output string) bool {
oc.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return !strings.Contains(output, noResourcesMsg)
}, true)
return Cmd(oc.path, ocArgs...).ShouldPass().Out()
return Cmd(oc.path, args...).ShouldPass().Out()
}
//StatFileInPod returns stat result of filepath in pod of given component, in a given app, in a given project.
@@ -707,10 +707,54 @@ func (oc OcRunner) PodsShouldBeRunning(project string, regex string) {
// now verify if the pods for the operator have started
pods := oc.GetAllPodsInNs(project)
// Look for pods with specified regex
etcdPod := regexp.MustCompile(regex).FindString(pods)
ocArgs := []string{"get", "pods", etcdPod, "-o", "template=\"{{.status.phase}}\"", "-n", project}
WaitForCmdOut("oc", ocArgs, 1, true, func(output string) bool {
pod := regexp.MustCompile(regex).FindString(pods)
args := []string{"get", "pods", pod, "-o", "template=\"{{.status.phase}}\"", "-n", project}
oc.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return strings.Contains(output, "Running")
})
}
// WaitForRunnerCmdOut runs "oc" command until it gets
// the expected output.
// It accepts 4 arguments
// args (arguments to the program)
// timeout (the time to wait for the output)
// errOnFail (flag to set if test should fail if command fails)
// check (function with output check logic)
// It times out if the command doesn't fetch the
// expected output within the timeout period.
func (oc OcRunner) WaitForRunnerCmdOut(args []string, timeout int, errOnFail bool, check func(output string) bool, includeStdErr ...bool) bool {
pingTimeout := time.After(time.Duration(timeout) * time.Minute)
// this is a test package so time.Tick() is acceptable
// nolint
tick := time.Tick(time.Second)
for {
select {
case <-pingTimeout:
Fail(fmt.Sprintf("Timeout after %v minutes", timeout))
case <-tick:
session := CmdRunner(oc.path, args...)
if errOnFail {
Eventually(session).Should(gexec.Exit(0), runningCmd(session.Command))
} else {
Eventually(session).Should(gexec.Exit(), runningCmd(session.Command))
}
session.Wait()
output := string(session.Out.Contents())
if len(includeStdErr) > 0 && includeStdErr[0] {
output += "\n"
output += string(session.Err.Contents())
}
if check(strings.TrimSpace(string(output))) {
return true
}
}
}
}
// CreateSecret takes secret name, password and the namespace where we want to create the specific secret into the cluster
func (oc OcRunner) CreateSecret(secretName, secretPass, project string) {
Cmd(oc.path, "create", "secret", "generic", secretName, "--from-literal=password="+secretPass, "-n", project).ShouldPass()
}

View File

@@ -26,26 +26,23 @@ var _ = Describe("odo link command tests for OperatorHub", func() {
Context("Operators are installed in the cluster", func() {
var etcdOperator string
var etcdCluster string
var redisOperator string
var redisCluster string
BeforeEach(func() {
// wait till odo can see that all operators installed by setup script in the namespace
odoArgs := []string{"catalog", "list", "services"}
operators := []string{"etcdoperator", "service-binding-operator"}
operators := []string{"redis-operator", "service-binding-operator"}
for _, operator := range operators {
helper.WaitForCmdOut("odo", odoArgs, 5, true, func(output string) bool {
return strings.Contains(output, operator)
})
}
commonVar.CliRunner.CreateSecret("redis-secret", "password", commonVar.Project)
list := helper.Cmd("odo", "catalog", "list", "services").ShouldPass().Out()
etcdOperator = regexp.MustCompile(`etcdoperator\.*[a-z][0-9]\.[0-9]\.[0-9]-clusterwide`).FindString(list)
etcdCluster = fmt.Sprintf("%s/EtcdCluster", etcdOperator)
})
AfterEach(func() {
helper.DeleteProject(commonVar.Project)
redisOperator = regexp.MustCompile(`redis-operator\.*[a-z][0-9]\.[0-9]\.[0-9]`).FindString(list)
redisCluster = fmt.Sprintf("%s/Redis", redisOperator)
})
When("a component and a service are deployed", func() {
@@ -59,8 +56,8 @@ var _ = Describe("odo link command tests for OperatorHub", func() {
helper.Cmd("odo", "create", "nodejs", componentName).ShouldPass()
serviceName := "service-" + helper.RandString(6)
svcFullName = strings.Join([]string{"EtcdCluster", serviceName}, "/")
helper.Cmd("odo", "service", "create", etcdCluster, serviceName, "--project", commonVar.Project).ShouldPass()
svcFullName = strings.Join([]string{"Redis", serviceName}, "/")
helper.Cmd("odo", "service", "create", redisCluster, serviceName, "--project", commonVar.Project).ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
name := commonVar.CliRunner.GetRunningPodNameByComponent(componentName, commonVar.Project)
@@ -94,7 +91,7 @@ var _ = Describe("odo link command tests for OperatorHub", func() {
})
It("should find the link environment variable", func() {
stdOut := helper.Cmd("odo", "exec", "--", "sh", "-c", "echo $ETCDCLUSTER_CLUSTERIP").ShouldPass().Out()
stdOut := helper.Cmd("odo", "exec", "--", "sh", "-c", "echo $REDIS_CLUSTERIP").ShouldPass().Out()
Expect(stdOut).To(Not(BeEmpty()))
})
@@ -102,7 +99,7 @@ var _ = Describe("odo link command tests for OperatorHub", func() {
stdOut := helper.Cmd("odo", "describe").ShouldPass().Out()
Expect(stdOut).To(ContainSubstring(svcFullName))
Expect(stdOut).To(ContainSubstring("Environment Variables"))
Expect(stdOut).To(ContainSubstring("ETCDCLUSTER_CLUSTERIP"))
Expect(stdOut).To(ContainSubstring("REDIS_CLUSTERIP"))
})
})
})
@@ -163,17 +160,17 @@ var _ = Describe("odo link command tests for OperatorHub", func() {
})
It("should find bindings for service", func() {
helper.Cmd("odo", "exec", "--", "ls", "/bindings/etcd-link/clusterIP").ShouldPass()
helper.Cmd("odo", "exec", "--", "ls", "/bindings/redis-link/clusterIP").ShouldPass()
})
It("should find owner references on link and service", func() {
ocArgs := []string{"get", "servicebinding", "etcd-link", "-o", "jsonpath='{.metadata.ownerReferences.*.name}'", "-n", commonVar.Project}
helper.WaitForCmdOut("oc", ocArgs, 1, true, func(output string) bool {
args := []string{"get", "servicebinding", "redis-link", "-o", "jsonpath='{.metadata.ownerReferences.*.name}'", "-n", commonVar.Project}
commonVar.CliRunner.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return strings.Contains(output, "api-app")
})
ocArgs = []string{"get", "etcdclusters.etcd.database.coreos.com", "myetcd", "-o", "jsonpath='{.metadata.ownerReferences.*.name}'", "-n", commonVar.Project}
helper.WaitForCmdOut("oc", ocArgs, 1, true, func(output string) bool {
args = []string{"get", "redis.redis.redis.opstreelabs.in", "myredis", "-o", "jsonpath='{.metadata.ownerReferences.*.name}'", "-n", commonVar.Project}
commonVar.CliRunner.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return strings.Contains(output, "api-app")
})
})

View File

@@ -18,12 +18,10 @@ import (
var _ = Describe("odo service command tests for OperatorHub", func() {
var commonVar helper.CommonVar
var oc helper.OcRunner
BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
helper.Chdir(commonVar.Context)
oc = helper.NewOcRunner("oc")
})
AfterEach(func() {
@@ -35,7 +33,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
BeforeEach(func() {
// wait till odo can see that all operators installed by setup script in the namespace
odoArgs := []string{"catalog", "list", "services"}
operators := []string{"etcdoperator", "service-binding-operator"}
operators := []string{"redis-operator", "service-binding-operator"}
for _, operator := range operators {
helper.WaitForCmdOut("odo", odoArgs, 5, true, func(output string) bool {
return strings.Contains(output, operator)
@@ -43,10 +41,6 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
}
})
AfterEach(func() {
helper.DeleteProject(commonVar.Project)
})
It("should not allow creating service without valid context", func() {
stdOut := helper.Cmd("odo", "service", "create").ShouldFail().Err()
Expect(stdOut).To(ContainSubstring("service can be created/deleted from a valid component directory only"))
@@ -59,6 +53,9 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
var projectName string
BeforeEach(func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
projectName = util.GetEnvWithDefault("REDHAT_POSTGRES_OPERATOR_PROJECT", "odo-operator-test")
helper.GetCliRunner().SetProject(projectName)
operators := helper.Cmd("odo", "catalog", "list", "services").ShouldPass().Out()
@@ -99,7 +96,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
})
It("should create pods in running state", func() {
oc.PodsShouldBeRunning(projectName, fmt.Sprintf(`%s-.[\-a-z0-9]*`, operandName))
commonVar.CliRunner.PodsShouldBeRunning(projectName, fmt.Sprintf(`%s-.[\-a-z0-9]*`, operandName))
})
It("should list the service", func() {
@@ -115,45 +112,46 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
})
Context("a specific operator is installed", func() {
var etcdOperator string
var etcdCluster string
var redisOperator string
var redisCluster string
BeforeEach(func() {
commonVar.CliRunner.CreateSecret("redis-secret", "password", commonVar.Project)
operators := helper.Cmd("odo", "catalog", "list", "services").ShouldPass().Out()
etcdOperator = regexp.MustCompile(`etcdoperator\.*[a-z][0-9]\.[0-9]\.[0-9]-clusterwide`).FindString(operators)
etcdCluster = fmt.Sprintf("%s/EtcdCluster", etcdOperator)
redisOperator = regexp.MustCompile(`redis-operator\.*[a-z][0-9]\.[0-9]\.[0-9]`).FindString(operators)
redisCluster = fmt.Sprintf("%s/Redis", redisOperator)
})
It("should describe the operator with human-readable output", func() {
output := helper.Cmd("odo", "catalog", "describe", "service", etcdCluster).ShouldPass().Out()
Expect(output).To(ContainSubstring("Kind: EtcdCluster"))
output := helper.Cmd("odo", "catalog", "describe", "service", redisCluster).ShouldPass().Out()
Expect(output).To(ContainSubstring("Kind: Redis"))
})
It("should describe the example of the operator", func() {
output := helper.Cmd("odo", "catalog", "describe", "service", etcdCluster, "--example").ShouldPass().Out()
Expect(output).To(ContainSubstring("kind: EtcdCluster"))
output := helper.Cmd("odo", "catalog", "describe", "service", redisCluster, "--example").ShouldPass().Out()
Expect(output).To(ContainSubstring("kind: Redis"))
helper.MatchAllInOutput(output, []string{"apiVersion", "kind"})
})
It("should describe the example of the operator as json", func() {
outputJSON := helper.Cmd("odo", "catalog", "describe", "service", etcdCluster, "--example", "-o", "json").ShouldPass().Out()
outputJSON := helper.Cmd("odo", "catalog", "describe", "service", redisCluster, "--example", "-o", "json").ShouldPass().Out()
value := gjson.Get(outputJSON, "spec.kind")
Expect(value.String()).To(Equal("EtcdCluster"))
Expect(value.String()).To(Equal("Redis"))
})
It("should describe the operator with json output", func() {
outputJSON := helper.Cmd("odo", "catalog", "describe", "service", etcdCluster, "-o", "json").ShouldPass().Out()
outputJSON := helper.Cmd("odo", "catalog", "describe", "service", redisCluster, "-o", "json").ShouldPass().Out()
values := gjson.GetMany(outputJSON, "spec.kind", "spec.displayName")
expected := []string{"EtcdCluster", "etcd Cluster"}
expected := []string{"Redis", "Redis"}
Expect(helper.GjsonMatcher(values, expected)).To(Equal(true))
})
It("should find the services by keyword", func() {
stdOut := helper.Cmd("odo", "catalog", "search", "service", "etcd").ShouldPass().Out()
helper.MatchAllInOutput(stdOut, []string{"etcdoperator", "EtcdCluster"})
stdOut := helper.Cmd("odo", "catalog", "search", "service", "redis").ShouldPass().Out()
helper.MatchAllInOutput(stdOut, []string{"redis-operator", "Redis"})
stdOut = helper.Cmd("odo", "catalog", "search", "service", "EtcdCluster").ShouldPass().Out()
helper.MatchAllInOutput(stdOut, []string{"etcdoperator", "EtcdCluster"})
stdOut = helper.Cmd("odo", "catalog", "search", "service", "Redis").ShouldPass().Out()
helper.MatchAllInOutput(stdOut, []string{"redis-operator", "Redis"})
stdOut = helper.Cmd("odo", "catalog", "search", "service", "dummy").ShouldFail().Err()
Expect(stdOut).To(ContainSubstring("no service matched the query: dummy"))
@@ -161,7 +159,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
It("should list the operator in JSON output", func() {
jsonOut := helper.Cmd("odo", "catalog", "list", "services", "-o", "json").ShouldPass().Out()
helper.MatchAllInOutput(jsonOut, []string{"etcdoperator"})
helper.MatchAllInOutput(jsonOut, []string{"redis-operator"})
})
When("a nodejs component is created", func() {
@@ -175,6 +173,11 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
Expect(stdOut).To(ContainSubstring("odo doesn't support interactive mode for creating Operator backed service"))
})
It("should define the CR output of the operator instance in dryRun mode", func() {
stdOut := helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/Redis", redisOperator), "--dry-run", "--project", commonVar.Project).ShouldPass().Out()
helper.MatchAllInOutput(stdOut, []string{"apiVersion", "kind"})
})
When("odo push is executed", func() {
BeforeEach(func() {
helper.Cmd("odo", "push").ShouldPass()
@@ -184,24 +187,19 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
stdOut := helper.Cmd("odo", "link", "EtcdCluster/example").ShouldFail().Err()
Expect(stdOut).To(ContainSubstring("couldn't find service named %q", "EtcdCluster/example"))
stdOut := helper.Cmd("odo", "link", "Redis/redis-standalone").ShouldFail().Err()
Expect(stdOut).To(ContainSubstring("couldn't find service named %q", "Redis/redis-standalone"))
})
})
When("an EtcdCluster instance is created in dryRun mode and output stored in a file", func() {
When("an Redis instance definition copied from example file", func() {
var fileName string
BeforeEach(func() {
stdOut := helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/EtcdCluster", etcdOperator), "--dry-run", "--project", commonVar.Project).ShouldPass().Out()
helper.MatchAllInOutput(stdOut, []string{"apiVersion", "kind"})
randomFileName := helper.RandString(6) + ".yaml"
fileName = filepath.Join(os.TempDir(), randomFileName)
if err := ioutil.WriteFile(fileName, []byte(stdOut), 0644); err != nil {
fmt.Printf("Could not write yaml spec to file %s because of the error %v", fileName, err.Error())
}
helper.CopyExampleFile(filepath.Join("operators", "redis.yaml"), filepath.Join(fileName))
})
AfterEach(func() {
@@ -215,12 +213,12 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
})
AfterEach(func() {
helper.Cmd("odo", "service", "delete", "EtcdCluster/example", "-f").ShouldPass()
helper.Cmd("odo", "service", "delete", "Redis/redis-standalone", "-f").ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
It("should create pods in running state", func() {
oc.PodsShouldBeRunning(commonVar.Project, `example-.[a-z0-9]*`)
commonVar.CliRunner.PodsShouldBeRunning(commonVar.Project, `redis.[a-z0-9-]*`)
})
})
@@ -230,7 +228,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
var svcFullName string
BeforeEach(func() {
name = helper.RandString(6)
svcFullName = strings.Join([]string{"EtcdCluster", name}, "/")
svcFullName = strings.Join([]string{"Redis", name}, "/")
helper.Cmd("odo", "service", "create", "--from-file", fileName, name, "--project", commonVar.Project).ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
@@ -246,15 +244,15 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
})
It("should create pods in running state", func() {
oc.PodsShouldBeRunning(commonVar.Project, name+`-.[a-z0-9]*`)
commonVar.CliRunner.PodsShouldBeRunning(commonVar.Project, name+`-.[a-z0-9-]*`)
})
})
})
When("an EtcdCluster instance is created with no name", func() {
When("an Redis instance is created with no name", func() {
var stdOut string
BeforeEach(func() {
stdOut = helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/EtcdCluster", etcdOperator), "--project", commonVar.Project).ShouldPass().Out()
stdOut = helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/Redis", redisOperator), "--project", commonVar.Project).ShouldPass().Out()
Expect(stdOut).To(ContainSubstring("Successfully added service to the configuration"))
})
@@ -262,7 +260,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
devfilePath := filepath.Join(commonVar.Context, "devfile.yaml")
content, err := ioutil.ReadFile(devfilePath)
Expect(err).To(BeNil())
matchInOutput := []string{"kubernetes", "inlined", "EtcdCluster", "etcdcluster"}
matchInOutput := []string{"kubernetes", "inlined", "Redis", "redis"}
helper.MatchAllInOutput(string(content), matchInOutput)
})
@@ -273,58 +271,52 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
})
It("should create pods in running state", func() {
oc.PodsShouldBeRunning(commonVar.Project, `etcdcluster-.[a-z0-9]*`)
commonVar.CliRunner.PodsShouldBeRunning(commonVar.Project, `redis.[a-z0-9-]*`)
})
It("should list the service", func() {
// now test listing of the service using odo
stdOut := helper.Cmd("odo", "service", "list").ShouldPass().Out()
Expect(stdOut).To(ContainSubstring("EtcdCluster/etcdcluster"))
Expect(stdOut).To(ContainSubstring("Redis/redis"))
})
It("should list the service in JSON format", func() {
jsonOut := helper.Cmd("odo", "service", "list", "-o", "json").ShouldPass().Out()
helper.MatchAllInOutput(jsonOut, []string{"\"apiVersion\": \"etcd.database.coreos.com/v1beta2\"", "\"kind\": \"EtcdCluster\"", "\"name\": \"etcdcluster\""})
helper.MatchAllInOutput(jsonOut, []string{"\"apiVersion\": \"redis.redis.opstreelabs.in/v1beta1\"", "\"kind\": \"Redis\"", "\"name\": \"redis\""})
})
When("a link is created with the service", func() {
var stdOut string
BeforeEach(func() {
stdOut = helper.Cmd("odo", "link", "EtcdCluster/etcdcluster").ShouldPass().Out()
})
It("should display a successful message", func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
stdOut = helper.Cmd("odo", "link", "Redis/redis").ShouldPass().Out()
})
It("should display a successful message", func() {
Expect(stdOut).To(ContainSubstring("Successfully created link between component"))
})
It("Should fail to link it again", func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
stdOut = helper.Cmd("odo", "link", "EtcdCluster/etcdcluster").ShouldFail().Err()
stdOut = helper.Cmd("odo", "link", "Redis/redis").ShouldFail().Err()
Expect(stdOut).To(ContainSubstring("already linked with the service"))
})
When("the link is deleted", func() {
BeforeEach(func() {
stdOut = helper.Cmd("odo", "unlink", "EtcdCluster/etcdcluster").ShouldPass().Out()
})
It("should display a successful message", func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
stdOut = helper.Cmd("odo", "unlink", "Redis/redis").ShouldPass().Out()
})
It("should display a successful message", func() {
Expect(stdOut).To(ContainSubstring("Successfully unlinked component"))
})
It("should fail to delete it again", func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
stdOut = helper.Cmd("odo", "unlink", "EtcdCluster/etcdcluster").ShouldFail().Err()
stdOut = helper.Cmd("odo", "unlink", "Redis/redis").ShouldFail().Err()
Expect(stdOut).To(ContainSubstring("failed to unlink the service"))
})
})
@@ -332,7 +324,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
When("the service is deleted", func() {
BeforeEach(func() {
helper.Cmd("odo", "service", "delete", "EtcdCluster/etcdcluster", "-f").ShouldPass()
helper.Cmd("odo", "service", "delete", "Redis/redis", "-f").ShouldPass()
})
It("should delete service definition from devfile.yaml", func() {
@@ -340,12 +332,12 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
devfilePath := filepath.Join(commonVar.Context, "devfile.yaml")
content, err := ioutil.ReadFile(devfilePath)
Expect(err).To(BeNil())
matchInOutput := []string{"kubernetes", "inlined", "EtcdCluster", "etcdcluster"}
matchInOutput := []string{"kubernetes", "inlined", "Redis", "redis"}
helper.DontMatchAllInOutput(string(content), matchInOutput)
})
It("should fail to delete the service again", func() {
stdOut = helper.Cmd("odo", "service", "delete", "EtcdCluster/etcdcluster", "-f").ShouldFail().Err()
stdOut = helper.Cmd("odo", "service", "delete", "Redis/redis", "-f").ShouldFail().Err()
Expect(stdOut).To(ContainSubstring("couldn't find service named"))
})
@@ -371,7 +363,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
When("a second service is created and odo push is executed", func() {
BeforeEach(func() {
stdOut = helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/EtcdCluster", etcdOperator), "myetcd2", "--project", commonVar.Project).ShouldPass().Out()
stdOut = helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/Redis", redisOperator), "myredis2", "--project", commonVar.Project).ShouldPass().Out()
Expect(stdOut).To(ContainSubstring("Successfully added service to the configuration"))
helper.Cmd("odo", "push").ShouldPass()
})
@@ -379,23 +371,23 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
It("should list both services", func() {
stdOut = helper.Cmd("odo", "service", "list").ShouldPass().Out()
// first service still here
Expect(stdOut).To(ContainSubstring("EtcdCluster/etcdcluster"))
Expect(stdOut).To(ContainSubstring("Redis/redis"))
// second service created
Expect(stdOut).To(ContainSubstring("EtcdCluster/myetcd2"))
Expect(stdOut).To(ContainSubstring("Redis/myredis2"))
})
})
})
})
When("an EtcdCluster instance is created with a specific name", func() {
When("an Redis instance is created with a specific name", func() {
var name string
var svcFullName string
BeforeEach(func() {
name = helper.RandString(6)
svcFullName = strings.Join([]string{"EtcdCluster", name}, "/")
helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/EtcdCluster", etcdOperator), name, "--project", commonVar.Project).ShouldPass()
svcFullName = strings.Join([]string{"Redis", name}, "/")
helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/Redis", redisOperator), name, "--project", commonVar.Project).ShouldPass()
})
AfterEach(func() {
@@ -414,11 +406,11 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
})
It("should create pods in running state", func() {
oc.PodsShouldBeRunning(commonVar.Project, name+`-.[a-z0-9]*`)
commonVar.CliRunner.PodsShouldBeRunning(commonVar.Project, name+`-.[a-z0-9-]*`)
})
It("should fail to create a service again with the same name", func() {
stdOut := helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/EtcdCluster", etcdOperator), name, "--project", commonVar.Project).ShouldFail().Err()
stdOut := helper.Cmd("odo", "service", "create", fmt.Sprintf("%s/Redis", redisOperator), name, "--project", commonVar.Project).ShouldFail().Err()
Expect(stdOut).To(ContainSubstring(fmt.Sprintf("service %q already exists", svcFullName)))
})
@@ -427,7 +419,7 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
helper.MatchAllInOutput(stdOut, []string{svcFullName, "Pushed"})
})
When("the etcdCluster instance is deleted", func() {
When("the redisCluster instance is deleted", func() {
BeforeEach(func() {
helper.Cmd("odo", "service", "delete", svcFullName, "-f").ShouldPass()
})
@@ -455,19 +447,19 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
BeforeEach(func() {
linkName = "link-" + helper.RandString(6)
helper.Cmd("odo", "link", "EtcdCluster/"+name, "--name", linkName).ShouldPass()
helper.Cmd("odo", "link", "Redis/"+name, "--name", linkName).ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
AfterEach(func() {
// delete the link
helper.Cmd("odo", "unlink", "EtcdCluster/"+name).ShouldPass()
helper.Cmd("odo", "unlink", "Redis/"+name).ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
It("should create the link with the specified name", func() {
ocArgs := []string{"get", "servicebinding", linkName, "-n", commonVar.Project}
helper.WaitForCmdOut("oc", ocArgs, 1, true, func(output string) bool {
args := []string{"get", "servicebinding", linkName, "-n", commonVar.Project}
commonVar.CliRunner.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return strings.Contains(output, linkName)
})
})
@@ -479,19 +471,19 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
BeforeEach(func() {
linkName = "link-" + helper.RandString(6)
helper.Cmd("odo", "link", "EtcdCluster/"+name, "--name", linkName, "--bind-as-files").ShouldPass()
helper.Cmd("odo", "link", "Redis/"+name, "--name", linkName, "--bind-as-files").ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
AfterEach(func() {
// delete the link
helper.Cmd("odo", "unlink", "EtcdCluster/"+name).ShouldPass()
helper.Cmd("odo", "unlink", "Redis/"+name).ShouldPass()
helper.Cmd("odo", "push").ShouldPass()
})
It("should create a servicebinding resource with bindAsFiles set to true", func() {
ocArgs := []string{"get", "servicebinding", linkName, "-o", "jsonpath='{.spec.bindAsFiles}'", "-n", commonVar.Project}
helper.WaitForCmdOut("oc", ocArgs, 1, true, func(output string) bool {
args := []string{"get", "servicebinding", linkName, "-o", "jsonpath='{.spec.bindAsFiles}'", "-n", commonVar.Project}
commonVar.CliRunner.WaitForRunnerCmdOut(args, 1, true, func(output string) bool {
return strings.Contains(output, "true")
})
})
@@ -510,11 +502,9 @@ var _ = Describe("odo service command tests for OperatorHub", func() {
// TODO write helpers to create such files
noMetadata := `
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdCluster
spec:
size: 3
version: 3.2.13`
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: Redis
spec:`
noMetaFile := helper.RandString(6) + ".yaml"
noMetaFileName = filepath.Join(tmpContext, noMetaFile)
if err := ioutil.WriteFile(noMetaFileName, []byte(noMetadata), 0644); err != nil {
@@ -522,13 +512,11 @@ spec:
}
invalidMetadata := `
apiVersion: etcd.database.coreos.com/v1beta2
kind: EtcdCluster
apiVersion: redis.redis.opstreelabs.in/v1beta1
kind: Redis
metadata:
noname: noname
spec:
size: 3
version: 3.2.13`
spec:`
invalidMetaFile := helper.RandString(6) + ".yaml"
invalidFileName = filepath.Join(tmpContext, invalidMetaFile)
if err := ioutil.WriteFile(invalidFileName, []byte(invalidMetadata), 0644); err != nil {
@@ -581,12 +569,9 @@ spec:
})
It("should fail if the component doesn't exist and the service name doesn't adhere to the <service-type>/<service-name> format", func() {
if os.Getenv("KUBERNETES") == "true" {
Skip("This is a OpenShift specific scenario, skipping")
}
helper.Cmd("odo", "link", "EtcdCluster").ShouldFail()
helper.Cmd("odo", "link", "EtcdCluster/").ShouldFail()
helper.Cmd("odo", "link", "/example").ShouldFail()
helper.Cmd("odo", "link", "Redis").ShouldFail()
helper.Cmd("odo", "link", "Redis/").ShouldFail()
helper.Cmd("odo", "link", "/redis-standalone").ShouldFail()
})
When("another component is deployed", func() {