Skip to content
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
10 changes: 9 additions & 1 deletion deploy/knative/README-kind.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,24 @@ curl -H 'Host: serverless-harness.default.example.com' \
## Options

```
--skip-build Do not build/load the harness image (use existing)
--build Force a local harness build from this checkout
--skip-build Do not build/pull the harness image (use existing dev.local tag)
--image <ref> Published harness image to pull (default: ghcr.io/rossoctl/serverless-harness:latest)
--cluster-name <name> Kind cluster name (default: sh-knative)
```

By default (no `--build`/`--skip-build`) the script **pulls the published harness image** and
loads it into the cluster, falling back to a local build only if the pull is unavailable
(offline or image missing). Use `--build` to always build from source.

Environment variables:

| Variable | Default | Description |
|----------|---------|-------------|
| `CLUSTER_NAME` | `sh-knative` | Kind cluster name |
| `KNATIVE_VERSION` | `v1.14.0` | Knative Serving version |
| `SH_IMAGE` | `ghcr.io/rossoctl/serverless-harness:latest` | Published harness image pulled by default (same as `--image`) |
| `FORCE_BUILD` | `false` | Force a local build (same as `--build`) |
| `KEDA_VERSION` | `v2.14.0` | KEDA version |

## Choosing the model
Expand Down
67 changes: 57 additions & 10 deletions deploy/knative/setup-kind.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,76 @@
# - ANTHROPIC_API_KEY env var set
#
# Usage:
# ./deploy/knative/setup-kind.sh [--skip-build] [--cluster-name <name>]
# ./deploy/knative/setup-kind.sh [--skip-build] [--build] [--image <ref>] [--cluster-name <name>]
#
# Harness image (dev.local/serverless-harness:local, referenced by service.yaml):
# default Pull the published image ($SH_IMAGE) and load it into kind; if the pull is
# unavailable (offline / image missing), transparently fall back to a local build.
# A first-time quickstart therefore needs no local Docker build.
# --build Force a local build from this checkout (use when testing local source changes).
# --skip-build Do neither; assume dev.local/serverless-harness:local is already loaded.
# --image <ref> / SH_IMAGE=<ref> Override the published image to pull.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
CLUSTER_NAME="${CLUSTER_NAME:-sh-knative}"
SKIP_BUILD="${SKIP_BUILD:-false}"
FORCE_BUILD="${FORCE_BUILD:-false}"
KNATIVE_VERSION="${KNATIVE_VERSION:-v1.14.0}"
# Published harness image pulled by default (public GHCR package under the rossoctl org).
SH_IMAGE="${SH_IMAGE:-ghcr.io/rossoctl/serverless-harness:latest}"
# Local tag the Knative manifests reference; the pulled/built image is (re)tagged to this.
LOCAL_IMAGE="${LOCAL_IMAGE:-dev.local/serverless-harness:local}"

# Parse args
for arg in "$@"; do
case $arg in
--skip-build) SKIP_BUILD=true ;;
--build) FORCE_BUILD=true ;;
--cluster-name) shift; CLUSTER_NAME="$1" ;;
--cluster-name=*) CLUSTER_NAME="${arg#*=}" ;;
--image) shift; SH_IMAGE="$1" ;;
--image=*) SH_IMAGE="${arg#*=}" ;;
esac
shift 2>/dev/null || true
done

# Provide the harness image referenced by service.yaml ($LOCAL_IMAGE). By default we pull the
# published image (fast path — no local build needed for a first-time quickstart) and fall back
# to a local build only if the pull is unavailable. --build forces a local build; --skip-build
# assumes the image is already loaded. Sets HARNESS_IMAGE_LOADED when it (re)loads an image.
ensure_harness_image() {
HARNESS_IMAGE_LOADED=false
if [ "$SKIP_BUILD" = "true" ]; then
echo "--- Skipping harness image build/pull (--skip-build): expecting $LOCAL_IMAGE preloaded ---"
return 0
fi
if [ "$FORCE_BUILD" != "true" ]; then
echo "--- Pulling published harness image: $SH_IMAGE ---"
if docker pull "$SH_IMAGE"; then
docker tag "$SH_IMAGE" "$LOCAL_IMAGE"
echo "--- Loading pulled image into kind ---"
kind load docker-image "$LOCAL_IMAGE" --name "$CLUSTER_NAME"
HARNESS_IMAGE_LOADED=true
return 0
fi
echo "--- Pull unavailable ($SH_IMAGE); falling back to a local build ---"
else
echo "--- Building serverless-harness image locally (--build) ---"
fi
docker build --load -t "$LOCAL_IMAGE" "$REPO_ROOT"
echo "--- Loading built image into kind ---"
kind load docker-image "$LOCAL_IMAGE" --name "$CLUSTER_NAME"
HARNESS_IMAGE_LOADED=true
}

# When sourced by tests (SH_SOURCE_ONLY=1), stop here so only the functions above are exposed.
if [ "${SH_SOURCE_ONLY:-0}" = "1" ]; then
return 0
fi

echo "=== M4 Knative Setup (cluster: $CLUSTER_NAME) ==="

# 1. Create kind cluster if it doesn't exist
Expand Down Expand Up @@ -124,13 +174,9 @@ kubectl -n default wait --for=condition=Ready pod -l "$POOL_SELECTOR" --timeout=
exit 1
}

# 7. Build and load harness image
if [ "$SKIP_BUILD" != "true" ]; then
echo "--- Building serverless-harness image ---"
docker build --load -t dev.local/serverless-harness:local "$REPO_ROOT"
echo "--- Loading image into kind ---"
kind load docker-image dev.local/serverless-harness:local --name "$CLUSTER_NAME"
fi
# 7. Provide the harness image ($LOCAL_IMAGE) referenced by service.yaml (pull by default,
# local build with --build, or reuse a preloaded image with --skip-build).
ensure_harness_image

# 8. Create LLM credentials secret (supports direct API key or gateway bridge)
if [ "${SH_AUTHBRIDGE:-0}" = "1" ]; then
Expand Down Expand Up @@ -268,8 +314,9 @@ kubectl apply -f "$SCRIPT_DIR/service.yaml"
# The image tag (dev.local/serverless-harness:local) is mutable, so re-applying an unchanged
# service spec does NOT roll a new Revision — Knative would keep serving the previous Revision
# (pinned to the OLD image digest) and a freshly built image would never be deployed. Force a new
# Revision by stamping a build marker into the template so the rebuilt image is always picked up.
if [ "$SKIP_BUILD" != "true" ]; then
# Revision by stamping a build marker into the template so the (re)loaded image is always picked up.
# Stamp whenever we loaded an image this run (pull or build); skip when --skip-build reused one.
if [ "${HARNESS_IMAGE_LOADED:-false}" = "true" ]; then
kubectl -n default patch ksvc serverless-harness --type merge \
-p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"deploy.sh/build-ts\":\"$(date +%s)\"}}}}}"
fi
Expand Down
90 changes: 90 additions & 0 deletions deploy/knative/tests/setup-kind-image.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# deploy/knative/tests/setup-kind-image.test.sh
#
# Unit test for ensure_harness_image() in ../setup-kind.sh — the harness-image
# provisioning decision (pull-by-default, --build force, --skip-build reuse, and
# transparent fallback to a local build when the pull is unavailable).
#
# No cluster required: docker/kind are mocked on PATH and only the call log is
# asserted. Run: bash deploy/knative/tests/setup-kind-image.test.sh
set -uo pipefail

SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/setup-kind.sh"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
export MOCK_LOG="$TMP/calls.log"

# --- mocks: log every invocation; `docker pull` exit code is controlled by MOCK_PULL_RC ---
mkdir -p "$TMP/bin"
cat > "$TMP/bin/docker" <<'EOF'
#!/usr/bin/env bash
echo "docker $*" >> "$MOCK_LOG"
[ "${1:-}" = "pull" ] && exit "${MOCK_PULL_RC:-0}"
exit 0
EOF
cat > "$TMP/bin/kind" <<'EOF'
#!/usr/bin/env bash
echo "kind $*" >> "$MOCK_LOG"
exit 0
EOF
chmod +x "$TMP/bin/docker" "$TMP/bin/kind"
export PATH="$TMP/bin:$PATH"

# Run ensure_harness_image() in a subshell with the given knobs; capture the call log.
# Args: SKIP_BUILD FORCE_BUILD MOCK_PULL_RC. Captured into named vars first because sourcing
# setup-kind.sh runs its arg-parse loop, which shifts away the subshell's positional params.
run_case() {
: > "$MOCK_LOG"
local sb="$1" fb="$2" prc="$3"
# SKIP_BUILD/FORCE_BUILD/SH_IMAGE/LOCAL_IMAGE/CLUSTER_NAME are consumed by
# ensure_harness_image, which is defined in the sourced setup-kind.sh — shellcheck
# can't follow the source, hence the directives below.
# shellcheck disable=SC2034
(
# shellcheck source=/dev/null
SH_SOURCE_ONLY=1 source "$SCRIPT"
SKIP_BUILD="$sb" FORCE_BUILD="$fb"
SH_IMAGE="ghcr.io/rossoctl/serverless-harness:latest"
LOCAL_IMAGE="dev.local/serverless-harness:local"
CLUSTER_NAME="sh-test"
export MOCK_PULL_RC="$prc"
ensure_harness_image
) >/dev/null
}

FAILS=0
# assert_grep / assert_absent <pattern> <message>
assert_grep() { if grep -q -- "$1" "$MOCK_LOG"; then echo " ok: $2"; else echo " FAIL: $2 (expected /$1/)"; FAILS=$((FAILS+1)); fi; }
assert_absent() { if grep -q -- "$1" "$MOCK_LOG"; then echo " FAIL: $2 (unexpected /$1/)"; FAILS=$((FAILS+1)); else echo " ok: $2"; fi; }

echo "case 1: default (pull succeeds) -> pull + tag + load, no build"
run_case false false 0
assert_grep "docker pull ghcr.io/rossoctl/serverless-harness:latest" "pulls the published image"
assert_grep "docker tag" "retags to the local image"
assert_grep "kind load" "loads into kind"
assert_absent "docker build" "does not build locally"

echo "case 2: default but pull fails -> falls back to local build"
run_case false false 1
assert_grep "docker pull" "attempts the pull first"
assert_grep "docker build" "falls back to a local build"
assert_grep "kind load" "loads the built image"

echo "case 3: --build (FORCE_BUILD) -> build only, never pulls"
run_case false true 0
assert_grep "docker build" "builds locally"
assert_grep "kind load" "loads the built image"
assert_absent "docker pull" "does not pull"

echo "case 4: --skip-build -> neither pull nor build"
run_case true false 0
assert_absent "docker" "runs no docker command"
assert_absent "kind" "runs no kind command"

echo
if [ "$FAILS" -eq 0 ]; then
echo "PASS: all ensure_harness_image cases"
else
echo "FAIL: $FAILS assertion(s) failed"
fi
exit "$FAILS"
15 changes: 7 additions & 8 deletions serverless-harness-demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ git clone --recurse-submodules https://github.com/kagenti/serverless-harness.git
./deploy/knative/setup-kind.sh
```

> To skip the local harness build and use the published image, pull it into the kind
> cluster first under the tag the manifests reference, then pass `--skip-build`:
> ```
> docker pull ghcr.io/rossoctl/serverless-harness:latest
> docker tag ghcr.io/rossoctl/serverless-harness:latest dev.local/serverless-harness:local
> kind load docker-image dev.local/serverless-harness:local --name sh-knative
> ./deploy/knative/setup-kind.sh --skip-build
> ```
> By default `setup-kind.sh` **pulls the published harness image**
> (`ghcr.io/rossoctl/serverless-harness:latest`) and loads it into the kind cluster — a
> first-time quickstart needs no local Docker build. If the pull is unavailable (offline, or
> the image is missing) it transparently falls back to building from this checkout.
> - `--build` — force a local build (use when testing local source changes).
> - `--skip-build` — reuse an image you already loaded as `dev.local/serverless-harness:local`.
>
> See [`deploy/knative/README-kind.md`](deploy/knative/README-kind.md) for more setup options.

setup inferencing
Expand Down
Loading