Skip to main content

CI/CD

Integrate Speedscale into your CI/CD workflow to enable high-velocity deployment with confidence.

Run Speedscale after your build and unit tests, but before deploying to production.

Adding anything to your CI/CD pipeline generally involves the same 3 steps:

  1. adding a step to your pipeline
  2. setting the right environment variables
  3. executing the right script to perform a desired action

Let's walk through these steps for Speedscale.

Prerequisites

In order to integrate Speedscale into your CI/CD you will need:

Direct access to your Kubernetes cluster is optional.

info

Speedscale can also be run in Docker

The Pipeline

info

Every CI system has a different way of setting the PATH for actions, make sure ~/.speedscale is in the path after running the installer script.

The speedscale.sh script can be implemented a few ways. Choose the one that makes the most sense for your environment.

version: "2.1"
orbs:
kubernetes: circleci/kubernetes@1.3.1 # <--- if using kubectl - see below
jobs:
speedscale:
docker:
- image: "cimg/python:3.10"
environment:
SERVICE: my-service # <--- Speedscale environment variables - see below
steps:
- kubernetes/install-kubectl: # <--- if using kubectl - see below
kubectl-version: << parameters.kubectl-version >>
- run:
command: |
export PATH=${PATH}:${HOME}/.speedscale
./speedscale.sh # <--- Speedscale script - see below

See a full config.yml for more context.

The Environment

Set these environment variables to configure the replay, either in the script or through the CI/CD platform:

VariableExample ValueDescription
CLUSTERmy-clusterName of the cluster running the Speedscale Operator.
SERVICEmy-serviceService name as displayed at https://app.speedscale.com.
NAMESPACEmy-nsKubernetes namespace where your service is running.
TEST_CONFIGstandardTest configuration defined at https://app.speedscale.com/config.
SNAPSHOT_IDlatestUse the latest snapshot for this service.
TIMEOUT10mMaximum amount of time to wait for replay to complete.
BUILD_TAGv1.2.3Identifies the version of your service being tested.
Namespace inference

If NAMESPACE is omitted (or --namespace is not passed), speedctl infra replay will infer the namespace automatically when the snapshot was captured from a single namespace. If the snapshot spans multiple namespaces the variable is required.

The Script

Let's fill in the speedscale.sh script shown above. Choose the implementation that makes the most sense for your environment.

If you do not have cluster access the replay can be started and validated with speedctl.

In addition to the other environment variables, your speedscale API key should be set (securely) for authentication:

VariableExample ValueDescription
SPEEDSCALE_API_KEY2ca17dbe5b3b7a4f15f926b83d1ed567a98d38a3e47beService account API key created from https://app.speedscale.com/tenant
#!/usr/bin/env sh

if [ -z "$SPEEDSCALE_API_KEY" ];then
echo "SPEEDSCALE_API_KEY is required"
exit 1
fi

echo "installing speedctl"
sh -c "$(curl -Lfs https://downloads.speedscale.com/speedctl/install)"

echo "creating replay for service $SERVICE in cluster $CLUSTER in namespace $NAMESPACE"
REPORT_ID=$(speedctl infra replay "$SERVICE" \
--cluster "$CLUSTER" \
--namespace "$NAMESPACE" \
--test-config-id "$TEST_CONFIG" \
--snapshot-id "$SNAPSHOT_ID" \
--id-only \
--build-tag "$BUILD_TAG")

echo "waiting for replay with report ID $REPORT_ID to complete"
speedctl wait report "$REPORT_ID" \
--timeout "$TIMEOUT"

# exit script with the code from wait report command
exit $?

Use your existing test driver

A replay can start Speedscale service mocks without starting the Speedscale traffic generator. This lets an existing k6, Postman, or other test suite drive the system under test while its outbound dependencies are served from recorded traffic.

The pipeline order is:

  1. Start a replay with a test config whose cluster.replayMode is responder-only.
  2. Wait for mocks-ready.
  3. Run the test driver against the service under test.
  4. Cancel the replay, including when the driver fails or the job is interrupted.

Use Speedscale v2.5.789 or newer in the cluster and speedctl v2.5.833 or newer in CI. Older operators do not publish the MocksReady condition consumed by the CLI. Older clients cannot scope the wait to the target cluster and namespace. Configure the responder with passthrough_mode: false when the test must fail instead of reaching a live dependency. The external driver demo includes a complete test config and a reusable wrapper script.

#!/usr/bin/env bash

set -euo pipefail

REPORT_ID=""
TIMEOUT=${TIMEOUT:-10m}
cleanup() {
status=$?
trap - EXIT
if [[ -n $REPORT_ID ]]; then
speedctl infra cancel-replay "$REPORT_ID" || true
fi
exit "$status"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

REPORT_ID=$(speedctl infra replay \
--snapshot-id "$SNAPSHOT_ID" \
--cluster "$CLUSTER" \
--namespace "$NAMESPACE" \
--service "$SERVICE" \
--test-config-id external-driver \
--id-only)

speedctl wait replay "$REPORT_ID" \
--for mocks-ready \
--cluster "$CLUSTER" \
--namespace "$NAMESPACE" \
--timeout "$TIMEOUT" \
--poll-interval 5s

"$@"

The driver must be able to reach the service under test. It does not connect directly to the Speedscale responder. Run the driver inside the cluster when the service has no route from the CI runner. If you use kubectl port-forward, start it from the driver command after mocks-ready; the replay can replace the application pod and disconnect a port-forward started earlier. The demo includes a port-forward helper for local runs.

Pass the service URL as a k6 environment variable:

./speedscale.sh \
k6 run --env BASE_URL="$SUT_URL" test.js

Define thresholds for checks and failed HTTP requests. Checks by themselves do not make k6 return a failure code.

export const options = {
thresholds: {
checks: ['rate==1'],
http_req_failed: ['rate==0'],
},
};

The k6 demo script runs the full lifecycle against a service with a mocked outbound dependency.

The driver exit code is the primary CI result. If the responder test config enables with_report, you can also wait for the Speedscale report after canceling the replay and inspect mock hits and misses.

Customising mock behaviour in CI

The speedctl infra replay command supports several flags to control which outbound dependencies are mocked during a pipeline run. These can be passed directly in your speedscale.sh script or added as additional environment variables.

Disable all mocks

Pass --no-mocks to send real outbound traffic instead of using recorded responses:

REPORT_ID=$(speedctl infra replay "$SERVICE" \
--cluster "$CLUSTER" \
--namespace "$NAMESPACE" \
--test-config-id "$TEST_CONFIG" \
--snapshot-id "$SNAPSHOT_ID" \
--id-only \
--build-tag "$BUILD_TAG" \
--no-mocks)

Mock only specific dependencies

Use --mock-only (repeatable) to mock a subset of outbound services and call everything else live. Selectors accept svc:<substring>, host:<hostname>, glob:<pattern>, or an exact service key.

REPORT_ID=$(speedctl infra replay "$SERVICE" \
--cluster "$CLUSTER" \
--namespace "$NAMESPACE" \
--test-config-id "$TEST_CONFIG" \
--snapshot-id "$SNAPSHOT_ID" \
--id-only \
--build-tag "$BUILD_TAG" \
--mock-only 'svc:payments' \
--mock-only 'host:api.stripe.com')

Exclude specific dependencies from mocking

Use --mock-except to mock everything except the listed services:

REPORT_ID=$(speedctl infra replay "$SERVICE" \
--cluster "$CLUSTER" \
--namespace "$NAMESPACE" \
--test-config-id "$TEST_CONFIG" \
--snapshot-id "$SNAPSHOT_ID" \
--id-only \
--build-tag "$BUILD_TAG" \
--mock-except 'svc:db')

Exclude inbound services from replay

Use --exclude-in (repeatable) to skip specific inbound services so they are not replayed:

REPORT_ID=$(speedctl infra replay "$SERVICE" \
--cluster "$CLUSTER" \
--namespace "$NAMESPACE" \
--test-config-id "$TEST_CONFIG" \
--snapshot-id "$SNAPSHOT_ID" \
--id-only \
--build-tag "$BUILD_TAG" \
--exclude-in 'svc:frontend')

Preview the mock plan without running a replay

During pipeline development you can validate your selector configuration with --dry-run-mocks. This prints the resolved mock plan and exits without triggering a replay:

speedctl infra replay "$SERVICE" \
--cluster "$CLUSTER" \
--snapshot-id "$SNAPSHOT_ID" \
--mock-only 'svc:payments' \
--dry-run-mocks -o pretty

See the full replay reference for a complete list of flags and selector syntax.

Need Help?

Let us know on the community Slack if instructions for your deploy system are not included here.