Skip to content
Draft
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
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
template/
build/
context/
.secrets/
*.tar.gz
__pycache__/
.pytest_cache/
.tox/
*.py[cod]
node_modules/
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
# function-patterns
Patterns for OpenFaaS Functions

Examples for the OpenFaaS documentation. Each example is
self-contained: it has its own `stack.yaml` and function directories, so it
can be built and deployed on its own.

## Examples

- [**Director**](https://docs.openfaas.com/languages/patterns/director/) — [Go](go/director/) · [Python](python/director/) · [Node.js](node/director/)
- [**Fan-out**](https://docs.openfaas.com/languages/patterns/fan-out/) — [Go](go/fan-out/) · [Python](python/fan-out/) · [Node.js](node/fan-out/)
- [**Singleton**](https://docs.openfaas.com/languages/patterns/singleton/) — [Go](go/singleton/) · [Python](python/singleton/)

## Prerequisites

- [faas-cli](https://docs.openfaas.com/cli/install/)
- Docker, for building images
- A running OpenFaaS cluster, for e2e testing — set `OPENFAAS_URL` and
provide credentials, or point the `gateway` in each `stack.yaml` at a
local gateway

## Build and test an example

```bash
cd go/director
faas-cli up --tag=digest
```

`faas-cli up` builds each function image, pushes it to the registry, and
deploys it to the gateway.

The examples default to the public [ttl.sh](https://ttl.sh) registry so they
can be pushed without credentials. The registry and owner are overridable
through environment variables:

```bash
REGISTRY=ghcr.io OWNER=welteki faas-cli up
```
3 changes: 3 additions & 0 deletions go/director/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
template
build
.secrets
56 changes: 56 additions & 0 deletions go/director/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Director pattern — telemetry triage

The director function `telemetry-workflow` validates a sensor reading, invokes
temperature and battery checks in parallel, and combines their results into an
`ok` or `alert` response:

```
telemetry-workflow ◄── director: owns the workflow and handles errors
├── 1. invoke ──► validate-reading
├── 2. invoke in parallel
│ ├──► temperature-check ──┐
│ └──► battery-check ──────┤
│◄──── check results ────────────┘
└── 3. combine results and return ok or alert
```

Documentation: [Director pattern](https://docs.openfaas.com/languages/patterns/director/)

## Build and deploy

```bash
faas-cli up --tag=digest
```

The stage functions are invoked through the gateway. The `stage_timeout`
environment variable limits each downstream call, while the watchdog timeout
variables limit the complete director invocation.

## Invoke

```bash
curl -s http://127.0.0.1:8080/function/telemetry-workflow \
-H "Content-Type: application/json" \
-d '{"device_id":"pump-17","temperature_c":82.4,"battery_percent":12}' | \
jq
```

Example output:

```json
{
"device_id": "pump-17",
"status": "alert",
"temperature": {
"value_c": 82.4,
"threshold_c": 75,
"alert": true
},
"battery": {
"value_percent": 12,
"threshold_percent": 20,
"alert": true
},
"duration_ms": 4
}
```
3 changes: 3 additions & 0 deletions go/director/battery-check/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module handler/function

go 1.25.0
35 changes: 35 additions & 0 deletions go/director/battery-check/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package function

import (
"encoding/json"
"net/http"
)

const thresholdPercent = 20

type Reading struct {
BatteryPercent int `json:"battery_percent"`
}

type Response struct {
ValuePercent int `json:"value_percent"`
ThresholdPercent int `json:"threshold_percent"`
Alert bool `json:"alert"`
}

func Handle(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()

var reading Reading
if err := json.NewDecoder(r.Body).Decode(&reading); err != nil {
http.Error(w, "expected a JSON sensor reading", http.StatusBadRequest)
return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(Response{
ValuePercent: reading.BatteryPercent,
ThresholdPercent: thresholdPercent,
Alert: reading.BatteryPercent < thresholdPercent,
})
}
44 changes: 44 additions & 0 deletions go/director/battery-check/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package function

import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)

func TestHandleBatteryThreshold(t *testing.T) {
tests := []struct {
name string
value int
alert bool
}{
{name: "below", value: 19, alert: true},
{name: "at threshold", value: 20, alert: false},
{name: "above", value: 21, alert: false},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(
`{"battery_percent":`+jsonNumber(test.value)+`}`,
))
recorder := httptest.NewRecorder()
Handle(recorder, req)

var response Response
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
t.Fatal(err)
}
if response.Alert != test.alert {
t.Fatalf("want alert %t, got %t", test.alert, response.Alert)
}
})
}
}

func jsonNumber(value int) string {
data, _ := json.Marshal(value)
return string(data)
}
29 changes: 29 additions & 0 deletions go/director/stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
version: 1.0
provider:
name: openfaas
gateway: http://127.0.0.1:8080
functions:
telemetry-workflow:
lang: golang-middleware
handler: ./telemetry-workflow
image: ${REGISTRY:-ttl.sh}/${OWNER:-openfaas-examples}/telemetry-workflow:latest
environment:
stage_timeout: 5s
exec_timeout: 15s
read_timeout: 16s
write_timeout: 16s

validate-reading:
lang: golang-middleware
handler: ./validate-reading
image: ${REGISTRY:-ttl.sh}/${OWNER:-openfaas-examples}/validate-reading:latest

temperature-check:
lang: golang-middleware
handler: ./temperature-check
image: ${REGISTRY:-ttl.sh}/${OWNER:-openfaas-examples}/temperature-check:latest

battery-check:
lang: golang-middleware
handler: ./battery-check
image: ${REGISTRY:-ttl.sh}/${OWNER:-openfaas-examples}/battery-check:latest
3 changes: 3 additions & 0 deletions go/director/telemetry-workflow/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module handler/function

go 1.25.0
Loading