Commit Graph
70 Commits
Author SHA1 Message Date
Tim Hockin ef2afd1262 Cleanup: remove redundant LOC in boilerplate python 2026-05-31 19:45:36 -07:00
Tim Hockin c6209d5ad4 Fix boilerplate in other shell-style files 2026-05-31 19:45:36 -07:00
Tim Hockin d2416e25e7 Fix boilerplate in proto files 2026-05-31 19:45:36 -07:00
Tim Hockin 1ac5ba32a1 Fix boilerplate in go files 2026-05-31 19:45:36 -07:00
Tim Hockin 4b537a02bb Fix boilerplate in python files 2026-05-31 19:45:36 -07:00
Tim Hockin 5d266a026e Fix boilerplate in shell files 2026-05-31 19:45:36 -07:00
Tim Hockin 20c845260c Fix boilerplate in YAML files 2026-05-31 19:45:36 -07:00
Tim Hockin 6724b1bd95 Fix boilerplate to single-space
Google guidelines do not indicate double space, our code is already
mixed-use, and it's weird.  Fixing files will follow.
2026-05-31 19:45:36 -07:00
Tim Hockin faeee7d04f Remove redundant boilerplate file, rename 2026-05-31 19:45:36 -07:00
Tim Hockin b80ef62263 Cleanup: use long-form names for shopts 2026-05-31 19:45:36 -07:00
Tim Hockin bfdba4c402 Cleanup: always quote ROOT 2026-05-31 19:45:36 -07:00
Tim Hockin 6fcded0243 Cleanup: set -o on one line 2026-05-31 19:45:36 -07:00
Tim Hockin 872da12864 No need for echo -n in run-tool.sh 2026-05-31 19:45:36 -07:00
Taahir Ahmed fae81c62f0 Adopt the Kubernetes AI contribution policy 2026-05-28 21:24:19 -07:00
Benjamin Elder f2ce91022f dedupe kind and go version from CI config, upgrade go to latest (#116)
In github actions:
- We manage kind with a script, no need to separately install at a
possibly incorrect version
- Use go.mod to specify version, instead of repeating the version in
actions config

In the code:
- Upgrade go.mod to use latest go release (which should be reflected in
CI)


- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-05-28 21:24:14 -04:00
Benjamin Elder 4a536f081f document new slack channels (#108)
Fixes https://github.com/agent-substrate/substrate/issues/107

> It's a good idea to open an issue first for discussion.

- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-05-28 17:47:10 -07:00
Julian Gutierrez Oschmann 04e16bfa23 Remove "app" label from the worker pool deployment. (#111)
It's not really needed, we are already tagging pods with the
"ate.dev/worker-pool" label so we can use it to select the pods.
2026-05-28 15:47:37 -07:00
Alex BulankouandAlex Bulankou 66bf0df1eb internal/dns/readme: refresh stale regex example and terminology (#113)
The example CoreDNS regex in `internal/dns/readme.md` was last updated
before two renames: the actor DNS suffix changed from `substrate.k8s.io`
to `substrate.ate.dev`, and the actor id format changed from UUID to a
generic DNS label. The authoritative source
(`internal/resources/actor.go` — `ActorIDRegexPattern` +
`ActorDNSSuffix`) and the corefile test
(`internal/dns/corefile_test.go`) both reflect the current shape; only
the readme drifted.

Pure documentation change; no code or test changes.

Reopening manually under my credentials, with the commit authored by me.
The original PR was drafted by an a4 fleet bot and tracked as #99
(closed because the bot account is not CLA-signed). It was reviewed and
**approved by @bowei** on commit f92de4d before being closed — linking
that PR for review continuity:
https://github.com/agent-substrate/substrate/pull/99

(First re-open attempt landed as #112 with a bot author because the
local CLI shim swapped my OAuth token for the bot's installation token
on the API call — closed and re-opened here under the correct identity.
Same branch, same single commit `1df9895a` authored as me.)

Co-authored-by: Alex Bulankou <11968377+AlexBulankou@users.noreply.github.com>
2026-05-28 14:58:52 -07:00
Taahir Ahmed 9291df0b6d ActorTemplate: Enforce that all images must be pinned 2026-05-28 13:25:10 -07:00
Davanum Srinivas fe854f26fa chore(benchmarking): remove deprecated sessionDiscovery from workload manifests (#102)
`sessionDiscovery` was removed from the `ActorTemplate` CRD when
Substrate adopted the Uniform DNS Mesh (`docs/api-guide.md` §Workload
Connectivity). Four references in
`benchmarking/workloads/manifests/{workloads,full_workloads}.yaml.tmpl`
were missed in that cleanup — strict Kubernetes API servers reject the
rendered manifests via `benchmarking/workloads/deploy.sh`.

Pure deletion; no functional change to the rendered ActorTemplates.
2026-05-28 12:11:18 -04:00
Adrien Delorme 27f239dcf5 fix(ateompath): validate ateom socket path length (#92)
## Symptom

When `<namespace>:<pod-name>` exceeds 107 bytes, `ateom-gvisor` exits
with `bind: invalid argument` and the controller surfaces a generic
`DeadlineExceeded`.

## Cause

Linux limits unix-socket paths to 107 bytes (`sockaddr_un.sun_path`). A
pod's ReplicaSet hash can jiggle between 8 and 10 chars across rollouts,
so a demo that fits at 8 silently breaks at 10 with no clear error.

## Fix

Add `ValidateAteomSocketPath` (with unit tests) and call it from
`ateom-gvisor` before the bind, so the operator sees the real cause in
the pod log instead of a kernel EINVAL.
2026-05-27 18:58:47 -07:00
Arnaud M. 2dc9e6f023 lint: enable misspell linter (#94)
> It's a good idea to open an issue first for discussion.

- [ ] Tests pass
- [ ] Appropriate changes to documentation are included in the PR

Signed-off-by: Arnaud Meukam <ameukam@gmail.com>
2026-05-27 13:17:48 -07:00
Davanum SrinivasandJulian Gutierrez Oschmann 1a852c80e0 ateapi/syncer: release actor when host pod is deleted (#75)
Today, if a worker pod that hosts an active actor is forcibly destroyed,
substrate does not migrate the actor. The actor's AteomPodName still
points at the dead pod and the router times out forwarding to a dead IP.

WorkerPoolSyncer's pod-informer DeleteFunc and soft-delete branch now
check whether the worker being removed is bound to an actor, and if so,
reset that actor to `STATUS_SUSPENDED` before the worker row goes away.

The new helper releaseActorOnDeadWorker reads the actor, only acts if
the actor still claims this worker (a concurrent SuspendActor or
DeleteActor that already advanced state is respected), clears the
pod-binding fields and InProgressSnapshot, preserves LastSnapshot, and
writes via version-checked UpdateActor. On ErrPersistenceRetry we drop
the attempt and let the next informer event retry — no separate lock, no
in-handler retry budget.

`STATUS_SUSPENDED` is reused rather than introducing a new state. The
post-orphan invariant is identical and reusing keeps the proto, printer,
dialer, and router contracts unchanged. The next request through atenet
triggers an implicit resume; findFreeWorker picks any free worker in the
pool; LastSnapshot is restored.

Test plan: `TestSyncer_DeleteBoundWorker_ClearsActor` covers RUNNING
actor → pod delete → SUSPENDED with cleared bind fields,
InProgressSnapshot dropped, LastSnapshot preserved. All existing tests
in `cmd/ateapi/internal/controlapi/` still pass.

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
Co-authored-by: Julian Gutierrez Oschmann <juliangut@google.com>
2026-05-27 16:17:33 -04:00
Adrien Delorme 22600fee00 fix(hack): only pass --context to kubectl-delegating ko subcommands (#93)
## Symptom

With `KUBECTL_CONTEXT` set in `.ate-dev-env.sh`, `./hack/install-ate.sh
--deploy-ate-system` aborts mid-deploy at the first `ko resolve`:

    Error: unknown command "--context=..." for "ko resolve"

CRDs, the ate-system namespace, and podcertificate-controller have
already been applied by then, leaving the cluster half-installed.

## Cause

`run_ko` unconditionally appends `-- --context=$KUBECTL_CONTEXT` to
every ko invocation. Only `ko apply`, `ko create`, `ko delete`, `ko run`
have a kubectl phase that consumes args after `--`. Other subcommands
(`resolve`, `build`, `deps`, `login`, ...) parse it as a flag and reject
it.

## Fix

Only forward `--context` for the ko subcommands that actually delegate
to kubectl. `run_kubectl` already passes the context to direct kubectl
invocations, so the chained `ko resolve | run_kubectl apply -f -` flow
still targets the right cluster.
2026-05-27 13:17:13 -07:00
Benjamin Elder b42350f050 make sure building with ko still works by adding main_unsupported.go
ko resolves packages before resolving the build variants, so if you only have build tagged code it will error
2026-05-27 12:09:31 -07:00
Benjamin Elder 6fb16fc850 build: add linux build tags and relocate portable aetom-gvisor code to internal package 2026-05-27 12:09:31 -07:00
Haven Xia 3f4aa5d010 Refactor logging (#65)
This PR refactors `ateom-gvisor` and `kubectl-ate` to adopt a
flat-merged logging architecture, eliminating server-side and
client-side field guessing:
1. **Cloud Logging**: `ateom-gvisor` parses actor JSON, injects actor
labels/time, and outputs the flat map directly. GKE Cloud Logging
built-in rules parse native severity/message fields automatically.
2. **CLI Logging**: Removed the `--raw` flag. `kubectl ate logs` strips
Substrate metadata and outputs clean, consistent JSON lines with the
`time` field prepended first.
<img width="3235" height="306" alt="image"
src="https://github.com/user-attachments/assets/4a6ca291-db8d-45dc-b840-43efa74739f0"
/>

3. **Documentation**: Updated `docs/observability.md`

- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-05-27 10:50:10 -07:00
Davanum Srinivas a436ea216a refactor: trim cmd/* boilerplate, extract internal/serverboot (#88)
Three related cleanups in cmd/{ateapi,atelet,ateom-gvisor}/main.go:

1. atelet Run/Checkpoint/Restore handlers shared three blocks of
cut-and-paste (fetchRunsc + MkdirAll, errgroup over pause + application
containers calling prepareOCIDirectory, dial-ateom + NewAteomClient, the
ateletpb -> ateompb WorkloadSpec projection). Factored into
fetchRunscAndPrep, prepareOCIBundles, dialAteom, buildAteomWorkloadSpec,
plus a uploadIfExists for the optional pages.img / pages_meta.img upload
in Checkpoint.

2. ateapi main() was a 242-line wall of inlined startup steps;
decomposed into named helpers (loadFlagsFromEnv, logFlagValues,
connectRedis with buildRedisTLSConfig + pingRedisWithRetries,
newKubeClients, buildServerCreds) so the top-level flow reads as
orchestration.

3. ateapi, atelet, and ateom-gvisor each carried their own ~30-line
initTracing + ~35-line initMetrics + slog/metrics-server boilerplate
that differed only in ServiceName, sampler choice, and whether the OTLP
exporter was wired. Lifted to a new internal/serverboot package exposing
InitLogger, InitTracing (with TracingOptions.NoExporter for ateom-gvisor
which loses egress once eth0 moves into the gvisor netns), InitMetrics,
Fatal, ShutdownProvider, and StartMetricsServer.

No behaviour change. Verified with go vet, GOOS=linux go build, and go
test ./cmd/ateapi/internal/...

Fixes #<issue_number_goes_here>

> It's a good idea to open an issue first for discussion.

- [ ] Tests pass
- [ ] Appropriate changes to documentation are included in the PR

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2026-05-27 10:30:38 -04:00
Julian Gutierrez Oschmann 9bd41df2a3 Document code layout. (#87)
Document the rationale behind each directory and guidelines on how to
decide where to put new code in a dedicated markdown file.

Update AGENTS.md with a summary and a reference to the new markdown
file.
2026-05-26 19:42:21 -07:00
Benjamin Elder 370bb5b668 remove go vet, which is redundant to golangci-lint (#89)
save some time in CI

follow-up https://github.com/agent-substrate/substrate/pull/63

> It's a good idea to open an issue first for discussion.

- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-05-26 22:32:27 -04:00
Davanum Srinivas 94ffaf5dbd ci: also run pr-workflow on push to main (#77)
The existing trigger is pull_request only, so when a PR merges no CI
runs against the resulting main-branch commit. A bad squash, a
post-rebase test failure, or a flake-masked regression isn't surfaced
until the next PR opens, at which point bisecting "is it my change or
main?" wastes contributor time.

Add a push trigger restricted to main so the same run-tests and e2e-test
jobs run against the merged commit. No new jobs, no new permissions, no
change to PR behavior.

Fixes #<issue_number_goes_here>

> It's a good idea to open an issue first for discussion.

- [ ] Tests pass
- [ ] Appropriate changes to documentation are included in the PR

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2026-05-26 18:24:06 -07:00
Tim Hockin d5e8c99e38 Add shellcheck and fix issues (#70)
Not too many were found.  Let's keep it that way.
2026-05-26 18:09:33 -07:00
Julian Gutierrez Oschmann 736a4fffc7 Make WorkerPool implement /scale sub-resource. (#83)
Now the worker pool can be scaled via regular `kubectl scale` commands.

Use server-side-apply to apply changes.
Propagate the effective number of replicas from the status of the
managed deployment.

Fixes https://github.com/agent-substrate/substrate/issues/48
2026-05-26 16:53:30 -07:00
Davanum Srinivas de2ad8add9 fix(ateom-gvisor): make eth0 move/restore idempotent + roll back on error
ateom-gvisor moves the pod's eth0 interface into the actor's interior
netns before invoking runsc run/restore. The current code assumes eth0
is in the pod netns at the start of every RunWorkload/RestoreWorkload.
That assumption breaks if a previous call failed mid-flight: eth0 is
left stranded in the interior netns, the next call errors with
"eth0: Link not found", and the worker pod is stuck until restart.

Add ensureEth0InPodNetns that moves eth0 back to the pod netns if it
is in the interior, and is a no-op otherwise. Call it at the top of
both entry points to recover from a previously-aborted call, and via
a deferred handler after the eth0-into-interior move to roll back on
errors in the current call. The success path is unchanged.

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2026-05-26 15:59:04 -07:00
Julian Gutierrez Oschmann b17a64cc51 Remove unused "utils" package. (#86) 2026-05-26 14:06:37 -07:00
Arnaud M.andJulian Gutierrez Oschmann a4d89ed0d1 Bump default base image (#76)
Debian 11 is EOL.
Also ensure images are built for also AArch 64

- [x] Tests pass
- [ ] Appropriate changes to documentation are included in the PR

Signed-off-by: Arnaud Meukam <ameukam@gmail.com>
Co-authored-by: Julian Gutierrez Oschmann <juliangut@google.com>
2026-05-26 11:45:55 -04:00
Davanum Srinivas 1b2fd58515 fix(make): correct build-demos target path (#81)
`make build-demos` invoked `ko build ./cmd/demos/counter`, but the demo
sources live at `./demos/counter`. The target failed loudly when run,
but nothing in CI exercises it — `hack/install-ate-kind.sh
--deploy-demo-counter` (the path CI actually uses) goes through
`hack/install-demo-counter.sh` and doesn't touch the Makefile, so the
breakage went unnoticed.

Fixes #<issue_number_goes_here>

> It's a good idea to open an issue first for discussion.

- [ ] Tests pass
- [ ] Appropriate changes to documentation are included in the PR

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2026-05-26 10:20:14 -04:00
Davanum Srinivas 55923825c7 chore(hack): add golangci-lint
Adds golangci-lint v2.12.2 using the per-tool-module pattern under
hack/tools/golangci-lint/. The verifier at hack/verify/golangci-lint.sh
is auto-discovered by hack/verify-all.sh, so the existing CI verify
step picks it up with no workflow YAML change.

Ships .golangci.yaml with the standard linter set, a make lint
target, and a verify wrapper that pins GOOS=linux so ateom-gvisor's
netlink references typecheck on macOS the same way they do on CI.

Adjustments to keep the first run clean: two dead-code deletions
((*runsc).cmdList, MemoryPullCache.gcsClient), two SA9003 empty
branches replaced with slog.Error / t.Logf, one S1008 return-bool
simplification, and inline //nolint for one intentional best-effort
ReleaseLock plus four audit-tracked SA1019 deprecations.

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2026-05-23 16:19:09 -07:00
Julian Gutierrez Oschmann 80a883c589 Remove tools/reorg after migration. 2026-05-22 14:43:26 -07:00
Julian Gutierrez Oschmann b1fe163fcf Run tools/reorg tool.
This commit was automatically generated by the tools/reorg tool.

```
go run ./tools/reorg
go generate ./...
bash benchmarking/locust/generate_protos.sh
```
2026-05-22 14:43:26 -07:00
Julian Gutierrez Oschmann 8e1071b690 Add tool to reorganize files within the repo.
All rules are defined in rules.json. Then the tools/reorg tool will execute them.
2026-05-22 14:43:26 -07:00
Tim Hockin 766a0298e4 Drop ko.sh (#62)
We only use this script in a few places and it's basically a short
one-line wrapper.
2026-05-22 12:39:59 -04:00
Dmitry Berkovich c996c82577 Reoganize demo folders (#60)
Reorganize demo folders by moving manifests files inside the demo folder
from shared manifest folder
2026-05-22 11:01:19 -04:00
Benjamin Elder 379e7e677d feat: add script to update tool dependencies in hack/tools directory (#56)
We are just using tools modules (plural) to manage installing go
binaries with binned versions easily.
We are NOT trying to patch transitive dependencies in those tools,
versus just updating those tools themselves.

Add a helper script to update them as intended, and add and AGENTS.md to
tell agents to use the script.

- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-05-21 21:36:39 -04:00
Haven Xia acee90f9e5 Support streaming actor logs with --follow/-f for kubectl ate logs (#28)
This PR adds live-streaming log support (`--follow/-f`) to `kubectl-ate
logs actors`. Unlike static container logs, this implementation is
actor-aware and resilient to virtual actor suspensions and migrations:

Suspend Lifecycle:
<img width="3700" height="1856" alt="image"
src="https://github.com/user-attachments/assets/38568108-8229-40b0-afc7-d1d5ddc817e1"
/>

Resume Lifecycle:
<img width="3696" height="1530" alt="image"
src="https://github.com/user-attachments/assets/cf934608-62fe-4fb8-bd2c-5e6e7a0af776"
/>



> It's a good idea to open an issue first for discussion.

- [ ] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-05-21 18:09:07 -07:00
Troy Chiu c88402b98e fix failing kind demo commands in README (#57)
Fixes #55

I was following the demo but ran into the issues described in #55. The
command is failing because the curl command runs before the
port-forwarding is ready.
2026-05-21 18:02:15 -07:00
Davanum Srinivas 0ff0b271da hack: tear down the kind overlay symmetrically on --delete-all (#33)
`install-ate-kind.sh --deploy-ate-system` installs the kind overlay
(rustfs and the `otel-system` namespace) via a kustomize build of
`manifests/ate-install/kind`, but the matching teardown only removed
`kind/rustfs.yaml`, and only when `ATE_INSTALL_RUSTFS=true` was set in
the environment.

Teardown now deletes the same kustomize set whenever
`ATE_INSTALL_KIND=true` (already exported by `install-ate-kind.sh`). The
GKE path is unchanged; `ATE_INSTALL_RUSTFS` is unused after this change.

Fixes #<issue_number_goes_here>

> It's a good idea to open an issue first for discussion.

- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2026-05-21 17:50:40 -07:00
Peter Jausovec ce41f14fab sort actors/workers alphabetically
Signed-off-by: Peter Jausovec <peter.jausovec@solo.io>
2026-05-21 17:15:50 -07:00
Adrien Delorme 2f2292f171 fix(ategcs): surface GCS upload error during checkpoint (#40)
## Symptom

`atelet`'s `Checkpoint` RPC returns `err: nil` even when the GCS upload
it performs silently failed. The destination bucket stays empty (no
`checkpoint.img.zstd`, no `pages.img.zstd`), and every subsequent actor
`Resume` then fails with `storage: object doesn't exist`, which looks
like a Resume bug but is actually a checkpoint-upload bug.

## Root cause


[`internal/ategcs/ategcs.go:51`](https://github.com/agent-substrate/substrate/blob/main/internal/ategcs/ategcs.go#L51)
— `gcsClient.PutObject`:

```go
wc := g.client.Bucket(bucket).Object(object).NewWriter(ctx)
defer wc.Close()
_, err := io.Copy(wc, reader)
return err
```

For the GCS Go client, `io.Copy` only writes into a client-side buffer;
the actual HTTP PUT (and any API-level error like 403 from an OAuth
scope mismatch) is returned from `Writer.Close()`. The `defer
wc.Close()` here discards exactly that error, so PutObject returns `nil`
even when the upload was rejected.

## Fix

Return the error from `wc.Close()`. On `io.Copy` failure still
best-effort-close the writer but report the copy error, matching the
existing `slog.InfoContext("Dropped error ...")` pattern used elsewhere
in this file. Minimal, localised change; no API surface change.

## How I noticed this

Evaluating Substrate on a GKE Standard 1.35.3 cluster (with
`--enable-kubernetes-unstable-apis=certificates.k8s.io/v1beta1/{podcertificaterequests,clustertrustbundles}`)
where the node pool's OAuth scope was `devstorage.read_only`. That's my
misconfig — pods can't write to GCS — but the user-visible symptom was
confusing: `atelet.Checkpoint` reported success in ~282ms,
`gs://ate-snapshots-.../` was empty, and the first user-facing error
came from a completely unrelated actor's Resume call. Surfacing the
upload error at the checkpoint call site makes the misconfig immediately
diagnosable.

## Verification

- `GOOS=linux go build ./...` passes locally.
- No new dependencies, no new logging frameworks, single file touched.

Leaving as **draft** for maintainer eyes before marking ready — happy to
add a unit test against a fake `ObjectStorage` if you'd like, but didn't
want to bloat the diff before getting a signal on the approach.
2026-05-21 12:41:03 -07:00
Benjamin Elder e7e804a7db manage kind in tools module, add deletion script (#46)
We've already been managing other go binaries with tools modules.

Doing this limits the number of setup steps needed, and allows our
scripts to make assumptions about the kind version.

`kind` has minimal dependencies and should compile very quickly.

Label the registry as created by our script, so we know if we should
delete it.

- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-05-21 15:38:34 -04:00