Skip to content
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

Implement e2e dualStack control plane tests #373

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tests/e2e/common-operator-integ-suite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ fi

# Run the go test passing the env variables defined that are going to be used in the operator tests
# shellcheck disable=SC2086
IMAGE="${HUB}/${IMAGE_BASE}:${TAG}" SKIP_DEPLOY="${SKIP_DEPLOY}" OCP="${OCP}" ISTIO_MANIFEST="${ISTIO_MANIFEST}" \
IMAGE="${HUB}/${IMAGE_BASE}:${TAG}" SKIP_DEPLOY="${SKIP_DEPLOY}" OCP="${OCP}" IP_FAMILY="${IP_FAMILY}" ISTIO_MANIFEST="${ISTIO_MANIFEST}" \
NAMESPACE="${NAMESPACE}" CONTROL_PLANE_NS="${CONTROL_PLANE_NS}" DEPLOYMENT_NAME="${DEPLOYMENT_NAME}" MULTICLUSTER="${MULTICLUSTER}" ARTIFACTS="${ARTIFACTS}" \
ISTIO_NAME="${ISTIO_NAME}" COMMAND="${COMMAND}" VERSIONS_YAML_FILE="${VERSIONS_YAML_FILE}" KUBECONFIG="${KUBECONFIG}" ISTIOCTL_PATH="${ISTIOCTL}" \
go run github.com/onsi/ginkgo/v2/ginkgo -tags e2e --timeout 30m --junit-report=report.xml ${GINKGO_FLAGS} "${WD}"/...
65 changes: 65 additions & 0 deletions tests/e2e/dualstack/dualstack_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//go:build e2e

// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dualstack

import (
"testing"

k8sclient "github.com/istio-ecosystem/sail-operator/tests/e2e/util/client"
env "github.com/istio-ecosystem/sail-operator/tests/e2e/util/env"
"github.com/istio-ecosystem/sail-operator/tests/e2e/util/kubectl"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var (
cl client.Client
err error
ocp = env.GetBool("OCP", false)
namespace = env.Get("NAMESPACE", "sail-operator")
deploymentName = env.Get("DEPLOYMENT_NAME", "sail-operator")
controlPlaneNamespace = env.Get("CONTROL_PLANE_NS", "istio-system")
istioName = env.Get("ISTIO_NAME", "default")
image = env.Get("IMAGE", "quay.io/maistra-dev/sail-operator:latest")
skipDeploy = env.GetBool("SKIP_DEPLOY", false)
expectedRegistry = env.Get("EXPECTED_REGISTRY", "^docker\\.io|^gcr\\.io")
multicluster = env.GetBool("MULTICLUSTER", false)
ipFamily = env.Get("IP_FAMILY", "ipv4")

k *kubectl.KubectlBuilder
)

func TestDualStack(t *testing.T) {
if ipFamily != "dual" || multicluster {
sridhargaddam marked this conversation as resolved.
Show resolved Hide resolved
t.Skip("Skipping the dualStack tests")
}

RegisterFailHandler(Fail)
setup()
RunSpecs(t, "DualStack test suite")
}

func setup() {
GinkgoWriter.Println("************ Running Setup ************")

GinkgoWriter.Println("Initializing k8s client")
cl, err = k8sclient.InitK8sClient("")
Expect(err).NotTo(HaveOccurred())

k = kubectl.NewKubectlBuilder()
}
213 changes: 213 additions & 0 deletions tests/e2e/dualstack/dualstack_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
//go:build e2e

// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR Condition OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dualstack

import (
"fmt"
"path/filepath"
"time"

"github.com/istio-ecosystem/sail-operator/api/v1alpha1"
"github.com/istio-ecosystem/sail-operator/pkg/kube"
"github.com/istio-ecosystem/sail-operator/pkg/test/project"
. "github.com/istio-ecosystem/sail-operator/pkg/test/util/ginkgo"
"github.com/istio-ecosystem/sail-operator/pkg/test/util/supportedversion"
common "github.com/istio-ecosystem/sail-operator/tests/e2e/util/common"
. "github.com/istio-ecosystem/sail-operator/tests/e2e/util/gomega"
"github.com/istio-ecosystem/sail-operator/tests/e2e/util/helm"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/types"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe("DualStack configuration ", Ordered, func() {
SetDefaultEventuallyTimeout(180 * time.Second)
SetDefaultEventuallyPollingInterval(time.Second)

debugInfoLogged := false

BeforeAll(func(ctx SpecContext) {
Expect(k.CreateNamespace(namespace)).To(Succeed(), "Namespace failed to be created")

extraArg := ""
if ocp {
extraArg = "--set=platform=openshift"
}

if skipDeploy {
Success("Skipping operator installation because it was deployed externally")
} else {
Expect(helm.Install("sail-operator", filepath.Join(project.RootDir, "chart"), "--namespace "+namespace, "--set=image="+image, extraArg)).
To(Succeed(), "Operator failed to be deployed")
}

Eventually(common.GetObject).WithArguments(ctx, cl, kube.Key(deploymentName, namespace), &appsv1.Deployment{}).
Should(HaveCondition(appsv1.DeploymentAvailable, metav1.ConditionTrue), "Error getting Istio CRD")
Success("Operator is deployed in the namespace and Running")
})

Describe("for supported versions", func() {
for _, version := range supportedversion.List {
// Note: This var version is needed to avoid the closure of the loop
version := version

// The minimum supported version is 1.23 (and above)
if version.Major == 1 && version.Minor < 23 {
continue
}

Context("Istio version is: "+version.Version, func() {
BeforeAll(func() {
Expect(k.CreateNamespace(controlPlaneNamespace)).To(Succeed(), "Istio namespace failed to be created")
})

When("the Istio CR is created with DualStack configuration", func() {
BeforeAll(func() {
istioYAML := `
apiVersion: sailoperator.io/v1alpha1
kind: Istio
metadata:
name: default
spec:
values:
meshConfig:
defaultConfig:
proxyMetadata:
ISTIO_DUAL_STACK: "true"
pilot:
ipFamilyPolicy: %s
env:
ISTIO_DUAL_STACK: "true"
version: %s
namespace: %s`
istioYAML = fmt.Sprintf(istioYAML, corev1.IPFamilyPolicyRequireDualStack, version.Name, controlPlaneNamespace)
Log("Istio YAML:", istioYAML)
Expect(k.CreateFromString(istioYAML)).
To(Succeed(), "Istio CR failed to be created")
Success("Istio CR created")
})

It("updates the Istio CR status to Reconciled", func(ctx SpecContext) {
Eventually(common.GetObject).WithArguments(ctx, cl, kube.Key(istioName), &v1alpha1.Istio{}).
Should(HaveCondition(v1alpha1.IstioConditionReconciled, metav1.ConditionTrue), "Istio is not Reconciled; unexpected Condition")
Success("Istio CR is Reconciled")
})

It("updates the Istio CR status to Ready", func(ctx SpecContext) {
Eventually(common.GetObject).WithArguments(ctx, cl, kube.Key(istioName), &v1alpha1.Istio{}).
Should(HaveCondition(v1alpha1.IstioConditionReady, metav1.ConditionTrue), "Istio is not Ready; unexpected Condition")
Success("Istio CR is Ready")
})

It("deploys istiod", func(ctx SpecContext) {
Eventually(common.GetObject).WithArguments(ctx, cl, kube.Key("istiod", controlPlaneNamespace), &appsv1.Deployment{}).
Should(HaveCondition(appsv1.DeploymentAvailable, metav1.ConditionTrue), "Istiod is not Available; unexpected Condition")
Expect(common.GetVersionFromIstiod()).To(Equal(version.Version), "Unexpected istiod version")
Success("Istiod is deployed in the namespace and Running")
})

It("uses the correct image", func(ctx SpecContext) {
Expect(common.GetObject(ctx, cl, kube.Key("istiod", controlPlaneNamespace), &appsv1.Deployment{})).
To(HaveContainersThat(HaveEach(ImageFromRegistry(expectedRegistry))))
})

It("has ISTIO_DUAL_STACK env variable set", func(ctx SpecContext) {
Expect(common.GetObject(ctx, cl, kube.Key("istiod", controlPlaneNamespace), &appsv1.Deployment{})).
To(HaveContainersThat(ContainElement(WithTransform(getEnvVars, ContainElement(corev1.EnvVar{Name: "ISTIO_DUAL_STACK", Value: "true"})))),
"Expected ISTIO_DUAL_STACK to be set to true, but not found")
})

It("deploys istiod service in dualStack mode", func(ctx SpecContext) {
var istiodSvcObj corev1.Service

Eventually(func() error {
_, err := common.GetObject(ctx, cl, kube.Key("istiod", controlPlaneNamespace), &istiodSvcObj)
return err
}).Should(Succeed(), "Expected to retrieve the 'istiod' service")

Expect(istiodSvcObj.Spec.IPFamilyPolicy).ToNot(BeNil(), "Expected IPFamilyPolicy to be set")
Expect(*istiodSvcObj.Spec.IPFamilyPolicy).To(Equal(corev1.IPFamilyPolicyRequireDualStack), "Expected ipFamilyPolicy to be 'RequireDualStack'")
Success("Istio Service is deployed in the namespace and Running")
})
})

When("the Istio CR is deleted", func() {
BeforeEach(func() {
Expect(k.SetNamespace(controlPlaneNamespace).Delete("istio", istioName)).To(Succeed(), "Istio CR failed to be deleted")
Success("Istio CR deleted")
})

It("removes everything from the namespace", func(ctx SpecContext) {
Eventually(cl.Get).WithArguments(ctx, kube.Key("istiod", controlPlaneNamespace), &appsv1.Deployment{}).
Should(ReturnNotFoundError(), "Istiod should not exist anymore")
common.CheckNamespaceEmpty(ctx, cl, controlPlaneNamespace)
Success("Namespace is empty")
})
})
})
}

AfterAll(func(ctx SpecContext) {
if CurrentSpecReport().Failed() {
common.LogDebugInfo()
sridhargaddam marked this conversation as resolved.
Show resolved Hide resolved
debugInfoLogged = true
}

By("Cleaning up the Istio namespace")
Expect(cl.Delete(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: controlPlaneNamespace}})).To(Succeed(), "Istio Namespace failed to be deleted")

By("Deleting any left-over Istio and IstioRevision resources")
Success("Resources deleted")
Success("Cleanup done")
})
})

AfterAll(func() {
if CurrentSpecReport().Failed() && !debugInfoLogged {
common.LogDebugInfo()
debugInfoLogged = true
}

if skipDeploy {
Success("Skipping operator undeploy because it was deployed externally")
return
}

By("Deleting operator deployment")
Expect(helm.Uninstall("sail-operator", "--namespace "+namespace)).
To(Succeed(), "Operator failed to be deleted")
GinkgoWriter.Println("Operator uninstalled")

Expect(k.DeleteNamespace(namespace)).To(Succeed(), "Namespace failed to be deleted")
Success("Namespace deleted")
})
})

func HaveContainersThat(matcher types.GomegaMatcher) types.GomegaMatcher {
return HaveField("Spec.Template.Spec.Containers", matcher)
}

func ImageFromRegistry(regexp string) types.GomegaMatcher {
return HaveField("Image", MatchRegexp(regexp))
}

func getEnvVars(container corev1.Container) []corev1.EnvVar {
return container.Env
}
2 changes: 1 addition & 1 deletion tests/e2e/integ-suite-kind.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,5 @@ echo "Running integration tests"
if [ "${MULTICLUSTER}" == "true" ]; then
ARTIFACTS="${ARTIFACTS}" ISTIOCTL="${ISTIOCTL}" ./tests/e2e/common-operator-integ-suite.sh --kind --multicluster
else
ARTIFACTS="${ARTIFACTS}" ./tests/e2e/common-operator-integ-suite.sh --kind
ARTIFACTS="${ARTIFACTS}" IP_FAMILY="${IP_FAMILY}" ./tests/e2e/common-operator-integ-suite.sh --kind
fi