diff --git a/.github/workflows/sample-apps.yaml b/.github/workflows/sample-apps.yaml index c29c1177..0082122c 100644 --- a/.github/workflows/sample-apps.yaml +++ b/.github/workflows/sample-apps.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - app: [cpu-load-gen, hello-world, outbound-http, variabletester, redis-sample] + app: [cpu-load-gen, hello-world, outbound-http, variabletester, redis-sample, salutations] env: IMAGE_NAME: ${{ github.repository }} diff --git a/README.md b/README.md index f6b504f6..254a936d 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Create a k3d cluster: ```shell k3d cluster create wasm-cluster \ - --image ghcr.io/spinkube/containerd-shim-spin/k3d:v0.15.1 \ + --image ghcr.io/spinkube/containerd-shim-spin/k3d:v0.16.0 \ -p "8081:80@loadbalancer" \ --agents 2 ``` diff --git a/api/v1alpha1/spinapp_types.go b/api/v1alpha1/spinapp_types.go index dd147553..404c2940 100644 --- a/api/v1alpha1/spinapp_types.go +++ b/api/v1alpha1/spinapp_types.go @@ -80,6 +80,12 @@ type SpinAppSpec struct { // Resources defines the resource requirements for this app. Resources Resources `json:"resources,omitempty"` + + // Components of the app to execute. + // + // If this is not provided all components are executed. + // +kubebuilder:validation:MinItems:=1 + Components []string `json:"components,omitempty"` } // SpinAppStatus defines the observed state of SpinApp diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 93d3663c..af660413 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -516,6 +516,11 @@ func (in *SpinAppSpec) DeepCopyInto(out *SpinAppSpec) { } } in.Resources.DeepCopyInto(&out.Resources) + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpinAppSpec. diff --git a/apps/salutations/.gitignore b/apps/salutations/.gitignore new file mode 100644 index 00000000..b5650104 --- /dev/null +++ b/apps/salutations/.gitignore @@ -0,0 +1,2 @@ +main.wasm +.spin/ diff --git a/apps/salutations/go.mod b/apps/salutations/go.mod new file mode 100644 index 00000000..f7629d27 --- /dev/null +++ b/apps/salutations/go.mod @@ -0,0 +1,7 @@ +module github.com/salutations + +go 1.20 + +require github.com/fermyon/spin/sdk/go/v2 v2.2.0 + +require github.com/julienschmidt/httprouter v1.3.0 // indirect diff --git a/apps/salutations/go.sum b/apps/salutations/go.sum new file mode 100644 index 00000000..c283accd --- /dev/null +++ b/apps/salutations/go.sum @@ -0,0 +1,4 @@ +github.com/fermyon/spin/sdk/go/v2 v2.2.0 h1:zHZdIqjbUwyxiwdygHItnM+vUUNSZ3CX43jbIUemBI4= +github.com/fermyon/spin/sdk/go/v2 v2.2.0/go.mod h1:kfJ+gdf/xIaKrsC6JHCUDYMv2Bzib1ohFIYUzvP+SCw= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= diff --git a/apps/salutations/main.go b/apps/salutations/main.go new file mode 100644 index 00000000..01d1499f --- /dev/null +++ b/apps/salutations/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "net/http" + + spinhttp "github.com/fermyon/spin/sdk/go/v2/http" +) + +func init() { + spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + fmt.Fprintln(w, "Goodbye!") + }) +} + +func main() {} diff --git a/apps/salutations/spin.toml b/apps/salutations/spin.toml new file mode 100644 index 00000000..ae1edb8a --- /dev/null +++ b/apps/salutations/spin.toml @@ -0,0 +1,29 @@ +spin_manifest_version = 2 + +[application] +name = "salutations" +version = "0.1.0" +authors = ["Kate Goldenring "] +description = "An app that gives salutations" + +[[trigger.http]] +route = "/hi" +component = "hello" + +[component.hello] +source = "../hello-world/main.wasm" +allowed_outbound_hosts = [] +[component.hello.build] +command = "pushd ../hello-world && tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go && popd" +watch = ["**/*.go", "go.mod"] + +[[trigger.http]] +route = "/bye" +component = "goodbye" + +[component.goodbye] +source = "main.wasm" +allowed_outbound_hosts = [] +[component.goodbye.build] +command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go" +watch = ["**/*.go", "go.mod"] \ No newline at end of file diff --git a/config/crd/bases/core.spinoperator.dev_spinapps.yaml b/config/crd/bases/core.spinoperator.dev_spinapps.yaml index 6589f8a4..cd11ecdc 100644 --- a/config/crd/bases/core.spinoperator.dev_spinapps.yaml +++ b/config/crd/bases/core.spinoperator.dev_spinapps.yaml @@ -188,6 +188,12 @@ spec: type: integer type: object type: object + components: + description: Components to be executed in this group. + items: + type: string + minItems: 1 + type: array deploymentAnnotations: additionalProperties: type: string diff --git a/config/samples/component-filtering.yaml b/config/samples/component-filtering.yaml new file mode 100644 index 00000000..bbbadab8 --- /dev/null +++ b/config/samples/component-filtering.yaml @@ -0,0 +1,12 @@ +apiVersion: core.spinoperator.dev/v1alpha1 +kind: SpinApp +metadata: + name: hello-salutation-spinapp +spec: + # TODO: change to image hosted at ghcr.io/spinkube/spin-operator/salutations when published + image: "ghcr.io/kate-goldenring/spin-operator/examples/spin-salutations:20241022-144454" + replicas: 1 + executor: containerd-shim-spin + # Configure the application to only contain the "hello" component + # Who doesn't hate goodbyes? + components: ["hello"] diff --git a/internal/controller/spinapp_controller.go b/internal/controller/spinapp_controller.go index 130f2621..a46c98ee 100644 --- a/internal/controller/spinapp_controller.go +++ b/internal/controller/spinapp_controller.go @@ -22,6 +22,7 @@ import ( "fmt" "hash/adler32" "maps" + "strings" "github.com/pelletier/go-toml/v2" appsv1 "k8s.io/api/apps/v1" @@ -429,6 +430,12 @@ func constructDeployment(ctx context.Context, app *spinv1alpha1.SpinApp, config } env := ConstructEnvForApp(ctx, app, spinapp.DefaultHTTPPort, config.Otel) + if app.Spec.Components != nil { + env = append(env, corev1.EnvVar{ + Name: "SPIN_COMPONENTS_TO_RETAIN", + Value: strings.Join(app.Spec.Components[:], ","), + }) + } readinessProbe, livenessProbe, err := ConstructPodHealthChecks(app) if err != nil { @@ -457,7 +464,8 @@ func constructDeployment(ctx context.Context, app *spinv1alpha1.SpinApp, config container = corev1.Container{ Name: app.Name, Image: *config.SpinImage, - Args: []string{"up", "--listen", fmt.Sprintf("0.0.0.0:%d", spinapp.DefaultHTTPPort), "-f", app.Spec.Image, "--runtime-config-file", "/runtime-config.toml"}, + // TODO: add support for --component-id flags to set components to be retained once spintainer supports Spin v3.0 + Args: []string{"up", "--listen", fmt.Sprintf("0.0.0.0:%d", spinapp.DefaultHTTPPort), "-f", app.Spec.Image, "--runtime-config-file", "/runtime-config.toml"}, Ports: []corev1.ContainerPort{{ Name: spinapp.HTTPPortName, ContainerPort: spinapp.DefaultHTTPPort,