Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding redis-sample #292

Merged
merged 3 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/sample-apps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}

Expand Down
90 changes: 90 additions & 0 deletions apps/redis-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 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
```
7 changes: 7 additions & 0 deletions apps/redis-sample/go.mod
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions apps/redis-sample/go.sum
Original file line number Diff line number Diff line change
@@ -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=
79 changes: 79 additions & 0 deletions apps/redis-sample/main.go
Original file line number Diff line number Diff line change
@@ -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() {}
24 changes: 24 additions & 0 deletions apps/redis-sample/spin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
spin_manifest_version = 2

[application]
name = "spin-redis-sample"
version = "0.1.0"
authors = ["Fermyon Engineering Team <engineering@fermyon.com>"]

[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 }}"
Loading