Skip to content

Commit a7a0532

Browse files
authored
Enable OpenWhisk in CI (#302)
* [ci] Add test job to verify if OpenWhisk works * Fix Dockerfile path for main Python action * [ci] Try to run OpenWhisk on GH Actions * [system] Try to fix bug in cache initialization for a new deployment type * [ci] Try to get more diagnostics from OpenWhisk * [ci] Increase max memory of action in Whisk * [ci] Fix getting failed activations from openWhisk * [ci] Fix wrong limit flag * [ci] Fix parsing activation IDs * [whisk] Try to fix the pulling of image from a different registry * [storage] implement missing minio method * Remove logging for MinIO client creation Removed logging of MinIO client connection details. * Remove extra logging * [dev] Remove unnecessay code * [storage] Fix cache loading * [ci] Extend OpenWhisk with more languages to test * [ci] Minor correctness fixes * [openwhiks] Update configs * [openwhisk] Attemp to fix the format of Java output * [openwhisk] Attempt to fix the Node.js wrapper * [ci] Remove old branch in clone * [openwhisk] Reexport nosql args
1 parent 621ba5c commit a7a0532

9 files changed

Lines changed: 358 additions & 31 deletions

File tree

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
name: OpenWhisk Regression on kind
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- master
8+
- 'feature/**'
9+
10+
jobs:
11+
regression:
12+
strategy:
13+
matrix:
14+
include:
15+
- language: python
16+
version: "3.11"
17+
architecture: "x64"
18+
- language: nodejs
19+
version: "20"
20+
architecture: "x64"
21+
- language: java
22+
version: "8"
23+
architecture: "x64"
24+
fail-fast: false
25+
26+
name: ${{ matrix.language }} ${{ matrix.version }} ${{ matrix.architecture }}
27+
runs-on: ubuntu-latest
28+
timeout-minutes: 90
29+
30+
env:
31+
KIND_VERSION: v0.27.0
32+
KIND_NODE_IMAGE: kindest/node:v1.31.4
33+
REGISTRY_NAME: kind-registry
34+
REGISTRY_PORT: "5000"
35+
OW_NAMESPACE: openwhisk
36+
OW_API_PORT: "31001"
37+
WSK_AUTH: 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
38+
OW_ACTION_MEMORY_MAX: "3072"
39+
RESOURCE_PREFIX: ci
40+
LANGUAGE: ${{ matrix.language }}
41+
LANGUAGE_VERSION: ${{ matrix.version }}
42+
ARCHITECTURE: ${{ matrix.architecture }}
43+
44+
steps:
45+
- name: Checkout code
46+
uses: actions/checkout@v4
47+
with:
48+
submodules: recursive
49+
50+
#- name: Reclaim disk
51+
# run: |
52+
# sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
53+
# /opt/hostedtoolcache/CodeQL
54+
# docker image prune -af
55+
# df -h
56+
57+
- name: Install kind
58+
run: |
59+
curl -fsSLo kind https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64
60+
sudo install -m 0755 kind /usr/local/bin/kind
61+
kind version
62+
63+
- name: Install kubectl
64+
run: |
65+
KCTL=$(curl -fsSL https://dl.k8s.io/release/stable.txt)
66+
curl -fsSLo kubectl https://dl.k8s.io/release/${KCTL}/bin/linux/amd64/kubectl
67+
sudo install -m 0755 kubectl /usr/local/bin/kubectl
68+
kubectl version --client
69+
70+
- name: Install helm
71+
uses: azure/setup-helm@v4
72+
with:
73+
version: latest
74+
75+
- name: Install wsk CLI
76+
run: |
77+
curl -fsSLo wsk.tgz \
78+
https://github.com/apache/openwhisk-cli/releases/download/1.2.0/OpenWhisk_CLI-1.2.0-linux-amd64.tgz
79+
tar -xzf wsk.tgz wsk
80+
sudo install -m 0755 wsk /usr/local/bin/wsk
81+
82+
- name: Install uv
83+
uses: astral-sh/setup-uv@v4
84+
85+
- name: Create virtual environment and install SeBS
86+
run: |
87+
uv venv
88+
uv pip install .
89+
90+
- name: Start local registry
91+
run: |
92+
docker run -d --restart=always \
93+
-p 127.0.0.1:${REGISTRY_PORT}:5000 \
94+
--name ${REGISTRY_NAME} \
95+
registry:2
96+
97+
- name: Create kind cluster
98+
run: |
99+
cat > kind-config.yaml <<EOF
100+
kind: Cluster
101+
apiVersion: kind.x-k8s.io/v1alpha4
102+
containerdConfigPatches:
103+
- |-
104+
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${REGISTRY_PORT}"]
105+
endpoint = ["http://${REGISTRY_NAME}:5000"]
106+
nodes:
107+
- role: control-plane
108+
- role: worker
109+
labels:
110+
openwhisk-role: core
111+
extraPortMappings:
112+
- containerPort: ${OW_API_PORT}
113+
hostPort: ${OW_API_PORT}
114+
protocol: TCP
115+
- role: worker
116+
labels:
117+
openwhisk-role: invoker
118+
EOF
119+
kind create cluster --image ${KIND_NODE_IMAGE} --config kind-config.yaml --wait 5m
120+
121+
- name: Attach registry to kind network
122+
run: |
123+
if [ "$(docker inspect -f '{{json .NetworkSettings.Networks.kind}}' ${REGISTRY_NAME})" = "null" ]; then
124+
docker network connect kind ${REGISTRY_NAME}
125+
fi
126+
127+
- name: Start Minio and ScyllaDB
128+
run: |
129+
set -o pipefail
130+
uv run sebs storage start all configs/storage.json --output-json storage.json
131+
132+
- name: Configure OpenWhisk
133+
run: |
134+
WORKER_IP=$(kubectl get node kind-worker -o jsonpath='{.status.addresses[0].address}')
135+
HOST_IP=$(hostname -I | awk '{print $1}')
136+
echo "WORKER_IP=${WORKER_IP}" >> $GITHUB_ENV
137+
echo "HOST_IP=${HOST_IP}" >> $GITHUB_ENV
138+
139+
git clone --depth 1 https://github.com/apache/openwhisk-deploy-kube.git /tmp/ow
140+
141+
sed -i '/^ memory:$/,/^ concurrency:$/ s/^\( max:\) ".*"/\1 "'"${OW_ACTION_MEMORY_MAX}"' MB"/' \
142+
/tmp/ow/helm/openwhisk/values.yaml
143+
144+
helm install owdev /tmp/ow/helm/openwhisk \
145+
-n ${OW_NAMESPACE} --create-namespace \
146+
-f /tmp/ow/deploy/kind/mycluster.yaml
147+
148+
for i in $(seq 1 60); do
149+
kubectl get job owdev-install-packages -n ${OW_NAMESPACE} >/dev/null 2>&1 && break
150+
sleep 2
151+
done
152+
kubectl wait --for=condition=complete --timeout=20m \
153+
job/owdev-install-packages -n ${OW_NAMESPACE}
154+
kubectl get pods -n ${OW_NAMESPACE}
155+
156+
wsk property set \
157+
--apihost https://${WORKER_IP}:${OW_API_PORT} \
158+
--auth ${WSK_AUTH}
159+
160+
- name: Create OpenWhisk regression config
161+
run: |
162+
jq \
163+
--arg host_ip "${HOST_IP}" \
164+
'
165+
.object.minio.address = ($host_ip + ":" + (.object.minio.mapped_port | tostring))
166+
| .nosql.scylladb.address = ($host_ip + ":" + (.nosql.scylladb.mapped_port | tostring))
167+
' storage.json > storage-openwhisk.json
168+
169+
jq \
170+
--arg language "${LANGUAGE}" \
171+
--arg version "${LANGUAGE_VERSION}" \
172+
--arg architecture "${ARCHITECTURE}" \
173+
--arg registry "localhost:${REGISTRY_PORT}" \
174+
--slurpfile storage storage-openwhisk.json \
175+
'
176+
.experiments.architecture = $architecture
177+
| .experiments.runtime.language = $language
178+
| .experiments.runtime.version = $version
179+
| .deployment.openwhisk.docker_registry.registry = $registry
180+
| .deployment.openwhisk.storage = $storage[0]
181+
' configs/openwhisk.json > openwhisk-regression.json
182+
183+
- name: Run OpenWhisk regression
184+
id: regression
185+
continue-on-error: true
186+
timeout-minutes: 45
187+
run: |
188+
set -o pipefail
189+
uv run sebs benchmark regression test \
190+
--config openwhisk-regression.json \
191+
--deployment openwhisk \
192+
--language ${LANGUAGE} \
193+
--language-version ${LANGUAGE_VERSION} \
194+
--architecture ${ARCHITECTURE} \
195+
--selected-architecture \
196+
--resource-prefix ${RESOURCE_PREFIX} \
197+
--filter-output
198+
199+
- name: Collect OpenWhisk action logs
200+
if: steps.regression.outcome == 'failure'
201+
run: |
202+
mkdir -p diagnostics
203+
wsk -i action list > diagnostics/action-list.txt 2>&1 || true
204+
awk 'NF > 0 && $1 != "actions" && $1 != "ok:" {print $1}' diagnostics/action-list.txt \
205+
> diagnostics/actions.txt
206+
207+
if [ ! -s diagnostics/actions.txt ]; then
208+
echo "No OpenWhisk actions returned by 'wsk action list'." \
209+
> diagnostics/no-actions.txt
210+
fi
211+
212+
while IFS= read -r action; do
213+
[ -n "${action}" ] || continue
214+
safe_name=$(echo "${action}" | tr '/.' '__')
215+
{
216+
echo "=== action ${action} ==="
217+
wsk -i action get "${action}" || true
218+
echo
219+
echo "=== activations for ${action} ==="
220+
wsk -i activation list "${action}" || true
221+
echo
222+
} | tee "diagnostics/${safe_name}.txt"
223+
224+
activation_ids=$(
225+
wsk -i activation list "${action}" 2>/dev/null \
226+
| awk '$3 ~ /^[0-9a-f]{32}$/ {print $3}'
227+
)
228+
for activation_id in ${activation_ids}; do
229+
{
230+
echo "=== activation ${activation_id} logs for ${action} ==="
231+
wsk -i activation logs "${activation_id}" || true
232+
echo
233+
echo "=== activation ${activation_id} record for ${action} ==="
234+
wsk -i activation get "${activation_id}" || true
235+
echo
236+
} >> "diagnostics/${safe_name}.txt"
237+
done
238+
done < diagnostics/actions.txt
239+
240+
- name: Diagnostics on failure
241+
if: failure()
242+
run: |
243+
mkdir -p diagnostics
244+
{
245+
echo "=== pods ==="
246+
kubectl get pods -A
247+
echo
248+
echo "=== invoker logs ==="
249+
kubectl logs -n ${OW_NAMESPACE} -l name=owdev-invoker --tail=200 || true
250+
echo
251+
echo "=== controller logs ==="
252+
kubectl logs -n ${OW_NAMESPACE} -l name=owdev-controller --tail=200 || true
253+
echo
254+
echo "=== registry contents ==="
255+
curl -s http://localhost:${REGISTRY_PORT}/v2/_catalog || true
256+
echo
257+
echo "=== registry container logs ==="
258+
docker logs ${REGISTRY_NAME} --tail=100 || true
259+
} | tee diagnostics/cluster-diagnostics.txt
260+
261+
- name: Upload regression artifacts
262+
if: always()
263+
uses: actions/upload-artifact@v4
264+
with:
265+
name: openwhisk-regression-artifacts-${{ matrix.language }}-${{ matrix.version }}-${{ matrix.architecture }}
266+
path: |
267+
diagnostics/
268+
regression-cache/
269+
storage.json
270+
storage-openwhisk.json
271+
openwhisk-regression.json
272+
regression_*.json
273+
if-no-files-found: ignore
274+
275+
- name: Fail job when regression failed
276+
if: steps.regression.outcome == 'failure'
277+
run: exit 1
278+
279+
- name: Cleanup
280+
if: always()
281+
run: |
282+
uv run sebs storage stop all storage.json || true
283+
kind delete cluster || true
284+
docker rm -f ${REGISTRY_NAME} || true

benchmarks/wrappers/openwhisk/java/src/main/java/org/serverlessbench/Handler.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,14 @@ public static JsonObject main(JsonObject args) {
3838
requestId = "";
3939
}
4040

41-
// Create result wrapper matching Python format
42-
JsonObject logData = new JsonObject();
43-
logData.add("result", gson.toJsonTree(result).getAsJsonObject());
44-
4541
JsonObject jsonResult = new JsonObject();
4642
jsonResult.addProperty("begin", formattedBegin);
4743
jsonResult.addProperty("end", formattedEnd);
4844
jsonResult.addProperty("request_id", requestId);
4945
jsonResult.addProperty("results_time", 0);
5046
jsonResult.addProperty("is_cold", ColdStartTracker.isCold());
5147
jsonResult.addProperty("container_id", containerId);
52-
jsonResult.add("result", logData);
48+
jsonResult.add("result", gson.toJsonTree(result).getAsJsonObject());
5349

5450
return jsonResult;
5551
}

benchmarks/wrappers/openwhisk/nodejs/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ async function main(args) {
99
delete args[arg];
1010
});
1111

12+
Object.keys(args).forEach(function(arg) {
13+
if (arg.includes("NOSQL_STORAGE_")) {
14+
process.env[arg] = args[arg];
15+
delete args[arg];
16+
}
17+
});
18+
1219
var func = require('/function/function.js');
1320
var begin = Date.now() / 1000;
1421
var start = process.hrtime();

benchmarks/wrappers/openwhisk/nodejs/nosql.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const aws = require("aws-sdk");
44

55
class nosql {
66
constructor() {
7-
if (process.env.NOSQL_STORAGE_TYPE !== "scylladb") {
7+
if (process.env['NOSQL_STORAGE_TYPE'] !== "scylladb") {
88
throw new Error(`Unsupported NoSQL storage type: ${process.env.NOSQL_STORAGE_TYPE}!`);
99
}
1010
this.client = new aws.DynamoDB.DocumentClient({

configs/openwhisk.json

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
"update_storage": false,
55
"download_results": false,
66
"deployment": "openwhisk",
7+
"architecture": "x64",
8+
"system_variant": "container",
79
"runtime": {
810
"language": "python",
9-
"version": "3.6"
11+
"version": "3.11"
1012
}
1113
},
1214
"deployment": {
@@ -16,8 +18,27 @@
1618
"removeCluster": false,
1719
"wskBypassSecurity": "true",
1820
"wskExec": "wsk",
19-
"experimentalManifest": "false",
20-
"dockerhubRepository": ""
21+
"experimentalManifest": false,
22+
"dockerhubRepository": null,
23+
"docker_registry": {
24+
"registry": "",
25+
"username": "",
26+
"password": ""
27+
},
28+
"storage": {
29+
"object": {
30+
"type": "minio",
31+
"minio": {
32+
"address": "",
33+
"mapped_port": -1,
34+
"access_key": "",
35+
"secret_key": "",
36+
"instance_id": "",
37+
"input_buckets": [],
38+
"output_buckets": []
39+
}
40+
}
41+
}
2142
}
2243
}
2344
}

sebs/cache.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,12 @@ def add_code_package(
844844

845845
keys = [*base_keys, *extra_keys]
846846
language = keys[0]
847+
848+
# if we produced no code package (which can happen, e.g., on OpenWhisk),
849+
# there will be no "deployment" etrny at all:
850+
851+
if deployment_name not in cached_config:
852+
cached_config[deployment_name] = {language: config}
847853
if language in cached_config[deployment_name]:
848854
# language known - add code package,
849855
# but do not overwrite existing entries

0 commit comments

Comments
 (0)