diff --git a/test/config/gatewayclass.yaml b/test/config/gatewayclass.yaml index fa07a159305..4349320cf1c 100644 --- a/test/config/gatewayclass.yaml +++ b/test/config/gatewayclass.yaml @@ -165,4 +165,38 @@ metadata: spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- +kind: GatewayClass +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: lifecycle +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parametersRef: + group: gateway.envoyproxy.io + kind: EnvoyProxy + name: lifecycle-config + namespace: envoy-gateway-system +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: lifecycle-config + namespace: envoy-gateway-system +spec: + provider: + type: Kubernetes + kubernetes: + envoyDeployment: + replicas: 2 + patch: + type: StrategicMerge + value: + spec: + template: + spec: + containers: + - name: envoy + readinessProbe: + initialDelaySeconds: 5 +--- diff --git a/test/e2e/base/manifests.yaml b/test/e2e/base/manifests.yaml index 3bba21844a0..35d76358bca 100644 --- a/test/e2e/base/manifests.yaml +++ b/test/e2e/base/manifests.yaml @@ -666,3 +666,74 @@ spec: protocol: TCP port: 19001 targetPort: 19001 +--- +apiVersion: v1 +kind: Namespace +metadata: + name: gateway-lifecycle-infra +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: lifecycle-gateway + namespace: gateway-lifecycle-infra +spec: + gatewayClassName: lifecycle + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http1 + port: 80 + protocol: HTTP +--- +apiVersion: v1 +kind: Service +metadata: + name: infra-backend + namespace: gateway-lifecycle-infra +spec: + selector: + app: infra-backend + ports: + - protocol: TCP + port: 8080 + targetPort: 3000 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: infra-backend + namespace: gateway-lifecycle-infra + labels: + app: infra-backend +spec: + replicas: 2 + selector: + matchLabels: + app: infra-backend + template: + metadata: + labels: + app: infra-backend + spec: + containers: + - name: infra-backend + # From https://github.com/kubernetes-sigs/gateway-api/blob/main/conformance/echo-basic/echo-basic.go + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: infra-backend + resources: + requests: + cpu: 10m + +--- diff --git a/test/e2e/lifecycle/eg_lifecycle_test.go b/test/e2e/lifecycle/eg_lifecycle_test.go new file mode 100644 index 00000000000..0e771cc2b57 --- /dev/null +++ b/test/e2e/lifecycle/eg_lifecycle_test.go @@ -0,0 +1,67 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e +// +build e2e + +package lifecycle + +import ( + "flag" + "io/fs" + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/util/sets" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/gateway-api/conformance/utils/flags" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + "sigs.k8s.io/gateway-api/pkg/features" + + "github.com/envoyproxy/gateway/test/e2e" + "github.com/envoyproxy/gateway/test/e2e/tests" +) + +func TestEGLifecycle(t *testing.T) { + flag.Parse() + + cfg, err := config.GetConfig() + require.NoError(t, err) + + c, err := client.New(cfg, client.Options{}) + require.NoError(t, err) + + if flags.RunTest != nil && *flags.RunTest != "" { + t.Logf("Running E2E test %s with %s GatewayClass\n cleanup: %t\n debug: %t", + *flags.RunTest, *flags.GatewayClassName, *flags.CleanupBaseResources, *flags.ShowDebug) + } else { + t.Logf("Running E2E tests with %s GatewayClass\n cleanup: %t\n debug: %t", + *flags.GatewayClassName, *flags.CleanupBaseResources, *flags.ShowDebug) + } + + cSuite, err := suite.NewConformanceTestSuite(suite.ConformanceOptions{ + Client: c, + GatewayClassName: *flags.GatewayClassName, + Debug: *flags.ShowDebug, + CleanupBaseResources: *flags.CleanupBaseResources, + ManifestFS: []fs.FS{e2e.Manifests}, + RunTest: *flags.RunTest, + // SupportedFeatures cannot be empty, so we set it to SupportGateway + // All e2e tests should leave Features empty. + SupportedFeatures: sets.New[features.SupportedFeature](features.SupportGateway), + }) + if err != nil { + t.Fatalf("Failed to create test suite: %v", err) + } + + t.Logf("Running %d lifecycle tests", len(tests.LifecycleTests)) + cSuite.Setup(t, tests.LifecycleTests) + + err = cSuite.Run(t, tests.LifecycleTests) + if err != nil { + t.Fatalf("Failed to run tests: %v", err) + } +} diff --git a/test/e2e/testdata/eg-lifecycle.yaml b/test/e2e/testdata/eg-lifecycle.yaml new file mode 100644 index 00000000000..dd055449142 --- /dev/null +++ b/test/e2e/testdata/eg-lifecycle.yaml @@ -0,0 +1,16 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-backend-eg-lifecycle + namespace: gateway-lifecycle-infra +spec: + parentRefs: + - name: lifecycle-gateway + rules: + - matches: + - path: + type: PathPrefix + value: /eg-lifecycle + backendRefs: + - name: infra-backend + port: 8080 diff --git a/test/e2e/tests/eg_lifecycle.go b/test/e2e/tests/eg_lifecycle.go new file mode 100644 index 00000000000..9d41411c323 --- /dev/null +++ b/test/e2e/tests/eg_lifecycle.go @@ -0,0 +1,142 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e +// +build e2e + +package tests + +import ( + "os" + "testing" + "time" + + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + + "github.com/envoyproxy/gateway/internal/cmd/options" + "github.com/envoyproxy/gateway/internal/utils/helm" +) + +func init() { + LifecycleTests = append(LifecycleTests, EGUninstallAndInstallTest, EGUpgradeTest) +} + +var EGUninstallAndInstallTest = suite.ConformanceTest{ + ShortName: "EGUninstallAndInstall", + Description: "Uninstall and Install Envoy Gateway using Helm Package Tool", + Manifests: []string{"testdata/eg-lifecycle.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("Uninstall Envoy Gateway should succeed", func(t *testing.T) { + // first ensure that EG provides services normally + ns := "gateway-lifecycle-infra" + routeNN := types.NamespacedName{ + Name: "http-backend-eg-lifecycle", + Namespace: ns, + } + gwNN := types.NamespacedName{ + Name: "lifecycle-gateway", + Namespace: ns, + } + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, + suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/eg-lifecycle", + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response before the install process started: %v", err) + } + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response before the uninstall and install process started: %v", err) + } + + // we will start the uninstallation process after ensuring that the envoy gateway normal service. + relName := "eg" + relNamespace := "envoy-gateway-system" + options.DefaultConfigFlags.Namespace = ptr.To(relNamespace) + + ht := helm.NewPackageTool() + if err = ht.Setup(); err != nil { + t.Errorf("failed to setup of packageTool: %v", err) + } + + t.Log("start uninstall envoy gateway resources") + if err := ht.RunUninstall(&helm.PackageOptions{ + ReleaseName: relName, + }); err != nil { + t.Errorf("failed to uninstall envoy-gateway: %v", err) + } + t.Log("success to uninstall envoy gateway resources") + }) + t.Run("Install Envoy Gateway should succeed", func(t *testing.T) { + ns := "gateway-lifecycle-infra" + routeNN := types.NamespacedName{ + Name: "http-backend-eg-lifecycle", + Namespace: ns, + } + gwNN := types.NamespacedName{ + Name: "lifecycle-gateway", + Namespace: ns, + } + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, + suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + expectedResponse := http.ExpectedResponse{ + Request: http.Request{ + Path: "/eg-lifecycle", + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http") + + relName := "eg" + relNamespace := "envoy-gateway-system" + options.DefaultConfigFlags.Namespace = ptr.To(relNamespace) + lastVersionTag := os.Getenv("last_version_tag") + if lastVersionTag == "" { + lastVersionTag = "v0.0.0-latest" // Default version tag if not specified + } + + ht := helm.NewPackageTool() + if err := ht.Setup(); err != nil { + t.Errorf("failed to setup of packageTool: %v", err) + } + + t.Log("start install envoy gateway resource") + if err := ht.RunInstall(&helm.PackageOptions{ + SkipCRD: true, + ReleaseName: relName, + ReleaseNamespace: relNamespace, + Version: lastVersionTag, + Timeout: time.Minute * 5, + }); err != nil { + t.Errorf("failed to install envoy-gateway: %v", err) + } + + // finally, ensure that the envoy-gateway is in normal service. + cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req) + if err != nil { + t.Errorf("failed to get expected response before the install process started: %v", err) + } + if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil { + t.Errorf("failed to compare request and response before the uninstall and install process started: %v", err) + } + t.Log("success to install envoy gateway resources") + }) + }, +} diff --git a/test/e2e/tests/tests.go b/test/e2e/tests/tests.go index dc39be7790b..4a37f28bfa5 100644 --- a/test/e2e/tests/tests.go +++ b/test/e2e/tests/tests.go @@ -13,6 +13,7 @@ import "sigs.k8s.io/gateway-api/conformance/utils/suite" var ( ConformanceTests []suite.ConformanceTest UpgradeTests []suite.ConformanceTest + LifecycleTests []suite.ConformanceTest MergeGatewaysTests []suite.ConformanceTest MultipleGCTests map[string][]suite.ConformanceTest ) diff --git a/tools/make/kube.mk b/tools/make/kube.mk index 354781bd9fd..eef86d9599e 100644 --- a/tools/make/kube.mk +++ b/tools/make/kube.mk @@ -156,6 +156,7 @@ ifeq ($(E2E_RUN_TEST),) go test $(E2E_TEST_ARGS) ./test/e2e/merge_gateways --gateway-class=merge-gateways --debug=true --cleanup-base-resources=false go test $(E2E_TEST_ARGS) ./test/e2e/multiple_gc --debug=true --cleanup-base-resources=true go test $(E2E_TEST_ARGS) ./test/e2e/upgrade --gateway-class=upgrade --debug=true --cleanup-base-resources=$(E2E_CLEANUP) + go test $(E2E_TEST_ARGS) ./test/e2e/lifecycle --gateway-class=lifecycle --debug=true --cleanup-base-resources=$(E2E_CLEANUP) else go test $(E2E_TEST_ARGS) ./test/e2e --gateway-class=envoy-gateway --debug=true --cleanup-base-resources=$(E2E_CLEANUP) \ --run-test $(E2E_RUN_TEST)