From ff332f50717a93b29b849d4ad5e62904317d1ee9 Mon Sep 17 00:00:00 2001 From: Sven Pfennig Date: Tue, 29 Aug 2023 13:10:10 +0000 Subject: [PATCH] feat: add spin-dapr example Signed-off-by: Sven Pfennig --- .github/workflows/release.yaml | 1 + images/spin-dapr/.dockerignore | 5 ++ images/spin-dapr/.gitignore | 2 + images/spin-dapr/Cargo.toml | 20 +++++++ images/spin-dapr/Dockerfile | 4 ++ images/spin-dapr/README.md | 43 +++++++++++++++ images/spin-dapr/deploy.yaml | 95 ++++++++++++++++++++++++++++++++++ images/spin-dapr/spin.toml | 17 ++++++ images/spin-dapr/src/lib.rs | 30 +++++++++++ 9 files changed, 217 insertions(+) create mode 100644 images/spin-dapr/.dockerignore create mode 100644 images/spin-dapr/.gitignore create mode 100644 images/spin-dapr/Cargo.toml create mode 100644 images/spin-dapr/Dockerfile create mode 100644 images/spin-dapr/README.md create mode 100644 images/spin-dapr/deploy.yaml create mode 100644 images/spin-dapr/spin.toml create mode 100644 images/spin-dapr/src/lib.rs diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a2a9697e..030ecc1b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -63,6 +63,7 @@ jobs: {"imageName": "examples/wws-js-hello", "context": "images/wws"}, {"imageName": "examples/spin-inbound-redis", "context": "images/spin-inbound-redis"}, {"imageName": "examples/spin-outbound-redis", "context": "images/spin-outbound-redis"}], + {"imageName": "examples/spin-dapr", "context": "images/spin-dapr"}], {"imageName": "examples/lunatic-rust-hello", "context": "images/lunatic"}], {"imageName": "examples/lunatic-spawn-process", "context": "images/lunatic-spawn-process"}], {"imageName": "examples/lunatic-submillisecond", "context": "images/lunatic-submillisecond"}] diff --git a/images/spin-dapr/.dockerignore b/images/spin-dapr/.dockerignore new file mode 100644 index 00000000..fc8a9b5f --- /dev/null +++ b/images/spin-dapr/.dockerignore @@ -0,0 +1,5 @@ +target/debug +target/release +target/wasm32-wasi/build +target/wasm32-wasi/deps +.spin/ \ No newline at end of file diff --git a/images/spin-dapr/.gitignore b/images/spin-dapr/.gitignore new file mode 100644 index 00000000..386474fa --- /dev/null +++ b/images/spin-dapr/.gitignore @@ -0,0 +1,2 @@ +target/ +.spin/ diff --git a/images/spin-dapr/Cargo.toml b/images/spin-dapr/Cargo.toml new file mode 100644 index 00000000..66794f01 --- /dev/null +++ b/images/spin-dapr/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "spin-dapr" +authors = ["Sven Pfennig "] +description = "Example application to use Dapr with Spin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = [ "cdylib" ] + +[dependencies] +anyhow = "1" +# Crate to simplify working with bytes. +bytes = "1" +# General-purpose crate with common HTTP types. +http = "0.2" +# The Spin SDK. +spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v1.4.1" } + +[workspace] diff --git a/images/spin-dapr/Dockerfile b/images/spin-dapr/Dockerfile new file mode 100644 index 00000000..d8d08c8a --- /dev/null +++ b/images/spin-dapr/Dockerfile @@ -0,0 +1,4 @@ +FROM scratch +COPY ./spin.toml . +COPY --chmod=0755 ./target/wasm32-wasi/release/spin_dapr.wasm /target/wasm32-wasi/release/spin_dapr.wasm +ENTRYPOINT ["/target/wasm32-wasi/release/spin_dapr.wasm"] \ No newline at end of file diff --git a/images/spin-dapr/README.md b/images/spin-dapr/README.md new file mode 100644 index 00000000..5828c862 --- /dev/null +++ b/images/spin-dapr/README.md @@ -0,0 +1,43 @@ +# Spin Dapr Demo + +## Description +This demo application is a simple Spin app that is triggered by the daprs [kubernetes input binding](https://docs.dapr.io/reference/components-reference/supported-bindings/kubernetes-binding/) when it is called with the path `/kevents` it writes the body to a redis running at `redis://localhost:6379` to the key `lastEvent`. All other paths just return the value of the `lastEvent` key. + +### Prerequisites +Install dapr cli +```sh +wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash +`````` + +Install spin cli: +```sh +curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash +``` + +### Run example with K3d: +```sh +# start the K3d cluster +make up +# Install Dapr +dapr init -k --wait +# or via helm +# helm repo add dapr https://dapr.github.io/helm-charts/ +# helm repo update +# helm upgrade --install dapr dapr/dapr --namespace dapr-system --create-namespace --wait + +# build the application +cd images/spin-dapr +spin build +cd - +# create an image and load it into K3d +docker build images/spin-dapr -t spin-dapr:latest --load +mkdir -p test/out_spin-dapr/ +docker save spin-dapr:latest test/out_spin-dapr/img.tar +k3d image load -c test-cluster spin-dapr:latest test/out_spin-dapr/img.tar +# Apply the manifest +kubectl apply -f images/spin-dapr/deploy.yaml + +# When everythin is up, forward the port and get the last kubernetes event +kubectl port-forward svc/spin-dapr 8080:80 & +curl localhost:8080 +``` \ No newline at end of file diff --git a/images/spin-dapr/deploy.yaml b/images/spin-dapr/deploy.yaml new file mode 100644 index 00000000..3c2603a6 --- /dev/null +++ b/images/spin-dapr/deploy.yaml @@ -0,0 +1,95 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spin-dapr +spec: + replicas: 1 + selector: + matchLabels: + app: spin-dapr + template: + metadata: + labels: + app: spin-dapr + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "spin-dapr" + dapr.io/app-port: "80" + dapr.io/log-level: "debug" + spec: + runtimeClassName: wasmtime-spin-v1 + containers: + - image: redis + name: redis + - name: spin-dapr + image: spin-dapr:latest + imagePullPolicy: Never +--- +apiVersion: v1 +kind: Service +metadata: + name: spin-dapr +spec: + type: LoadBalancer + ports: + - protocol: TCP + port: 80 + targetPort: 80 + selector: + app: spin-dapr +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: spin-dapr + annotations: + ingress.kubernetes.io/ssl-redirect: "false" + kubernetes.io/ingress.class: traefik +spec: + rules: + - http: + paths: + - path: /spin-dapr + pathType: Prefix + backend: + service: + name: spin-dapr + port: + number: 80 +--- +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: kevents + namespace: default +spec: + type: bindings.kubernetes + version: v1 + metadata: + - name: namespace + value: "default" + - name: resyncPeriodInSec + value: "1" + - name: direction + value: "input" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: spin-dapr +rules: +- apiGroups: [""] + resources: ["events"] + verbs: ["get", "watch", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: spin-dapr +subjects: +- kind: ServiceAccount + name: default # or as need be, can be changed +roleRef: + kind: Role + name: spin-dapr # same as the one above + apiGroup: "" \ No newline at end of file diff --git a/images/spin-dapr/spin.toml b/images/spin-dapr/spin.toml new file mode 100644 index 00000000..eb004775 --- /dev/null +++ b/images/spin-dapr/spin.toml @@ -0,0 +1,17 @@ +spin_manifest_version = "1" +authors = ["Sven Pfennig "] +description = "Example application to use Dapr with Spin" +name = "spin-dapr" +trigger = { type = "http", base = "/" } +version = "0.1.0" + +[[component]] +id = "spin-dapr" +source = "target/wasm32-wasi/release/spin_dapr.wasm" +allowed_http_hosts = [] +[component.trigger] +route = "/..." + +[component.build] +command = "cargo build --target wasm32-wasi --release" +watch = ["src/**/*.rs", "Cargo.toml"] diff --git a/images/spin-dapr/src/lib.rs b/images/spin-dapr/src/lib.rs new file mode 100644 index 00000000..e6b70410 --- /dev/null +++ b/images/spin-dapr/src/lib.rs @@ -0,0 +1,30 @@ +use anyhow::{anyhow, Result}; +use spin_sdk::{ + http::{Request, Response}, + http_component, redis, +}; + +// Expect redis running on localhost or in the same pod +const ADDRESS: &str = "redis://localhost:6379"; +const KEY: &str = "lastEvent"; + + +#[http_component] +fn handle_spin_dapr(req: Request) -> Result { + println!("{:?}\n", req); + + if req.uri().path().ends_with("kevents"){ + let value = req.body().clone().unwrap(); + println!("Set: {:?}", value); + redis::set(ADDRESS, KEY, &value) + .map_err(|_| anyhow!("Error executing Redis set command"))?; + } + + let value = redis::get(ADDRESS, KEY) + .map_err(|_| anyhow!("Error executing Redis get command"))?; + + Ok(http::Response::builder() + .status(200) + .header("foo", "bar") + .body(Some(value.into()))?) +}