Skip to content
sacloudPublic

About

Mock suite for Sakura Cloud APIs

Resources

Contributing

Security policy

Stars

12 stars

Watchers

0 watching

Forks

Latest commit

 

History

650 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sakumock

Status: Alpha (under active development). APIs, behavior, and module layout may change without notice. Not recommended for production use.

A local mock server suite for SAKURA Cloud APIs, inspired by LocalStack. Run SAKURA Cloud services locally for development and testing without connecting to the real API.

Services

Every service is available as a subcommand of the single sakumock binary (e.g. sakumock simplemq), and as a Go library for spinning up in-process test servers. See each service's README for details.

Under sakumock all every control plane shares one listener (127.0.0.1:18000 by default) and is served under its Path (e.g. http://127.0.0.1:18000/kms). A service run on its own (sakumock kms) or under sakumock all --per-service-ports listens on its Standalone Port instead, at the root path.

Service Path Standalone Port Module Description
simplemq /simplemq 18080 github.com/sacloud/sakumock/simplemq SimpleMQ message API
secretmanager /secretmanager 18082 github.com/sacloud/sakumock/secretmanager SecretManager API
kms /kms 18081 github.com/sacloud/sakumock/kms KMS key management API
simplenotification /simplenotification 18083 github.com/sacloud/sakumock/simplenotification Simple Notification message-send API
monitoringsuite /monitoringsuite 18084 github.com/sacloud/sakumock/monitoringsuite Monitoring Suite control-plane API
eventbus /eventbus 18085 github.com/sacloud/sakumock/eventbus EventBus control-plane API
objectstorage /objectstorage 18086 github.com/sacloud/sakumock/objectstorage Object Storage control-plane API (optional S3 data plane via versitygw)
iam /iam 18087 github.com/sacloud/sakumock/iam IAM control-plane API
apprun /apprun 18088 github.com/sacloud/sakumock/apprun AppRun control-plane API (optional Docker data plane)
apprundedicated /apprun-dedicated 18089 github.com/sacloud/sakumock/apprundedicated AppRun Dedicated control-plane API (optional Docker data plane)
workflows /workflows 18090 github.com/sacloud/sakumock/workflows Workflows control-plane API (optional Runbook execution engine)
apigw /apigw 18091 github.com/sacloud/sakumock/apigw API Gateway control-plane API (optional gateway data plane)
cloudhsm /cloudhsm 18092 github.com/sacloud/sakumock/cloudhsm CloudHSM control-plane API
seg /seg 18093 github.com/sacloud/sakumock/seg Service Endpoint Gateway control-plane API
addon /addon 18094 github.com/sacloud/sakumock/addon Add-on API (AI / CDN / security / data analytics resources)

Quick Start

Install

Install with mise, which fetches the prebuilt binary straight from the GitHub Releases:

# Install globally (latest release)
mise use -g github:sacloud/sakumock@latest

Or pin a version in your project's mise.toml:

[tools]
"github:sacloud/sakumock" = "0.2.1"

Alternatively, download a prebuilt binary from the Releases page, or use the container image (see Docker):

docker pull ghcr.io/sacloud/sakumock:latest

Run

Run every service together in one process. This is the usual way to use sakumock:

sakumock all

Every service's control plane listens on 127.0.0.1:18000, each under /<service> — the paths of the real APIs overlap across services, so the prefix is what tells them apart (e.g. http://127.0.0.1:18000/kms, http://127.0.0.1:18000/simplemq). Change the address with --addr. Data planes, when enabled, keep their own listeners (see each service's README).

Run sakumock all --help for flags. Per-service flags are available with a service prefix (e.g. --kms-latency, --simplemq-rate-limit).

Connect Your Application

The sakumock env subcommand prints the environment variables your client (SAKURA Cloud SDK or the Terraform provider) needs as a dotenv file, so you never hand-copy endpoints. It starts no server, so you can run it before (or independently of) sakumock all:

sakumock env > ./sakumock.env

# In the shell that runs your SDK / Terraform:
set -a; source ./sakumock.env; set +a
terraform apply

The file sets each service's SAKURA_ENDPOINTS_* override plus dummy credentials. The dummy credentials also act as a safety net: a request to an API that sakumock does not mock reaches the real endpoint but fails authentication instead of touching your account.

Pass --export to prefix every line with export , so the output can be sourced directly (e.g. with direnv or a plain shell) without set -a:

sakumock env --export > .envrc   # or: source <(sakumock env --export)

By default the endpoints point at sakumock all's listen address (--addr, with each service's path). Pass sakumock env the same flags you pass sakumock all (--addr, --per-service-ports, --config, ...) and the endpoints match. When the client reaches sakumock over the network — most importantly from a container — pass --host to substitute the host the client actually uses (the port is kept):

sakumock env --host localhost > sakumock.env

Run sakumock env to see the full list of variables, or sakumock all --help for each flag's environment variable.

Configuration

sakumock all accepts per-service flags, a config file, and environment variables.

Flags

Per-service flags keep their defaults and are available with a service prefix (e.g. --kms-latency, --simplemq-rate-limit). Suite-wide flags such as --addr have no prefix. Run sakumock all --help for a full listing.

Config File

Instead of passing many flags, sakumock all can read a config file (--config, YAML or JSON by extension) with options grouped per service:

# sakumock.yaml
addr: 127.0.0.1:19000
simplemq:
  database: /var/lib/sakumock/mq.db
  message-expire: 96h
kms:
  latency: 5s
sakumock all --config sakumock.yaml

Each per-service flag maps to a key by stripping the service prefix: --simplemq-message-expire becomes message-expire under simplemq:, --kms-latency becomes latency under kms:. Suite-wide flags are top-level keys (addr, per-service-ports, listen-host, ...). Precedence, highest first: command-line flag, then config file, then environment variable, then the flag's default.

Environment Variables

Every per-service setting also has an environment variable, which is the most convenient way to configure the mock in a container. The names are <SERVICE>_<SETTING> (e.g. KMS_LATENCY, SIMPLEMQ_RATE_LIMIT, MONITORINGSUITE_DEBUG); each flag's exact variable is shown in sakumock all --help as ($VAR). They apply under sakumock all just as they do for the standalone subcommands:

KMS_LATENCY=200ms SIMPLEMQ_RATE_LIMIT=10 sakumock all

Per-Service Ports

Pass --per-service-ports (or SAKUMOCK_PER_SERVICE_PORTS=true) to serve each control plane on its own port at the root path instead — the Standalone Port in the service table, changed with the prefixed --<service>-addr flag (e.g. --kms-addr). Pass the flag to sakumock env as well so the endpoints match:

sakumock all --per-service-ports
sakumock env --per-service-ports > sakumock.env

Run a Single Service

You can also run any service on its own as a subcommand (same flags, without the service prefix). Each listens on its Standalone Port at the root path, e.g. SAKURA_ENDPOINTS_KMS=http://127.0.0.1:18081:

sakumock simplemq &
sakumock secretmanager &
sakumock kms &
sakumock simplenotification &
sakumock monitoringsuite &
sakumock eventbus &
sakumock objectstorage &
sakumock iam &
sakumock apprun &
sakumock apprun-dedicated &
sakumock workflows &
sakumock cloudhsm &
sakumock seg &
sakumock addon &

Run sakumock --help to list services, and sakumock <service> --help for its flags.

Embedded Documentation

The binary carries this README, the CHANGELOG, the compose example, the Terraform configuration of the end-to-end test, and every service README, so the documentation can be read without a source checkout — including by an LLM agent driving the CLI. sakumock docs prints an index in the llms.txt convention; topics are named after the service subcommands.

sakumock docs                              # index of topics
sakumock docs kms                          # a service README (same as `sakumock kms --docs`)
sakumock docs kms --toc                    # headings only
sakumock docs kms --section "Fixed keys"   # one section, by heading text or slug (fixed-keys)
sakumock docs readme --section fault-injection
sakumock docs terraform --section apigw       # known-good sacloud/sakura provider resources for one service
sakumock docs --search fault               # grep every topic: topic:line:section: text
sakumock docs eventbus --search simplemq   # grep one topic; the section slug feeds --section
sakumock docs --all > sakumock-docs.md     # everything concatenated (llms-full.txt style)

Each service also accepts --docs (sakumock kms --docs, sakumock-kms --docs) to print its own README, next to --routes for its endpoint table.

Latency

To test client timeouts, every service can delay its responses with --latency DURATION (e.g. 500ms, 2s). It applies to the control-plane API (and to same-port data planes such as simplemq messages); the mock-only /_sakumock/ inspection endpoints are never delayed.

Services with a separate-listener in-process data plane — Monitoring Suite ingest, AppRun / AppRun Dedicated proxies, API Gateway — delay it independently with --data-plane-latency DURATION, so a slow ingest or proxy path can be tested without slowing the control plane (and vice versa). The object storage S3 data plane is served by versitygw, an external process, and has no --data-plane-latency.

# Under sakumock all, use the service prefix
sakumock all --monitoringsuite-latency 200ms --monitoringsuite-data-plane-latency 2s

Fault Injection

To test how a client (SDK, Terraform provider, your application) handles server errors and network failures, every service can probabilistically inject faults into its control-plane API with --fault CODE:RATE[:PHASE] (repeatable):

  • CODE — the HTTP status to return (200–599), or reset to drop the TCP connection abruptly (the client sees a transport error such as connection reset by peer, not an HTTP response).
  • RATE — the probability in (0, 1]. Each request rolls once; the configured rates are exact and mutually exclusive, so they must sum to at most 1.
  • PHASE — when the fault fires, relative to the mock's own processing:
    • before (default): the request is rejected before the handler runs — no state changes, safe to retry.
    • after: the handler runs first (state changes do happen), then its response is discarded and replaced by the fault — emulates "the server committed but the client got an error/disconnect", which is what exercises retry idempotency in a client.
# Standalone: 10% HTTP 500, 2% connection reset
sakumock kms --fault 500:0.1 --fault reset:0.02

# Under sakumock all, use the service prefix
sakumock all --kms-fault 500:0.1 --kms-fault 500:0.1:after

# Env var (comma-separated)
KMS_FAULT=500:0.1,reset:0.02 sakumock all

Config file (quote the specs — unquoted 500:0.1 in YAML flow style would parse as a mapping):

kms:
  fault:
    - "500:0.1"
    - "reset:0.02:after"

An injected status is returned in the service's own error envelope with a fault injection message, and the request log records it (reset logs as status 499). When tracing is enabled, the request's span is annotated with sakumock.fault.code and sakumock.fault.phase (plus sakumock.fault.replaced_status for after faults), and a reset marks the span status as error — so an injected fault is distinguishable from a real one in traces. Faults fire before authentication, rate limiting, and validation — like an infrastructure failure, an injected fault can mask a would-be 401/429/400. They never apply to the mock-only /_sakumock/ inspection endpoints, and in this iteration they cover control planes only (the separate-listener data planes — object storage S3, Monitoring Suite ingest, AppRun proxies, API Gateway — are not fault-injected).

TLS

Pass a certificate and key (--tls-cert/--tls-key, or SAKUMOCK_TLS_CERT/SAKUMOCK_TLS_KEY) to serve every listener over HTTPS — all control planes and data planes share the one cert (they run on the same host, only the port differs). TLS is enabled only when both files are set; otherwise everything stays plain HTTP. The object storage data plane is served by versitygw, which is handed the same cert/key (--cert/--key) so it terminates TLS itself. Standalone subcommands accept the same --tls-cert/--tls-key (env <SERVICE>_TLS_CERT / <SERVICE>_TLS_KEY). sakumock env emits https:// endpoints when TLS is set; with a self-signed cert the client must be told to trust it.

Service Link

Pass --enable-service-link (or SAKUMOCK_ENABLE_SERVICE_LINK=true) to let services forward requests to each other, just as the real SAKURA Cloud platform does. Currently EventBus forwards fired jobs to their destination service:

Source Destination What happens
EventBus SimpleMQ Sends a message to the configured queue
EventBus SimpleNotification Sends a notification to the configured group

Without --enable-service-link (the default), firings are recorded but not forwarded — each service works in isolation.

Service link requires the source service's data plane to be enabled as well — EventBus needs --eventbus-enable-data-plane to fire events:

sakumock all --enable-service-link --eventbus-enable-data-plane

Service link is only available with sakumock all; standalone services cannot discover each other's addresses.

OpenTelemetry Tracing

sakumock can emit OpenTelemetry traces for every request it handles — one server span per request, continuing the client's trace when the request carries a W3C traceparent header. Configuration uses the standard OTEL environment variables only; there are no sakumock flags:

# Export spans to any OTLP/HTTP endpoint (a collector, Jaeger, ...):
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 sakumock all

Tracing is enabled when OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set (and OTEL_SDK_DISABLED is not true); otherwise it is completely off. Spans are exported over OTLP http/protobuf by default; set OTEL_EXPORTER_OTLP_PROTOCOL=grpc (or OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc) to export over gRPC instead (e.g. OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317).

  • Spans are named after the matched route (e.g. GET /cloud/zone/is1a/api/kms/1.0/keys/{id}) and carry http.route plus a sakumock.service attribute identifying the service; the resource is service.name=sakumock (override with OTEL_SERVICE_NAME).
  • Every request log line gains trace_id and span_id attributes, so logs and traces correlate.
  • All listeners are instrumented: every control plane and the in-process data planes (monitoring suite ingest, AppRun proxies, API gateway). The object storage S3 data plane (versitygw, an external process) is not.
  • The SDK's other standard env vars work as usual: OTEL_RESOURCE_ATTRIBUTES, OTEL_TRACES_SAMPLER, OTEL_EXPORTER_OTLP_HEADERS, OTEL_BSP_*, ...

sakumock can even export to its own Monitoring Suite mock ingest:

MONITORINGSUITE_ENABLE_DATA_PLANE=true \
MONITORINGSUITE_DATA_PLANE_DUMP_DIR=/tmp/telemetry \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:28084 sakumock all

Note that in this loopback setup each export POST is itself traced, so an idle sakumock emits one heartbeat span per batch interval — harmless, but visible in the dumps.

Inspection

Every service exposes mock-only /_sakumock/ endpoints for observing or driving the mock — listing accepted messages, inspecting fired deliveries, injecting events, and resetting state. These endpoints do not exist in the real SAKURA Cloud API.

See each service's README for available endpoints and response shapes:

Inspection API (Go)

Each service with inspection endpoints provides an InspectionClient — an HTTP client that wraps the /_sakumock/ calls and returns typed domain objects. Use it from integration tests that run against a sakumock process or container (not just in-process test servers):

import "github.com/sacloud/sakumock/eventbus"

ic := eventbus.NewInspectionClient("http://localhost:18000/eventbus")
ds, _ := ic.InjectEvent(ctx, eventbus.Event{Source: "//monitoringsuite..."})
ds, _ = ic.Deliveries(ctx)
_ = ic.ClearDeliveries(ctx)

Inspection CLI

The sakumock inspect subcommand calls inspection endpoints from the command line:

# List recorded firings
sakumock inspect eventbus deliveries

# Inject an event and see which triggers fired
sakumock inspect eventbus inject-event --source "//monitoringsuite..."

# Force schedule evaluation at a specific time
sakumock inspect eventbus tick --at 2024-01-01T09:00:00+09:00

# List accepted notification messages
sakumock inspect simplenotification messages

# Clear state between test runs
sakumock inspect eventbus clear-deliveries
sakumock inspect simplenotification clear-messages

Each service subcommand accepts --addr to override the server address. The address resolves in order of precedence (highest first): --addr flag, then SAKURA_ENDPOINTS_* environment variable (so sakumock env output works as-is), then the built-in default.

Docker

A multi-platform image (linux/amd64, linux/arm64) is published to GitHub Container Registry.

Basic Usage

The default command runs every service bound to 0.0.0.0, so the published port is reachable from the host. Every control plane shares port 18000, each under /<service>:

docker run --rm \
  -p 18000:18000 \
  ghcr.io/sacloud/sakumock:latest

Data Plane Image

A second tag, :latest-dataplane (and :<version>-dataplane), enables every service's data plane by default: the Object Storage S3 data plane (bundling the versitygw S3 gateway; OBJECT_STORAGE_ENABLE_DATA_PLANE=true, listening on 0.0.0.0:28086), the Monitoring Suite telemetry ingest data plane (MONITORINGSUITE_ENABLE_DATA_PLANE=true, listening on 0.0.0.0:28084), the AppRun / AppRun Dedicated Docker data planes (APPRUN_ENABLE_DATA_PLANE=true on 0.0.0.0:28088, APPRUN_DEDICATED_ENABLE_DATA_PLANE=true on 0.0.0.0:28089), and the API Gateway data plane (APIGW_ENABLE_DATA_PLANE=true on 0.0.0.0:28091). The default image includes none of these, so use this tag when you need a data plane:

docker run --rm \
  -p 18000:18000 \
  -p 28084:28084 -p 28086:28086 -p 28091:28091 \
  ghcr.io/sacloud/sakumock:latest-dataplane

Objects are stored under /home/nonroot/data; mount a volume there to persist them. The image runs as a non-root user (uid 65532), so prefer a named volume (-v sakumock-data:/home/nonroot/data, which inherits the right ownership) — a bind-mounted host directory must be writable by uid 65532 or the data plane fails to start.

AppRun Data Planes

The AppRun and AppRun Dedicated data planes start Docker containers for each deployed application and reverse-proxy traffic to them. This requires the host Docker socket and --network host:

docker run --rm --network host \
  -v /var/run/docker.sock:/var/run/docker.sock \
  ghcr.io/sacloud/sakumock:latest-dataplane
  • -v /var/run/docker.sock:/var/run/docker.sock — lets the mock start and stop sibling containers on the host Docker daemon.
  • --network host — the reverse proxy connects to containers via 127.0.0.1:<port>, so the mock must share the host's network namespace to reach the published ports. With --network host the -p flags are unnecessary (all ports are directly accessible).
  • The host Docker socket must be readable by the container's user. Add the host's docker group GID with --group-add $(stat -c '%g' /var/run/docker.sock) (or chmod 666 /var/run/docker.sock on the host as a less secure alternative).

If you do not need the AppRun data planes, disable them with environment variables to skip the Docker dependency:

docker run --rm \
  -e APPRUN_ENABLE_DATA_PLANE=false \
  -e APPRUN_DEDICATED_ENABLE_DATA_PLANE=false \
  -p 18000:18000 \
  -p 28084:28084 -p 28086:28086 -p 28091:28091 \
  ghcr.io/sacloud/sakumock:latest-dataplane

Configuration and Client Env

Configure the mock's behavior with the per-service environment variables (see Environment Variables) — handier than flags in a container:

docker run --rm -p 18000:18000 \
  -e KMS_LATENCY=200ms -e KMS_RATE_LIMIT=10 \
  ghcr.io/sacloud/sakumock:latest

Writing an env file inside the container is not useful: a file there is invisible to a client on the host, and the in-container listen host is not how the client reaches the service. Instead, generate the client env with the env subcommand, telling it the host the client uses (the host shell does the redirection):

docker run --rm ghcr.io/sacloud/sakumock:latest env --host localhost > sakumock.env
set -a; source ./sakumock.env; set +a
terraform apply

For docker compose, run env as a oneshot that writes the file into a shared volume (the image has no shell, so use --output rather than >) and have your app load it. See examples/compose.yaml for a complete example.

Use as a Library

Each service can also be used as a Go library to spin up in-process test servers:

import "github.com/sacloud/sakumock/secretmanager"

srv := secretmanager.NewTestServer(secretmanager.Config{})
defer srv.Close()
// srv.TestURL() returns http://127.0.0.1:<random-port>

Contributing

See AGENTS.md for module conventions, file structure, public API contracts, port allocation, and the architectural guidelines each service must follow.

License

This project is published under Apache 2.0 License.

About

Mock suite for Sakura Cloud APIs

Resources

Contributing

Security policy

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages