Skip to content

Commit ab0b339

Browse files
authored
add path-parameters-function go example (#84)
* add path-parameters-function go example Signed-off-by: Lize Cai <lize.cai@sap.com> * update ff and builder versions and update the access method to gateway Signed-off-by: Lize Cai <lize.cai@sap.com> Signed-off-by: Lize Cai <lize.cai@sap.com>
1 parent 820d804 commit ab0b339

5 files changed

Lines changed: 314 additions & 0 deletions

File tree

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Path Parameters Function Go
2+
3+
## Prerequisites
4+
5+
### OpenFunction
6+
7+
You can refer to the [Installation Guide](https://github.com/OpenFunction/OpenFunction#install-openfunction) to setup OpenFunction.
8+
9+
## Run it locally
10+
11+
Build the function locally
12+
13+
```sh
14+
pack build sample-go-path-params-func --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="pathParametersFunction" --env FUNC_CLEAR_SOURCE=true
15+
```
16+
17+
Run the function
18+
19+
```sh
20+
docker compose up
21+
```
22+
23+
Send a request
24+
25+
```sh
26+
27+
# http
28+
curl -X POST "http://localhost:8080/hello/openfunction"
29+
# {"hello":"openfunction"}%
30+
31+
# cloudevent
32+
curl -X POST "http://localhost:8080/foo/openfunction" \
33+
-H "Content-Type: application/cloudevents+json" \
34+
-d '{"specversion":"1.0","type":"dev.knative.samples.helloworld","source":"dev.knative.samples/helloworldsource","id":"536808d3-88be-4077-9d7a-a3f162705f79","data":{"data":"hello"}}'
35+
# in docker compose terminal:
36+
# cloudevent - Data: {"{\"data\":\"hello\"}":"openfunction"}
37+
38+
39+
# http
40+
curl -X POST "http://localhost:8080/bar/openfunction" \
41+
-d 'hello'
42+
# {"hello":"openfunction"}%
43+
44+
# Structured CloudEvent
45+
curl -X POST "http://localhost:8080/bar/openfunction" \
46+
-H "Content-Type: application/cloudevents+json" \
47+
-d '{"specversion":"1.0","type":"dev.knative.samples.helloworld","source":"dev.knative.samples/helloworldsource","id":"536808d3-88be-4077-9d7a-a3f162705f79","data":{"data":"hello"}}'
48+
# {"{\"data\":\"hello\"}":"openfunction"}%
49+
50+
# Binary CloudEvent
51+
curl "http://localhost:8080/bar/openfunction" \
52+
-X POST \
53+
-H "Ce-Specversion: 1.0" \
54+
-H "Ce-Type: dev.knative.samples.helloworld" \
55+
-H "Ce-Source: dev.knative.samples/helloworldsource" \
56+
-H "Ce-Subject: 123" \
57+
-H "Ce-Id: 536808d3-88be-4077-9d7a-a3f162705f79" \
58+
-H "Content-Type: application/json" \
59+
-d '{"data":"hello"}'
60+
# {"{\"data\":\"hello\"}":"openfunction"}%
61+
```
62+
63+
64+
## Deployment
65+
66+
1. Create secret
67+
68+
Generate a secret to access your container registry, such as one on [Docker Hub](https://hub.docker.com/) or [Quay.io](https://quay.io/).
69+
You can create this secret by editing the ``REGISTRY_SERVER``, ``REGISTRY_USER`` and ``REGISTRY_PASSWORD`` fields in following command, and then run it.
70+
71+
```bash
72+
REGISTRY_SERVER=https://index.docker.io/v1/ REGISTRY_USER=<your_registry_user> REGISTRY_PASSWORD=<your_registry_password>
73+
kubectl create secret docker-registry push-secret \
74+
--docker-server=$REGISTRY_SERVER \
75+
--docker-username=$REGISTRY_USER \
76+
--docker-password=$REGISTRY_PASSWORD
77+
```
78+
79+
2. Create function
80+
81+
For sample function below, modify the ``spec.image`` field in ``function-sample.yaml`` to your own container registry address:
82+
83+
```yaml
84+
apiVersion: core.openfunction.io/v1beta1
85+
kind: Function
86+
metadata:
87+
name: function-sample
88+
spec:
89+
image: "<your registry name>/sample-go-path-params-func:latest"
90+
```
91+
92+
Use the following command to create this Function:
93+
94+
```shell
95+
kubectl apply -f function-sample.yaml
96+
```
97+
98+
3. Access function
99+
100+
You can observe the process of a function with the following command:
101+
102+
```shell
103+
kubectl get functions.core.openfunction.io
104+
105+
NAME BUILDSTATE SERVINGSTATE BUILDER SERVING URL AGE
106+
function-sample Succeeded Running builder-jgnzp serving-gsx8g http://function-sample.default.svc.cluster.local/ 56s
107+
```
108+
109+
The `Function.status.addresses` field provides various methods for accessing functions.
110+
Get `Function` addresses by running following command:
111+
```shell
112+
kubectl get function function-sample -o=jsonpath='{.status.addresses}'
113+
```
114+
You will get the following address:
115+
```json
116+
[{"type":"External","value":"http://function-sample.default.ofn.io/"},
117+
{"type":"Internal","value":"http://function-sample.default.svc.cluster.local/"}]
118+
```
119+
120+
> You can use the following command to create a pod in the cluster and access the function from the pod:
121+
>
122+
> ```shell
123+
> kubectl run curl --image=radial/busyboxplus:curl -i --tty
124+
> ```
125+
Access functions by the internal address:
126+
```shell
127+
[ root@curl:/ ]$ curl http://function-sample.default.svc.cluster.local/hello/openfunction
128+
{"hello":"openfunction"}%
129+
```
130+
131+
Access functions by the external address:
132+
> To access the function via the Address of type `External` in `Funtion.status`, you should configure local domain first, see [Configure Local Domain](https://openfunction.dev/docs/concepts/networking/local-domain).
133+
```shell
134+
[ root@curl:/ ]$ curl http://function-sample.default.ofn.io/hello/openfunction
135+
{"hello":"openfunction"}%
136+
```
137+
138+
There is also an alternative way to trigger the function via the access address provided by the Knative Services:
139+
140+
```shell
141+
kubectl get ksvc
142+
143+
NAME URL LATESTCREATED LATESTREADY READY REASON
144+
serving-gsx8g-ksvc-6fv9l http://serving-gsx8g-ksvc-6fv9l.default.<external-ip>.sslip.io serving-gsx8g-ksvc-6fv9l-v100 serving-gsx8g-ksvc-6fv9l-v100 True
145+
```
146+
147+
Or get the service address directly with the following command:
148+
149+
> where` <external-ip> `indicates the external address of your gateway service.
150+
>
151+
> You can do a simple configuration to use the node ip as the `<external-ip>` as follows (Assuming you are using Kourier as network layer of Knative). Where `1.2.3.4` can be replaced by your node ip.
152+
>
153+
> ```shell
154+
> kubectl patch svc -n kourier-system kourier \
155+
> -p '{"spec": {"type": "LoadBalancer", "externalIPs": ["1.2.3.4"]}}'
156+
>
157+
> kubectl patch configmap/config-domain -n knative-serving \
158+
> --type merge --patch '{"data":{"1.2.3.4.sslip.io":""}}'
159+
> ```
160+
161+
```shell
162+
kubectl get ksvc serving-gsx8g-ksvc-6fv9l -o jsonpath={.status.url}
163+
164+
http://serving-gsx8g-ksvc-6fv9l.default.<external-ip>.sslip.io
165+
```
166+
167+
Access the above service address via commands such as ``curl``:
168+
169+
```shell
170+
curl http://serving-gsx8g-ksvc-6fv9l.default.<external-ip>.sslip.io/hello/openfunction
171+
172+
{"hello":"openfunction"}%
173+
```
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# https://github.com/dapr/samples/tree/master/hello-docker-compose
2+
version: '3'
3+
services:
4+
############################
5+
# myfunction + Dapr sidecar
6+
############################
7+
myfunction:
8+
image: sample-go-path-params-func
9+
ports:
10+
- "50001:50001" # Dapr instances communicate over gRPC so we need to expose the gRPC port
11+
- "8080:8080"
12+
environment:
13+
FUNC_CONTEXT: "{\"name\":\"pathParametersFunction\",\"version\":\"v1.0.0\",\"port\":\"8080\",\"runtime\":\"Knative\"}"
14+
CONTEXT_MODE: "self-host"
15+
depends_on:
16+
- placement
17+
networks:
18+
- hello-dapr
19+
myfunction-dapr:
20+
image: "daprio/daprd:edge"
21+
command: [
22+
"./daprd",
23+
"-app-id", "pathParametersFunction",
24+
"-app-port", "3000",
25+
"-dapr-grpc-port", "50001",
26+
"-placement-host-address", "placement:50006" # Dapr's placement service can be reach via the docker DNS entry
27+
]
28+
depends_on:
29+
- myfunction
30+
network_mode: "service:myfunction" # Attach the myfunction-dapr service to the myfunction network namespace
31+
############################
32+
# Dapr placement service
33+
############################
34+
placement:
35+
image: "daprio/dapr"
36+
command: ["./placement", "-port", "50006"]
37+
ports:
38+
- "50006:50006"
39+
networks:
40+
- hello-dapr
41+
42+
networks:
43+
hello-dapr:
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
apiVersion: core.openfunction.io/v1beta1
2+
kind: Function
3+
metadata:
4+
name: function-sample
5+
spec:
6+
version: "v2.0.0"
7+
image: "<image-registry>/sample-go-path-params-func:v1"
8+
imageCredentials:
9+
name: push-secret
10+
port: 8080 # default to 8080
11+
build:
12+
builder: openfunction/builder-go:v2.4.0-1.17
13+
env:
14+
FUNC_NAME: "pathParametersFunction"
15+
FUNC_CLEAR_SOURCE: "true"
16+
srcRepo:
17+
url: "https://github.com/OpenFunction/samples.git"
18+
sourceSubPath: "functions/knative/path-parameters-function-go"
19+
revision: "main"
20+
serving:
21+
template:
22+
containers:
23+
- name: function
24+
imagePullPolicy: Always
25+
runtime: "knative"
26+
annotations:
27+
# to enable dapr manually
28+
# Dapr annotations: https://docs.dapr.io/reference/arguments-annotations-overview/
29+
dapr.io/enabled: "true"
30+
dapr.io/app-id: "function-sample-default"
31+
dapr.io/app-port: "8080" # must equal to function port
32+
dapr.io/dapr-grpc-port: "50001"
33+
dapr.io/log-as-json: "true"
34+
dapr.io/app-protocol: "grpc"
35+
dapr.io/enable-metrics: "true"
36+
dapr.io/metrics-port: "19090"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module example.com/hello
2+
3+
go 1.17
4+
5+
require github.com/OpenFunction/functions-framework-go v0.4.0
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package hello
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"net/http"
7+
8+
ofctx "github.com/OpenFunction/functions-framework-go/context"
9+
"github.com/OpenFunction/functions-framework-go/functions"
10+
cloudevents "github.com/cloudevents/sdk-go/v2"
11+
"k8s.io/klog/v2"
12+
)
13+
14+
func init() {
15+
functions.HTTP("Hello", hello,
16+
functions.WithFunctionPath("/hello/{name}"),
17+
functions.WithFunctionMethods("GET", "POST"),
18+
)
19+
20+
functions.CloudEvent("Foo", foo,
21+
functions.WithFunctionPath("/foo/{name}"),
22+
)
23+
24+
functions.OpenFunction("Bar", bar,
25+
functions.WithFunctionPath("/bar/{name}"),
26+
functions.WithFunctionMethods("GET", "POST"),
27+
)
28+
}
29+
30+
func hello(w http.ResponseWriter, r *http.Request) {
31+
vars := ofctx.VarsFromCtx(r.Context())
32+
response := map[string]string{
33+
"hello": vars["name"],
34+
}
35+
responseBytes, _ := json.Marshal(response)
36+
w.Header().Set("Content-type", "application/json")
37+
w.Write(responseBytes)
38+
}
39+
40+
func foo(ctx context.Context, ce cloudevents.Event) error {
41+
vars := ofctx.VarsFromCtx(ctx)
42+
response := map[string]string{
43+
string(ce.Data()): vars["name"],
44+
}
45+
responseBytes, _ := json.Marshal(response)
46+
klog.Infof("cloudevent - Data: %s", string(responseBytes))
47+
return nil
48+
}
49+
50+
func bar(ctx ofctx.Context, in []byte) (ofctx.Out, error) {
51+
vars := ofctx.VarsFromCtx(ctx.GetNativeContext())
52+
response := map[string]string{
53+
string(in): vars["name"],
54+
}
55+
responseBytes, _ := json.Marshal(response)
56+
return ctx.ReturnOnSuccess().WithData(responseBytes), nil
57+
}

0 commit comments

Comments
 (0)