-
Notifications
You must be signed in to change notification settings - Fork 238
Revamp the integration tests #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| #/bin/bash | ||
| set -euo pipefail | ||
|
|
||
| REPO="${REPO:-}" | ||
| MANIFESTS="${MANIFESTS:-}" | ||
|
|
||
| if [ -z "$REPO" ]; then echo "REPO is required"; exit 1; fi | ||
| if [ -z "$MANIFESTS" ]; then echo "MANIFESTS is required"; exit 1; fi | ||
|
|
||
| TEMP_COMMIT="false" | ||
| test -z "$(git status --porcelain)" || TEMP_COMMIT="true" | ||
|
|
||
| if [[ "${TEMP_COMMIT}" == "true" ]]; then | ||
| git add . | ||
| git commit -m "Temporary" || true | ||
| fi | ||
|
|
||
| REV=$(git rev-parse --short HEAD) | ||
| docker build -t $REPO:$REV -f images/cluster-ingress-operator/Dockerfile . | ||
| docker push $REPO:$REV | ||
|
|
||
| if [[ "${TEMP_COMMIT}" == "true" ]]; then | ||
| git reset --soft HEAD~1 | ||
| fi | ||
|
|
||
| cp -R manifests/ $MANIFESTS | ||
| cat manifests/02-deployment.yaml | sed "s~openshift/origin-cluster-ingress-operator:latest~$REPO:$REV~" > "$MANIFESTS/02-deployment.yaml" | ||
|
|
||
| echo "Pushed $REPO:$REV" | ||
| echo "Install manifests using:" | ||
| echo "" | ||
| echo "oc apply -f $MANIFESTS" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think -f does the recursion. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
|
|
||
| CLUSTER_NAME="${CLUSTER_NAME:-}" | ||
| MANIFESTS="${MANIFESTS:-}" | ||
|
|
||
| if [ -z "${CLUSTER_NAME}" ]; then echo "CLUSTER_NAME is required"; exit 1; fi | ||
| if [ -z "${MANIFESTS}" ]; then echo "MANIFESTS is required"; exit 1; fi | ||
|
|
||
| export WATCH_NAMESPACE="openshift-cluster-ingress-operator" | ||
| export KUBERNETES_CONFIG="${KUBECONFIG}" | ||
| go test -v -tags integration ./test/integration --manifests-dir "${MANIFESTS}" --cluster-name "${CLUSTER_NAME}" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,244 +3,106 @@ | |
| package integration | ||
|
|
||
| import ( | ||
| "context" | ||
| "flag" | ||
| "fmt" | ||
| "net/http" | ||
| "os" | ||
| "os/exec" | ||
| "testing" | ||
| "time" | ||
|
|
||
| stub "github.com/openshift/cluster-ingress-operator/pkg/stub" | ||
| "github.com/openshift/cluster-ingress-operator/test/manifests" | ||
|
|
||
| sdk "github.com/operator-framework/operator-sdk/pkg/sdk" | ||
| k8sutil "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" | ||
| "k8s.io/apimachinery/pkg/api/errors" | ||
| "k8s.io/apimachinery/pkg/util/wait" | ||
| "github.com/sirupsen/logrus" | ||
| ) | ||
|
|
||
| var testConfig *TestConfig | ||
|
|
||
| func TestIntegration(t *testing.T) { | ||
| testConfig = NewTestConfig(t) | ||
|
|
||
| testConfig.createCRD() | ||
| defer testConfig.deleteCRD() | ||
|
|
||
| testConfig.startOperator() | ||
| var clusterName = flag.String("cluster-name", "", "cluster name") | ||
| var manifestsDir = flag.String("manifests-dir", "", "manifests directory") | ||
|
|
||
| // Execute subtests | ||
| t.Run("TestMultipleIngresses", testMultipleIngresses) | ||
| func TestMain(m *testing.M) { | ||
| flag.Parse() | ||
| os.Exit(m.Run()) | ||
| } | ||
|
|
||
| func testMultipleIngresses(t *testing.T) { | ||
| f := manifests.NewFactory(testConfig.clusterName) | ||
|
|
||
| appNamespace, err := f.AppIngressNamespace() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| appDeployment, err := f.AppIngressDeployment() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| appService, err := f.AppIngressService() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| appRouteDefault, err := f.AppIngressRouteDefault() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| appRouteInternal, err := f.AppIngressRouteInternal() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| clusterIngressDefault, err := f.ClusterIngressDefault() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| clusterIngressDefault.Namespace = testConfig.operatorNamespace | ||
|
|
||
| clusterIngressInternal, err := f.ClusterIngressInternal() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| clusterIngressInternal.Namespace = testConfig.operatorNamespace | ||
|
|
||
| routerNamespace, err := f.RouterNamespace() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| defaultService, err := f.RouterServiceCloud(clusterIngressDefault) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| internalService, err := f.RouterServiceCloud(clusterIngressInternal) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| cleanup := func() { | ||
| leftovers := []sdk.Object{ | ||
| clusterIngressDefault, | ||
| clusterIngressInternal, | ||
| routerNamespace, | ||
| appNamespace, | ||
| } | ||
| anyFailed := false | ||
| for _, o := range leftovers { | ||
| err := sdk.Delete(o) | ||
| if err != nil && !errors.IsNotFound(err) { | ||
| t.Logf("failed to clean up object %#v: %s", o, err) | ||
| anyFailed = true | ||
| } | ||
| } | ||
| if anyFailed { | ||
| t.Fatalf("failed to clean up resources") | ||
| } | ||
| } | ||
| defer cleanup() | ||
|
|
||
| err = sdk.Create(appNamespace) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| err = sdk.Create(appDeployment) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| err = sdk.Create(appService) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| err = sdk.Create(appRouteDefault) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| err = sdk.Create(appRouteInternal) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| err = sdk.Create(clusterIngressDefault) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| err = sdk.Create(clusterIngressInternal) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| func TestIntegration(t *testing.T) { | ||
| tc := NewTestConfig(t, *clusterName, *manifestsDir) | ||
|
|
||
| for _, service := range []*corev1.Service{defaultService, internalService} { | ||
| err := wait.Poll(1*time.Second, 2*time.Minute, func() (bool, error) { | ||
| err := sdk.Get(service) | ||
| if err != nil { | ||
| if errors.IsNotFound(err) { | ||
| return false, nil | ||
| } | ||
| return false, err | ||
| } | ||
| for _, ingress := range service.Status.LoadBalancer.Ingress { | ||
| if len(ingress.IP) > 0 { | ||
| t.Logf("service %s/%s has ingress.IP %s", service.Namespace, service.Name, ingress.IP) | ||
| return true, nil | ||
| } | ||
| } | ||
| return false, nil | ||
| }) | ||
| if err != nil { | ||
| t.Fatalf("timed out waiting for service %s/%s: %s", service.Namespace, service.Name, err) | ||
| } | ||
| } | ||
| tc.setup(t) | ||
| defer tc.teardown(t) | ||
|
|
||
| client := &http.Client{} | ||
| for routeHost, ingressIP := range map[string]string{ | ||
| appRouteDefault.Spec.Host: defaultService.Status.LoadBalancer.Ingress[0].IP, | ||
| appRouteInternal.Spec.Host: internalService.Status.LoadBalancer.Ingress[0].IP, | ||
| } { | ||
| err := wait.Poll(1*time.Second, 2*time.Minute, func() (bool, error) { | ||
| req, err := http.NewRequest("GET", "http://"+ingressIP, nil) | ||
| req.Host = routeHost | ||
| resp, err := client.Do(req) | ||
| defer resp.Body.Close() | ||
| if err != nil { | ||
| return false, err | ||
| } | ||
| if resp.StatusCode == http.StatusOK { | ||
| return true, nil | ||
| } | ||
| return false, fmt.Errorf("last response: %s", resp.Status) | ||
| }) | ||
| if err != nil { | ||
| t.Fatalf("timed out waiting for route endpoint %q at ingress IP %q: %s", routeHost, ingressIP, err) | ||
| } | ||
| } | ||
| // Execute subtests | ||
| t.Run("TestDefaultIngress", func(t *testing.T) { testDefaultIngress(t, tc) }) | ||
| } | ||
|
|
||
| type TestConfig struct { | ||
| operatorNamespace string | ||
| clusterName string | ||
| manifestsDir string | ||
| kubeConfig string | ||
|
|
||
| t *testing.T | ||
| } | ||
|
|
||
| func NewTestConfig(t *testing.T) *TestConfig { | ||
| config := &TestConfig{t: t} | ||
|
|
||
| func NewTestConfig(t *testing.T, clusterName string, manifestsDir string) *TestConfig { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a little confusing to use the same names for the parameters as you used for the global variables for the flags.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redid all this |
||
| // Check prerequisites | ||
| kubeConfig := os.Getenv("KUBECONFIG") | ||
| if len(kubeConfig) == 0 { | ||
| t.Fatalf("KUBECONFIG is required") | ||
| } | ||
| // The operator-sdk uses KUBERNETES_CONFIG... | ||
| os.Setenv("KUBERNETES_CONFIG", kubeConfig) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gone |
||
| config.kubeConfig = kubeConfig | ||
|
|
||
| clusterName := os.Getenv("CLUSTER_NAME") | ||
| if len(clusterName) == 0 { | ||
| t.Fatalf("CLUSTER_NAME is required") | ||
| t.Fatalf("cluster name is required") | ||
| } | ||
|
|
||
| if len(manifestsDir) == 0 { | ||
| t.Fatalf("manifests directory is required") | ||
| } | ||
| config.clusterName = clusterName | ||
|
|
||
| namespace, err := k8sutil.GetWatchNamespace() | ||
| if err != nil { | ||
| namespace = "default" | ||
| os.Setenv("WATCH_NAMESPACE", namespace) | ||
| return &TestConfig{ | ||
| clusterName: clusterName, | ||
| kubeConfig: kubeConfig, | ||
| manifestsDir: manifestsDir, | ||
| operatorNamespace: "openshift-cluster-ingress-operator", | ||
| } | ||
| config.operatorNamespace = namespace | ||
| } | ||
|
|
||
| func (tc *TestConfig) setup(t *testing.T) { | ||
| // uninstall the CVO | ||
| tc.runShellCmdNonFatal(t, `oc patch -n openshift-cluster-version daemonsets/cluster-version-operator --patch '{"spec": {"template": {"spec": {"nodeSelector": {"node-role.kubernetes.io/fake": ""}}}}}'`) | ||
|
|
||
| // uninstall tectonic-ingress | ||
| tc.runShellCmdNonFatal(t, `oc delete namespaces/openshift-ingress`) | ||
|
|
||
| // uninstall any existing operator | ||
| tc.uninstallOperator(t) | ||
|
|
||
| return config | ||
| // reinstall the operator | ||
| tc.runShellCmdNonFatal(t, fmt.Sprintf(`oc apply -f %s`, tc.manifestsDir)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing |
||
| } | ||
|
|
||
| func (tc *TestConfig) startOperator() { | ||
| resource := "ingress.openshift.io/v1alpha1" | ||
| kind := "ClusterIngress" | ||
| resyncPeriod := 10 * time.Minute | ||
| tc.t.Logf("Watching %s, %s, %s, %d", resource, kind, tc.operatorNamespace, resyncPeriod) | ||
| sdk.Watch(resource, kind, tc.operatorNamespace, resyncPeriod) | ||
| sdk.Handle(stub.NewHandler()) | ||
| go sdk.Run(context.TODO()) | ||
| func (tc *TestConfig) uninstallOperator(t *testing.T) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should all this teardown stuff go in a script so it can be called outside the code? That's what I do for manual testing anyway |
||
| tc.runShellCmdNonFatal(t, `oc delete -n openshift-cluster-ingress-operator --force --grace-period=0 clusteringresses/default`) | ||
| tc.runShellCmdNonFatal(t, `oc delete namespaces/openshift-cluster-ingress-operator`) | ||
| tc.runShellCmdNonFatal(t, `oc delete namespaces/openshift-cluster-ingress-router`) | ||
| tc.runShellCmdNonFatal(t, `oc delete clusterroles/cluster-ingress-operator:operator`) | ||
| tc.runShellCmdNonFatal(t, `oc delete clusterroles/cluster-ingress:router`) | ||
| tc.runShellCmdNonFatal(t, `oc delete clusterrolebindings/cluster-ingress-operator:operator`) | ||
| tc.runShellCmdNonFatal(t, `oc delete clusterrolebindings/cluster-ingress:router`) | ||
| tc.runShellCmdNonFatal(t, `oc delete customresourcedefinition.apiextensions.k8s.io/clusteringresses.ingress.openshift.io`) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you replace all the individual deletes with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extracted all this into a script
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure about using
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's not a big deal I'd ask we keep what I have in the script for now (which works) and optimize it later if possible. |
||
| } | ||
|
|
||
| func (tc *TestConfig) createCRD() { | ||
| tc.runShellCmd(fmt.Sprintf("oc apply -f ../../manifests/00-custom-resource-definition.yaml -n %s", tc.operatorNamespace), "create cluster ingress CRD") | ||
| func (tc *TestConfig) teardown(t *testing.T) { | ||
| tc.uninstallOperator(t) | ||
| } | ||
|
|
||
| func (tc *TestConfig) deleteCRD() { | ||
| tc.runShellCmd(fmt.Sprintf("oc delete crd clusteringresses.ingress.openshift.io -n %s", tc.operatorNamespace), "delete cluster ingress CRD") | ||
| func (tc *TestConfig) runShellCmdNonFatal(t *testing.T, command string) { | ||
| tc.runShellCmd(t, command, "", false) | ||
| } | ||
|
|
||
| func (tc *TestConfig) runShellCmd(command, msg string) { | ||
| func (tc *TestConfig) runShellCmd(t *testing.T, command string, msg string, failOnError bool) { | ||
| cmd := []string{"sh", "-c", command} | ||
| c := exec.Command(cmd[0], cmd[1:]...) | ||
| c.Env = os.Environ() | ||
| c.Env = append(c.Env, fmt.Sprintf("KUBECONFIG=%s", tc.kubeConfig)) | ||
| if err := c.Run(); err != nil { | ||
| tc.t.Fatalf("failed to %s: %v", msg, err) | ||
| output, err := c.CombinedOutput() | ||
| if err != nil && failOnError { | ||
| t.Fatalf("failed to %s: %v", msg, err) | ||
| } | ||
| logrus.Infof("cmd output: %s", output) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing a
!.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed