Skip to content

Commit

Permalink
Merge pull request #227 from ThorstenHans/feat/pod-labels
Browse files Browse the repository at this point in the history
Add podLabels to `SpinApp` CRD
  • Loading branch information
michelleN committed May 20, 2024
2 parents 059776e + d1f01ef commit 519993a
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 16 deletions.
3 changes: 3 additions & 0 deletions api/v1alpha1/spinapp_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ type SpinAppSpec struct {
// PodAnnotations defines annotations to be applied to the underlying pods.
PodAnnotations map[string]string `json:"podAnnotations,omitempty"`

// PodLabels defines labels to be applied to the underlying pods.
PodLabels map[string]string `json:"podLabels,omitempty"`

// Resources defines the resource requirements for this app.
Resources Resources `json:"resources,omitempty"`
}
Expand Down
7 changes: 7 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions config/crd/bases/core.spinoperator.dev_spinapps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ spec:
description: PodAnnotations defines annotations to be applied to the
underlying pods.
type: object
podLabels:
additionalProperties:
type: string
description: PodLabels defines labels to be applied to the underlying
pods.
type: object
replicas:
description: Number of replicas to run.
format: int32
Expand Down
17 changes: 2 additions & 15 deletions e2e/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"time"

v1 "k8s.io/api/core/v1"
nodev1 "k8s.io/api/node/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/e2e-framework/klient"
"sigs.k8s.io/e2e-framework/klient/k8s"
Expand Down Expand Up @@ -37,17 +36,6 @@ func TestDefaultSetup(t *testing.T) {
t.Fatalf("failed to register the spinapps_v1alpha1 types with Kuberenets scheme: %s", err)
}

runtimeClass := &nodev1.RuntimeClass{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeClassName,
},
Handler: "spin",
}

if err := client.Resources().Create(ctx, runtimeClass); err != nil {
t.Fatalf("Failed to create runtimeclass: %s", err)
}

return ctx
}).
Assess("spin app custom resource is created", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
Expand Down Expand Up @@ -98,7 +86,8 @@ func TestDefaultSetup(t *testing.T) {
t.Fatal(err)
}
return ctx
}).Feature()
}).
Feature()
testEnv.Test(t, defaultTest)
}

Expand All @@ -114,9 +103,7 @@ func newSpinAppCR(name, image string) *spinapps_v1alpha1.SpinApp {
Executor: "containerd-shim-spin",
},
}

return testSpinApp

}

func newContainerdShimExecutor(namespace string) *spinapps_v1alpha1.SpinAppExecutor {
Expand Down
15 changes: 15 additions & 0 deletions e2e/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"testing"
"time"

nodev1 "k8s.io/api/node/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/e2e-framework/klient/wait"
"sigs.k8s.io/e2e-framework/klient/wait/conditions"
"sigs.k8s.io/e2e-framework/pkg/env"
Expand Down Expand Up @@ -92,6 +94,19 @@ func TestMain(m *testing.M) {

return ctx, nil
},
// deploy runtime class
func(ctx context.Context, c *envconf.Config) (context.Context, error) {
client := cfg.Client()
runtimeClass := &nodev1.RuntimeClass{
ObjectMeta: metav1.ObjectMeta{
Name: runtimeClassName,
},
Handler: "spin",
}

err := client.Resources().Create(ctx, runtimeClass)
return ctx, err
},
)

testEnv.Finish(
Expand Down
106 changes: 106 additions & 0 deletions e2e/podlabel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package e2e

import (
"context"
"testing"
"time"

appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/e2e-framework/klient"
"sigs.k8s.io/e2e-framework/klient/k8s"
"sigs.k8s.io/e2e-framework/klient/k8s/resources"
"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"
)

const (
testPodLabelName = "core.spinoperator.dev/test"
testPodLabelValue = "foobar"
)

// TestPodLabels is a test that checks that SpinApp.spec.podLabels are
// passed down to underlying pods
func TestPodLabels(t *testing.T) {
var client klient.Client

helloWorldImage := "ghcr.io/spinkube/containerd-shim-spin/examples/spin-rust-hello:v0.13.0"
testSpinAppName := "test-spinapp-with-pod-labels"

podLabelTest := features.New("SpinApp with custom PodLabels").
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 Kuberenets 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 := newSpinAppWithPodLabels(testSpinAppName, helloWorldImage)

if err := client.Resources().Create(ctx, testSpinApp); err != nil {
t.Fatalf("Failed to create spinapp: %s", err)
}

if err := wait.For(
conditions.New(client.Resources()).ResourceMatch(testSpinApp, func(object k8s.Object) bool {
return true
}),
wait.WithTimeout(3*time.Minute),
wait.WithInterval(30*time.Second),
); err != nil {
t.Fatal(err)
}

return ctx
}).
Assess("spin app deployment is available", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
if err := wait.For(
conditions.New(client.Resources()).DeploymentAvailable(testSpinAppName, testNamespace),
wait.WithTimeout(3*time.Minute),
wait.WithInterval(5*time.Second),
); err != nil {
t.Fatal(err)
}
return ctx
}).
Assess("spin app deployment has custom pod labels", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
res, err := resources.New(client.RESTConfig())
if err != nil {
t.Fatalf("Could not create controller runtime client: %s", err)
}
var deploy appsv1.Deployment
if err = res.Get(ctx, testSpinAppName, testNamespace, &deploy); err != nil {
t.Fatalf("Could not find deployment: %s", err)
}
v, ok := deploy.Spec.Template.Labels[testPodLabelName]
if !ok || v != testPodLabelValue {
t.Fatal("PodLabels were not passed from the SpinApp to the underlying Pod")
}
return ctx
}).
Feature()
testEnv.Test(t, podLabelTest)
}

func newSpinAppWithPodLabels(name, image string) *spinapps_v1alpha1.SpinApp {
return &spinapps_v1alpha1.SpinApp{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: testNamespace,
},
Spec: spinapps_v1alpha1.SpinAppSpec{
Replicas: 1,
Image: image,
Executor: "containerd-shim-spin",
PodLabels: map[string]string{testPodLabelName: testPodLabelValue},
},
}
}
15 changes: 15 additions & 0 deletions internal/controller/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,18 @@ func registerAndGetScheme() *runtime.Scheme {

return scheme
}

func spinAppWithLabels(labels map[string]string) *spinv1alpha1.SpinApp {
return &spinv1alpha1.SpinApp{
ObjectMeta: metav1.ObjectMeta{
Name: "my-app",
Namespace: "default",
},
Spec: spinv1alpha1.SpinAppSpec{
Executor: "containerd-shim-spin",
Image: "fakereg.dev/noapp:latest",
Replicas: 1,
PodLabels: labels,
},
}
}
9 changes: 8 additions & 1 deletion internal/controller/spinapp_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"hash/adler32"
"maps"

"github.com/pelletier/go-toml/v2"
appsv1 "k8s.io/api/apps/v1"
Expand Down Expand Up @@ -408,6 +409,12 @@ func constructDeployment(ctx context.Context, app *spinv1alpha1.SpinApp, config
statusKey: statusValue,
}

templateLabels := app.Spec.PodLabels
if templateLabels == nil {
templateLabels = map[string]string{}
}
maps.Copy(templateLabels, readyLabels)

// TODO: Once we land admission webhooks write some validation for this e.g.
// don't allow setting memory limit with cyclotron runtime.
resources := corev1.ResourceRequirements{
Expand Down Expand Up @@ -442,7 +449,7 @@ func constructDeployment(ctx context.Context, app *spinv1alpha1.SpinApp, config
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: readyLabels,
Labels: templateLabels,
Annotations: templateAnnotations,
},
Spec: corev1.PodSpec{
Expand Down
20 changes: 20 additions & 0 deletions internal/controller/spinapp_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,23 @@ func TestConstructDeployment_MinimalApp(t *testing.T) {
require.Equal(t, app.Spec.Image, deployment.Spec.Template.Spec.Containers[0].Image)
require.Equal(t, ptr("bananarama"), deployment.Spec.Template.Spec.RuntimeClassName)
}

func TestConstructDeployment_WithPodLabels(t *testing.T) {
t.Parallel()

key, value := "dev.spinkube.tests", "foo"
app := spinAppWithLabels(map[string]string{
key: value,
})

cfg := &spinv1alpha1.ExecutorDeploymentConfig{
RuntimeClassName: "bananarama",
}
deployment, err := constructDeployment(context.Background(), app, cfg, "", "", nil)
require.NoError(t, err)
require.NotNil(t, deployment)

require.Equal(t, ptr(int32(1)), deployment.Spec.Replicas)
require.Len(t, deployment.Spec.Template.Labels, 3)
require.Equal(t, deployment.Spec.Template.Labels[key], value)
}

0 comments on commit 519993a

Please sign in to comment.