From b74554f48c8ef373b1ace2e4cf6b1c9a37360637 Mon Sep 17 00:00:00 2001 From: Andrew Steurer Date: Tue, 13 Aug 2024 10:28:26 -0600 Subject: [PATCH 1/3] adding redis-sample Signed-off-by: Andrew Steurer --- .github/workflows/sample-apps.yaml | 2 +- apps/redis-sample/.gitignore | 2 + apps/redis-sample/README.md | 93 ++++++++++++++++++++++++++++++ apps/redis-sample/go.mod | 7 +++ apps/redis-sample/go.sum | 4 ++ apps/redis-sample/main.go | 79 +++++++++++++++++++++++++ apps/redis-sample/spin.toml | 24 ++++++++ 7 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 apps/redis-sample/.gitignore create mode 100644 apps/redis-sample/README.md create mode 100644 apps/redis-sample/go.mod create mode 100644 apps/redis-sample/go.sum create mode 100644 apps/redis-sample/main.go create mode 100644 apps/redis-sample/spin.toml diff --git a/.github/workflows/sample-apps.yaml b/.github/workflows/sample-apps.yaml index f0c4b21d..c29c1177 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] + app: [cpu-load-gen, hello-world, outbound-http, variabletester, redis-sample] env: IMAGE_NAME: ${{ github.repository }} diff --git a/apps/redis-sample/.gitignore b/apps/redis-sample/.gitignore new file mode 100644 index 00000000..b5650104 --- /dev/null +++ b/apps/redis-sample/.gitignore @@ -0,0 +1,2 @@ +main.wasm +.spin/ diff --git a/apps/redis-sample/README.md b/apps/redis-sample/README.md new file mode 100644 index 00000000..bbbd4459 --- /dev/null +++ b/apps/redis-sample/README.md @@ -0,0 +1,93 @@ +# Overview + +This is an OCI-compliant package that can be used to demonstrate how a Spin app interacts with Redis in a Kubernetes cluster. + +# Usage + +## Deploying the Spin app + +Create a Kubernetes manifest file named `redis_client.yaml` with the following code: + +```yaml +apiVersion: core.spinoperator.dev/v1alpha1 +kind: SpinApp +metadata: + name: redis-spinapp +spec: + image: "ghcr.io/spinkube/redis-sample" + replicas: 1 + executor: containerd-shim-spin + variables: + - name: redis_endpoint + value: redis://redis.default.svc.cluster.local:6379 + +``` + +Once created, run `kubectl apply -f redis_client.yaml`. + + +## Deploying Redis + +Create a Kubernetes manifest file named `redis_db.yaml` with the following code: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + labels: + app: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis + ports: + - containerPort: 6379 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: redis +spec: + selector: + app: redis + ports: + - protocol: TCP + port: 6379 + targetPort: 6379 +``` + +Once created, run `kubectl apply -f redis_db.yaml`. + +## Interacting with the Spin app + +In your terminal run `kubectl port-forward svc/redis-spinapp 3000:80`, then in a different terminal window, try the below commands: + +### Place a key-value pair in Redis + +```bash +curl --request PUT --data-binary "Hello, world\!" -H 'x-key: helloKey' localhost:3000 +``` + +### Retrieve a value from Redis + +```bash +curl -H 'x-key: helloKey' localhost:3000 +``` + +### Delete a value from Redis + +```bash +curl --request DELETE -H 'x-key: helloKey' localhost:3000 +``` \ No newline at end of file diff --git a/apps/redis-sample/go.mod b/apps/redis-sample/go.mod new file mode 100644 index 00000000..c3ef841d --- /dev/null +++ b/apps/redis-sample/go.mod @@ -0,0 +1,7 @@ +module github.com/spin_redis_sample + +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/redis-sample/go.sum b/apps/redis-sample/go.sum new file mode 100644 index 00000000..c283accd --- /dev/null +++ b/apps/redis-sample/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/redis-sample/main.go b/apps/redis-sample/main.go new file mode 100644 index 00000000..040f023b --- /dev/null +++ b/apps/redis-sample/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "fmt" + "io" + "net/http" + + spinhttp "github.com/fermyon/spin/sdk/go/v2/http" + "github.com/fermyon/spin/sdk/go/v2/redis" + "github.com/fermyon/spin/sdk/go/v2/variables" +) + +var rdb *redis.Client + +func init() { + spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) { + redisEndpoint, err := variables.Get("redis_endpoint") + if err != nil { + http.Error(w, "unable to parse variable 'redis_endpoint'", http.StatusInternalServerError) + return + } + + if redisEndpoint == "" { + http.Error(w, "cannot find 'redis_endpoint' environment variable", http.StatusInternalServerError) + return + } + + rdb = redis.NewClient(redisEndpoint) + + reqKey := r.Header.Get("x-key") + if reqKey == "" { + http.Error(w, "you must include the 'x-key' header in your request", http.StatusBadRequest) + return + } + + if r.Method == "GET" { + value, err := rdb.Get(reqKey) + if err != nil { + http.Error(w, fmt.Sprintf("no value found for key '%s'", reqKey), http.StatusNotFound) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(value) + return + + } else if r.Method == "PUT" { + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, fmt.Sprintf("error reading request body: %w", err), http.StatusInternalServerError) + } + defer r.Body.Close() + + if err := rdb.Set(reqKey, bodyBytes); err != nil { + http.Error(w, fmt.Sprintf("unable to add value for key '%s' to database: %w", reqKey, err), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusCreated) + return + + } else if r.Method == "DELETE" { + _, err := rdb.Del(reqKey) + if err != nil { + http.Error(w, fmt.Sprintf("error deleting value for key '%w'", err), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + return + + } else { + http.Error(w, fmt.Sprintf("method %q is not supported, so please try again using 'GET' or 'PUT' for the HTTP method", r.Method), http.StatusBadRequest) + return + } + }) +} + +func main() {} diff --git a/apps/redis-sample/spin.toml b/apps/redis-sample/spin.toml new file mode 100644 index 00000000..3765fb39 --- /dev/null +++ b/apps/redis-sample/spin.toml @@ -0,0 +1,24 @@ +spin_manifest_version = 2 + +[application] +name = "spin-redis-sample" +version = "0.1.0" +authors = ["Fermyon Engineering Team "] + +[variables] +redis_endpoint = {required = true} + +[[trigger.http]] +route = "/..." +component = "spin-redis-sample" + +[component.spin-redis-sample] +source = "main.wasm" +allowed_outbound_hosts = ["redis://*"] + +[component.spin-redis-sample.build] +command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go" +watch = ["**/*.go", "go.mod"] + +[component.spin-redis-sample.variables] +redis_endpoint = "{{ redis_endpoint }}" From bc6854f6f9915f734eb9b4c18a860ea09dbb0fa5 Mon Sep 17 00:00:00 2001 From: Andrew Steurer Date: Tue, 13 Aug 2024 10:29:45 -0600 Subject: [PATCH 2/3] removing .gitignore Signed-off-by: Andrew Steurer --- apps/redis-sample/.gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 apps/redis-sample/.gitignore diff --git a/apps/redis-sample/.gitignore b/apps/redis-sample/.gitignore deleted file mode 100644 index b5650104..00000000 --- a/apps/redis-sample/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main.wasm -.spin/ From e5ba0774dba55907a677509e955315d64d31dccd Mon Sep 17 00:00:00 2001 From: Andrew Steurer Date: Tue, 13 Aug 2024 15:39:09 -0600 Subject: [PATCH 3/3] fixing formatting issues Signed-off-by: Andrew Steurer --- apps/redis-sample/README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/redis-sample/README.md b/apps/redis-sample/README.md index bbbd4459..c046a28b 100644 --- a/apps/redis-sample/README.md +++ b/apps/redis-sample/README.md @@ -20,12 +20,10 @@ spec: variables: - name: redis_endpoint value: redis://redis.default.svc.cluster.local:6379 - ``` Once created, run `kubectl apply -f redis_client.yaml`. - ## Deploying Redis Create a Kubernetes manifest file named `redis_db.yaml` with the following code: @@ -51,10 +49,9 @@ spec: - name: redis image: redis ports: - - containerPort: 6379 + - containerPort: 6379 --- - apiVersion: v1 kind: Service metadata: @@ -90,4 +87,4 @@ curl -H 'x-key: helloKey' localhost:3000 ```bash curl --request DELETE -H 'x-key: helloKey' localhost:3000 -``` \ No newline at end of file +```