From ffe4e66106eb394883fc65a6a65b5528cb2dfef0 Mon Sep 17 00:00:00 2001 From: Kate Goldenring Date: Wed, 23 Oct 2024 13:04:35 -0700 Subject: [PATCH] Add e2e test for component filtering Signed-off-by: Kate Goldenring --- .../bases/core.spinoperator.dev_spinapps.yaml | 6 +- config/manager/kustomization.yml | 8 +- e2e/component_filtering_test.go | 90 +++++++++++++++++++ e2e/default_test.go | 10 ++- e2e/helper/helper.go | 12 +-- e2e/k3d_provider.go | 2 +- e2e/redis_test.go | 4 +- e2e/spintainer_test.go | 2 +- 8 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 e2e/component_filtering_test.go diff --git a/config/crd/bases/core.spinoperator.dev_spinapps.yaml b/config/crd/bases/core.spinoperator.dev_spinapps.yaml index 8bb0ef21..37d933f8 100644 --- a/config/crd/bases/core.spinoperator.dev_spinapps.yaml +++ b/config/crd/bases/core.spinoperator.dev_spinapps.yaml @@ -189,7 +189,11 @@ spec: type: object type: object components: - description: Components to be executed in this group. + description: |- + Components of the app to execute. + + + If this is not provided all components are executed. items: type: string minItems: 1 diff --git a/config/manager/kustomization.yml b/config/manager/kustomization.yml index 2e6cc797..93b0050e 100644 --- a/config/manager/kustomization.yml +++ b/config/manager/kustomization.yml @@ -1,2 +1,8 @@ resources: -- manager.yaml \ No newline at end of file +- manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: ghcr.io/spinkube/spin-operator + newName: ghcr.io/spinkube/spin-operator + newTag: dev diff --git a/e2e/component_filtering_test.go b/e2e/component_filtering_test.go new file mode 100644 index 00000000..997c39c7 --- /dev/null +++ b/e2e/component_filtering_test.go @@ -0,0 +1,90 @@ +package e2e + +import ( + "context" + "testing" + "time" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/e2e-framework/klient" + "sigs.k8s.io/e2e-framework/klient/wait" + "sigs.k8s.io/e2e-framework/klient/wait/conditions" + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/features" + + spinapps_v1alpha1 "github.com/spinkube/spin-operator/api/v1alpha1" + "github.com/spinkube/spin-operator/e2e/helper" + "github.com/stretchr/testify/require" +) + +// TestComponentFiltering checks that the operator and shim have support for running a subset of a Spin app's components +func TestComponentFiltering(t *testing.T) { + var client klient.Client + + // TODO: Use an image from a sample app in this repository + appImage := "ghcr.io/kate-goldenring/spin-operator/examples/spin-salutations:20241022-144454" + testSpinAppName := "test-component-filtering" + + defaultTest := features.New("default and most minimal setup"). + Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + + client = cfg.Client() + + if err := spinapps_v1alpha1.AddToScheme(client.Resources(testNamespace).GetScheme()); err != nil { + t.Fatalf("failed to register the spinapps_v1alpha1 types with Kubernetes scheme: %s", err) + } + + return ctx + }). + Assess("spin app custom resource is created", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + testSpinApp := newSpinAppCR(testSpinAppName, appImage, "containerd-shim-spin", []string{"hello"}) + + if err := client.Resources().Create(ctx, testSpinApp); err != nil { + t.Fatalf("Failed to create spinapp: %s", err) + } + return ctx + }). + Assess("spin app deployment and service are available", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + // wait for deployment to be ready + if err := wait.For( + conditions.New(client.Resources()).DeploymentAvailable(testSpinAppName, testNamespace), + wait.WithTimeout(3*time.Minute), + wait.WithInterval(time.Second), + ); err != nil { + t.Fatal(err) + } + + svc := &v1.ServiceList{ + Items: []v1.Service{ + {ObjectMeta: metav1.ObjectMeta{Name: testSpinAppName, Namespace: testNamespace}}, + }, + } + + if err := wait.For( + conditions.New(client.Resources()).ResourcesFound(svc), + wait.WithTimeout(3*time.Minute), + wait.WithInterval(500*time.Millisecond), + ); err != nil { + t.Fatal(err) + } + return ctx + }). + Assess("spin app is only serving hello component", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + helper.EnsureDebugContainer(t, ctx, cfg, testNamespace) + + _, status, err := helper.CurlSpinApp(t, ctx, cfg, testNamespace, testSpinAppName, "/hi", "") + + require.NoError(t, err) + require.Equal(t, 200, status) + + _, status, err = helper.CurlSpinApp(t, ctx, cfg, testNamespace, testSpinAppName, "/bye", "") + + require.NoError(t, err) + require.Equal(t, 404, status) + + return ctx + }). + Feature() + testEnv.Test(t, defaultTest) +} diff --git a/e2e/default_test.go b/e2e/default_test.go index d586fed3..537dc221 100644 --- a/e2e/default_test.go +++ b/e2e/default_test.go @@ -30,7 +30,7 @@ func TestDefaultSetup(t *testing.T) { Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { client = cfg.Client() - testSpinApp := newSpinAppCR(testSpinAppName, helloWorldImage, "containerd-shim-spin") + testSpinApp := newSpinAppCR(testSpinAppName, helloWorldImage, "containerd-shim-spin", nil) if err := client.Resources().Create(ctx, testSpinApp); err != nil { t.Fatalf("Failed to create spinapp: %s", err) } @@ -69,8 +69,8 @@ func TestDefaultSetup(t *testing.T) { testEnv.Test(t, defaultTest) } -func newSpinAppCR(name, image, executor string) *spinapps_v1alpha1.SpinApp { - return &spinapps_v1alpha1.SpinApp{ +func newSpinAppCR(name, image, executor string, components []string) *spinapps_v1alpha1.SpinApp { + app := spinapps_v1alpha1.SpinApp{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: testNamespace, @@ -81,4 +81,8 @@ func newSpinAppCR(name, image, executor string) *spinapps_v1alpha1.SpinApp { Executor: executor, }, } + if components != nil { + app.Spec.Components = components + } + return &app } diff --git a/e2e/helper/helper.go b/e2e/helper/helper.go index ecd753d1..e2825174 100644 --- a/e2e/helper/helper.go +++ b/e2e/helper/helper.go @@ -49,10 +49,9 @@ func EnsureDebugContainer(t *testing.T, ctx context.Context, cfg *envconf.Config } } -// PostToSpinApp is a crude function for using the debug pod to post to a spin app -// within the cluster. It allows customization of the route, but all requests -// are currently `POST`. -func PostToSpinApp(t *testing.T, ctx context.Context, cfg *envconf.Config, namespace, spinAppName, route, body string) (string, int, error) { +// CurlSpinApp is a crude function for using the debug pod to send a HTTP request to a spin app +// within the cluster. It allows customization of the route, and all requests are GET requests unless a non-empty body is provided. +func CurlSpinApp(t *testing.T, ctx context.Context, cfg *envconf.Config, namespace, spinAppName, route, body string) (string, int, error) { t.Helper() client, err := cfg.NewClient() @@ -71,7 +70,10 @@ func PostToSpinApp(t *testing.T, ctx context.Context, cfg *envconf.Config, names podName := debugPod.Name - command := []string{"curl", "-s", "-m", "5", "-w", "\n%{http_code}\n", "http://" + spinAppName + "." + namespace + route, "--data", body, "-o", "-"} + command := []string{"curl", "--silent", "--max-time", "5", "--write-out", "\n%{http_code}\n", "http://" + spinAppName + "." + namespace + route, "--output", "-"} + if body != "" { + command = append(command, "--data", body) + } var stdout, stderr bytes.Buffer if err := client.Resources().ExecInPod(ctx, namespace, podName, debugDeploymentName, command, &stdout, &stderr); err != nil { diff --git a/e2e/k3d_provider.go b/e2e/k3d_provider.go index 1a32af3f..a9814436 100644 --- a/e2e/k3d_provider.go +++ b/e2e/k3d_provider.go @@ -14,7 +14,7 @@ import ( "sigs.k8s.io/e2e-framework/support/utils" ) -const k3dImage = "ghcr.io/spinkube/containerd-shim-spin/k3d:v0.13.1" +const k3dImage = "ghcr.io/spinkube/containerd-shim-spin/k3d:20241015-215845-g71c8351" var k3dBin = "k3d" diff --git a/e2e/redis_test.go b/e2e/redis_test.go index 17a827d4..5f527b15 100644 --- a/e2e/redis_test.go +++ b/e2e/redis_test.go @@ -92,7 +92,7 @@ func TestShimRedis(t *testing.T) { Assess("spin app is using redis", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { helper.EnsureDebugContainer(t, ctx, cfg, testNamespace) - _, status, err := helper.PostToSpinApp(t, ctx, cfg, testNamespace, testSpinAppName, "/api", "{\"key\": \"foo\", \"value\": \"bar\"}") + _, status, err := helper.CurlSpinApp(t, ctx, cfg, testNamespace, testSpinAppName, "/api", "{\"key\": \"foo\", \"value\": \"bar\"}") require.NoError(t, err) require.Equal(t, 200, status) @@ -194,7 +194,7 @@ func TestSpintainerRedis(t *testing.T) { Assess("spin app is using redis", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { helper.EnsureDebugContainer(t, ctx, cfg, testNamespace) - _, status, err := helper.PostToSpinApp(t, ctx, cfg, testNamespace, testSpinAppName, "/api", "{\"key\": \"foo\", \"value\": \"bar\"}") + _, status, err := helper.CurlSpinApp(t, ctx, cfg, testNamespace, testSpinAppName, "/api", "{\"key\": \"foo\", \"value\": \"bar\"}") require.NoError(t, err) require.Equal(t, 200, status) diff --git a/e2e/spintainer_test.go b/e2e/spintainer_test.go index 546eab75..4869f49a 100644 --- a/e2e/spintainer_test.go +++ b/e2e/spintainer_test.go @@ -38,7 +38,7 @@ func TestSpintainer(t *testing.T) { return ctx }). Assess("spin app custom resource is created", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { - testSpinApp := newSpinAppCR(testSpinAppName, helloWorldImage, "spintainer") + testSpinApp := newSpinAppCR(testSpinAppName, helloWorldImage, "spintainer", nil) if err := client.Resources().Create(ctx, newSpintainerExecutor(testNamespace)); controllerruntimeclient.IgnoreAlreadyExists(err) != nil { t.Fatalf("Failed to create spinappexecutor: %s", err)