Cloud Scheduler¶
Cloudbox emulates the Cloud Scheduler REST API (v1). The google-cloud-scheduler Python
SDK works against it without modification.
Connection¶
Port: 8091 (override with CLOUDBOX_SCHEDULER_PORT)
from google.api_core.client_options import ClientOptions
from google.auth.credentials import AnonymousCredentials
from google.cloud import scheduler_v1
client = scheduler_v1.CloudSchedulerClient(
credentials=AnonymousCredentials(),
client_options=ClientOptions(api_endpoint="http://localhost:8091"),
)
Jobs¶
Create job¶
{
"name": "projects/local-project/locations/us-central1/jobs/my-job",
"description": "Runs every 5 minutes",
"schedule": "*/5 * * * *",
"timeZone": "UTC",
"httpTarget": {
"uri": "http://localhost:8080/cron/task",
"httpMethod": "POST",
"headers": { "Content-Type": "application/json" },
"body": "e30="
},
"retryConfig": {
"retryCount": 3,
"maxRetryDuration": "3600s",
"minBackoffDuration": "5s",
"maxBackoffDuration": "3600s",
"maxDoublings": 5
}
}
name is required. schedule is a standard Unix cron expression (5 or 6 fields, via
croniter). httpTarget.body must be base64-encoded. Returns the job resource with
state: ENABLED and a computed scheduleTime.
409 if the job already exists.
Get job¶
Returns the job resource. 404 if not found.
List jobs¶
Returns { "jobs": [...], "nextPageToken": "..." }.
Update job¶
Updates mutable fields: description, schedule, timeZone, httpTarget,
retryConfig. Updating schedule recomputes scheduleTime. Returns the updated resource.
Delete job¶
204 on success. 404 if not found.
Job actions¶
Force-run¶
Dispatches the job immediately, regardless of its cron schedule. Makes an HTTP POST to
httpTarget.uri synchronously within the request. Updates lastAttemptTime and status.
Returns the updated job resource. If the HTTP call fails, status.code is set to 2 and
status.message contains the error.
Pause¶
Sets state: PAUSED — the background worker stops dispatching this job. Returns the
updated job resource.
Resume¶
Sets state: ENABLED — the background worker resumes dispatching. Returns the updated
job resource.
Background scheduler¶
Cloudbox runs a background poll loop that dispatches due jobs:
- Poll interval: 30 seconds.
- Due check: a job is due if
cronitercomputes its next fire time (afterlastAttemptTime) as ≤ now. Jobs that have never run are always due on the first poll. - Dispatch: an HTTP POST is sent to
httpTarget.uriwith the configured headers and body. A2xxresponse marks success; any other status or a network error is recorded instatus.
Only jobs with state: ENABLED and a non-empty httpTarget are dispatched.
Job resource fields¶
| Field | Type | Description |
|---|---|---|
name |
string | Full resource name |
description |
string | Human-readable description |
schedule |
string | Cron expression ("*/5 * * * *", "0 9 * * 1-5", etc.) |
timeZone |
string | IANA timezone name (default: "UTC") |
state |
string | ENABLED, PAUSED, or DISABLED |
httpTarget |
object | HTTP dispatch configuration |
retryConfig |
object | Retry policy for failed dispatches |
scheduleTime |
string | RFC 3339 time of the next scheduled execution |
lastAttemptTime |
string | RFC 3339 time of the most recent dispatch attempt |
userUpdateTime |
string | RFC 3339 time of the last user update |
status |
object | Status of the last dispatch attempt |
HTTP target fields¶
| Field | Type | Description |
|---|---|---|
uri |
string | Target URL |
httpMethod |
string | GET, POST, PUT, DELETE, etc. (default: POST) |
headers |
object | HTTP headers |
body |
string | Base64-encoded request body |
oidcToken |
object | OIDC token config (stored, not enforced) |
oauthToken |
object | OAuth token config (stored, not enforced) |
Cron expression reference¶
| Expression | Meaning |
|---|---|
* * * * * |
Every minute |
*/5 * * * * |
Every 5 minutes |
0 * * * * |
Every hour |
0 9 * * 1-5 |
Weekdays at 09:00 |
0 0 1 * * |
First day of every month |
0 0 * * 0 |
Every Sunday at midnight |
Testing scheduler jobs¶
In tests, patch the internal _dispatch function to avoid real HTTP calls:
from unittest.mock import AsyncMock, patch
with patch("cloudbox.services.scheduler.worker._dispatch", new_callable=AsyncMock):
response = client.post(".../my-job:run")
This is necessary because httpx is imported inside the route handler, so patching
httpx.AsyncClient directly does not intercept the call.
Known limitations¶
| Feature | Notes |
|---|---|
| Pub/Sub target | Only httpTarget is supported; pubsubTarget is not dispatched |
| App Engine target | appEngineHttpTarget is stored but not dispatched |
| OIDC / OAuth token injection | Auth tokens accepted and stored but not attached to dispatch requests |
| Sub-minute scheduling | The 30-second poll interval limits effective resolution to ~1 minute |