Skip to content

Commit dab7773

Browse files
committed
Merge remote-tracking branch 'upstream/main'
# Conflicts: # cmd/ateapi/internal/controlapi/workflow.go # cmd/atenet/internal/router/router.go # cmd/atenet/internal/router/xds.go # cmd/atenet/internal/router/xds_test.go # internal/e2e/suites/demo/demo_test.go # manifests/ate-install/ate-controller.yaml # manifests/ate-install/atenet-router.yaml
2 parents 6c80130 + 46adcb8 commit dab7773

153 files changed

Lines changed: 8235 additions & 2652 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

benchmarking/automation/manifests/runner-job.yaml.tmpl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ spec:
7171
- name: servicedns-ca
7272
mountPath: /run/servicedns-ca
7373
readOnly: true
74+
- name: podidentity
75+
mountPath: /run/podidentity.podcert.ate.dev
76+
readOnly: true
7477
resources:
7578
requests:
7679
cpu: "500m"
@@ -85,3 +88,10 @@ spec:
8588
matchLabels:
8689
podcert.ate.dev/canarying: live
8790
path: ca.crt
91+
- name: podidentity
92+
projected:
93+
sources:
94+
- podCertificate:
95+
signerName: podidentity.podcert.ate.dev/identity
96+
keyType: ECDSAP256
97+
credentialBundlePath: credential-bundle.pem

benchmarking/locust/build_and_push.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@ fi
3131

3232
IMAGE="us-docker.pkg.dev/${PROJECT_ID}/gcr.io/ate-images/locust-test:latest"
3333

34-
echo "Building Docker image: $IMAGE"
34+
# Target platform must match the cluster's nodes, not the build host.
35+
PLATFORM="${LOCUST_IMAGE_PLATFORM:-linux/amd64}"
36+
37+
echo "Building Docker image: $IMAGE (platform: $PLATFORM)"
3538
# Build context is the monorepo root because the Dockerfile compiles the
3639
# boomer-glutton Go binary alongside the Python install (see Dockerfile).
37-
docker build -t "$IMAGE" -f benchmarking/locust/Dockerfile .
40+
docker build --platform "$PLATFORM" -t "$IMAGE" -f benchmarking/locust/Dockerfile .
3841

3942
echo "Pushing Docker image..."
4043
docker push "$IMAGE"

benchmarking/locust/manifests/locust.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ spec:
9090
- "-f"
9191
- "/app/tests"
9292
- "--worker"
93-
- "--master-host=localhost"
93+
# Literal IPv4, not "localhost". The master binds 0.0.0.0:5557, which
94+
# is IPv4-only; locust sets ZMQ_IPV6 on the worker socket whenever the
95+
# master host resolves to anything AF_INET6, and the pod's /etc/hosts
96+
# maps localhost to ::1 as well as 127.0.0.1. The worker would then
97+
# dial [::1]:5557 and retry forever against a silent master.
98+
- "--master-host=127.0.0.1"
9499
env:
95100
- name: OTEL_EXPORTER_OTLP_ENDPOINT
96101
value: http://opentelemetry-collector.gke-managed-otel.svc.cluster.local:4317

charts/substrate-crds/templates/ate.dev_workerpools.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,17 @@ spec:
411411
type: array
412412
x-kubernetes-list-type: atomic
413413
type: object
414+
terminationGracePeriodSeconds:
415+
default: 300
416+
description: |-
417+
TerminationGracePeriodSeconds is the termination grace period applied to
418+
this pool's worker pods. On eviction, ateom traps SIGTERM and forwards it
419+
to the actor so it can save state and exit cleanly before the kubelet
420+
sends SIGKILL. Tune this to the maximum time your actors need to shut
421+
down gracefully. Defaults to 300 (5 minutes).
422+
format: int32
423+
minimum: 1
424+
type: integer
414425
required:
415426
- ateomImage
416427
- replicas

charts/substrate/templates/ate-controller.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ spec:
8585
- "--ateapi-ca-file=/run/ateapi-ca/ca.crt"
8686
- "--ateapi-server-name={{ include "substrate.fullname" (list "api" .) }}.{{ .Release.Namespace }}.svc"
8787
- "--ateapi-token-file=/var/run/secrets/tokens/ateapi/token"
88+
{{- end }}
89+
{{- if .Values.otel.endpoint }}
90+
env:
91+
- name: OTEL_EXPORTER_OTLP_ENDPOINT
92+
value: {{ .Values.otel.endpoint | quote }}
8893
{{- end }}
8994
ports:
9095
- name: metrics

charts/substrate/templates/atenet-router.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,7 @@ spec:
294294
port: 443
295295
targetPort: 8443
296296
protocol: TCP
297+
- name: status
298+
port: 4040
299+
targetPort: status
300+
protocol: TCP

charts/substrate/templates/role.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,15 @@ rules:
8080
- get
8181
- patch
8282
- update
83+
- apiGroups:
84+
- networking.k8s.io
85+
resources:
86+
- networkpolicies
87+
verbs:
88+
- create
89+
- delete
90+
- get
91+
- list
92+
- patch
93+
- update
94+
- watch

cmd/ateapi/internal/controlapi/crash.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,34 @@ import (
2222

2323
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store"
2424
"github.com/agent-substrate/substrate/internal/ateerrors"
25+
"github.com/agent-substrate/substrate/internal/resources"
2526
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
2627
"google.golang.org/grpc/codes"
2728
"google.golang.org/grpc/status"
2829
)
2930

3031
// maybeCrashActor inspects err returned by an atelet RPC, it crashes
3132
// the actor if the err carries the actorCrashed=true metadata directive.
32-
func maybeCrashActor(ctx context.Context, st store.Interface, atespace, actorName string, err error, wrapMsg string) error {
33+
func maybeCrashActor(ctx context.Context, st store.Interface, actorRef resources.ActorRef, err error, wrapMsg string) error {
3334
if err == nil {
3435
return nil
3536
}
3637

3738
if ateerrors.ActorCrashRequested(err) {
3839
slog.ErrorContext(ctx, "Setting Actor to crashed due to error", slog.Any("error", err))
39-
if cerr := crashActor(ctx, st, atespace, actorName); cerr != nil {
40+
if cerr := crashActor(ctx, st, actorRef); cerr != nil {
4041
slog.ErrorContext(ctx, "Failed to crash actor", slog.Any("cerr", cerr))
4142
return cerr
4243
}
43-
return status.Errorf(codes.DataLoss, "actor %s crashed", actorName)
44+
return status.Errorf(codes.DataLoss, "actor %s crashed", actorRef)
4445
}
4546
return fmt.Errorf("%s: %w", wrapMsg, err)
4647
}
4748

4849
// crashActor moves the actor to CRASHED state and frees the worker it was
4950
// assigned to, if any, so the worker can host other actors.
50-
func crashActor(ctx context.Context, st store.Interface, atespace, actorName string) error {
51-
actor, err := st.GetActor(ctx, atespace, actorName)
51+
func crashActor(ctx context.Context, st store.Interface, actorRef resources.ActorRef) error {
52+
actor, err := st.GetActor(ctx, actorRef)
5253
if err != nil {
5354
return fmt.Errorf("while loading actor to crash: %w", err)
5455
}
@@ -102,7 +103,7 @@ func releaseWorker(ctx context.Context, st store.Interface, actor *ateapipb.Acto
102103
return nil
103104
}
104105
// Only free it if it still belongs to us
105-
if wass.GetActor().GetAtespace() != actor.GetMetadata().GetAtespace() || wass.GetActor().GetName() != actor.GetMetadata().GetName() {
106+
if resources.ActorRefFromObjectRef(wass.GetActor()) != resources.ActorRefFromActor(actor) {
106107
slog.WarnContext(ctx, "Worker already assigned to another Actor", slog.String("worker", podUid))
107108
return nil
108109
}

cmd/ateapi/internal/controlapi/crash_test.go

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,18 @@ import (
2323
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store"
2424
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store/storetest"
2525
"github.com/agent-substrate/substrate/internal/ateerrors"
26+
"github.com/agent-substrate/substrate/internal/resources"
2627
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
2728
"google.golang.org/grpc/codes"
2829
"google.golang.org/grpc/status"
2930
)
3031

3132
// seedActor stores a running actor with all worker-binding fields populated, so
3233
// tests can assert they are cleared when the actor crashes.
33-
func seedActor(t *testing.T, ctx context.Context, st store.Interface, atespace, actorName string) {
34+
func seedActor(t *testing.T, ctx context.Context, st store.Interface, actorRef resources.ActorRef) {
3435
t.Helper()
3536
if _, err := st.CreateActor(ctx, &ateapipb.Actor{
36-
Metadata: &ateapipb.ResourceMetadata{Name: actorName, Atespace: atespace},
37+
Metadata: &ateapipb.ResourceMetadata{Name: actorRef.Name, Atespace: actorRef.Atespace},
3738
Status: ateapipb.Actor_STATUS_RUNNING,
3839
AteomPodNamespace: "ns",
3940
AteomPodName: "pod",
@@ -47,17 +48,17 @@ func seedActor(t *testing.T, ctx context.Context, st store.Interface, atespace,
4748
}
4849

4950
// seedWorker registers the worker referenced by seedActor's binding fields,
50-
// assigned to the given actor in atespace (unassigned if assignedActor is "").
51-
func seedWorker(t *testing.T, ctx context.Context, st store.Interface, atespace, assignedActor string) {
51+
// assigned to the given actor (unassigned if assigned is the zero ActorRef).
52+
func seedWorker(t *testing.T, ctx context.Context, st store.Interface, actorRef resources.ActorRef) {
5253
t.Helper()
5354
worker := &ateapipb.Worker{
5455
WorkerNamespace: "ns",
5556
WorkerPool: "pool",
5657
WorkerPod: "pod",
5758
}
58-
if assignedActor != "" {
59+
if actorRef != (resources.ActorRef{}) {
5960
worker.Assignment = &ateapipb.Assignment{
60-
Actor: &ateapipb.ObjectRef{Atespace: atespace, Name: assignedActor},
61+
Actor: actorRef.ToObjectRef(),
6162
}
6263
}
6364
if err := st.CreateWorker(ctx, worker); err != nil {
@@ -67,10 +68,10 @@ func seedWorker(t *testing.T, ctx context.Context, st store.Interface, atespace,
6768

6869
// seedUnboundActor stores a running actor whose worker-binding fields were
6970
// already cleared, e.g. by a prior release.
70-
func seedUnboundActor(t *testing.T, ctx context.Context, st store.Interface, atespace, actorName string) {
71+
func seedUnboundActor(t *testing.T, ctx context.Context, st store.Interface, actorRef resources.ActorRef) {
7172
t.Helper()
7273
if _, err := st.CreateActor(ctx, &ateapipb.Actor{
73-
Metadata: &ateapipb.ResourceMetadata{Name: actorName, Atespace: atespace},
74+
Metadata: &ateapipb.ResourceMetadata{Name: actorRef.Name, Atespace: actorRef.Atespace},
7475
Status: ateapipb.Actor_STATUS_RUNNING,
7576
InProgressSnapshot: "gs://snapshots/actor-1/reserved",
7677
}); err != nil {
@@ -80,11 +81,11 @@ func seedUnboundActor(t *testing.T, ctx context.Context, st store.Interface, ate
8081

8182
// assertCrashed reloads the actor and verifies it is CRASHED with its worker
8283
// binding cleared.
83-
func assertCrashed(t *testing.T, ctx context.Context, st store.Interface, atespace, actorName string) {
84+
func assertCrashed(t *testing.T, ctx context.Context, st store.Interface, actorRef resources.ActorRef) {
8485
t.Helper()
85-
got, err := st.GetActor(ctx, atespace, actorName)
86+
got, err := st.GetActor(ctx, actorRef)
8687
if err != nil {
87-
t.Fatalf("GetActor(%q, %q) = %v, want nil", atespace, actorName, err)
88+
t.Fatalf("GetActor(%v) = %v, want nil", actorRef, err)
8889
}
8990
if got.GetStatus() != ateapipb.Actor_STATUS_CRASHED {
9091
t.Errorf("status = %v, want %v", got.GetStatus(), ateapipb.Actor_STATUS_CRASHED)
@@ -107,10 +108,7 @@ func assertCrashed(t *testing.T, ctx context.Context, st store.Interface, atespa
107108
}
108109

109110
func TestCrashActor(t *testing.T) {
110-
const (
111-
atespace = "team-a"
112-
actorName = "actor-1"
113-
)
111+
actorRef := resources.ActorRef{Atespace: "team-a", Name: "actor-1"}
114112

115113
tests := []struct {
116114
name string
@@ -127,20 +125,20 @@ func TestCrashActor(t *testing.T) {
127125
if err != nil {
128126
t.Fatalf("crashActor() = %v, want nil", err)
129127
}
130-
assertCrashed(t, ctx, st, atespace, actorName)
128+
assertCrashed(t, ctx, st, actorRef)
131129
},
132130
},
133131
{
134132
name: "releases worker assigned to crashed actor",
135133
seed: true,
136134
setup: func(t *testing.T, ctx context.Context, st store.Interface) {
137-
seedWorker(t, ctx, st, atespace, actorName)
135+
seedWorker(t, ctx, st, actorRef)
138136
},
139137
check: func(t *testing.T, ctx context.Context, st store.Interface, err error) {
140138
if err != nil {
141139
t.Fatalf("crashActor() = %v, want nil", err)
142140
}
143-
assertCrashed(t, ctx, st, atespace, actorName)
141+
assertCrashed(t, ctx, st, actorRef)
144142
worker, gerr := st.GetWorker(ctx, "ns", "pool", "pod")
145143
if gerr != nil {
146144
t.Fatalf("GetWorker() = %v, want nil", gerr)
@@ -154,13 +152,13 @@ func TestCrashActor(t *testing.T) {
154152
name: "keeps worker assigned to another actor",
155153
seed: true,
156154
setup: func(t *testing.T, ctx context.Context, st store.Interface) {
157-
seedWorker(t, ctx, st, atespace, "actor-2")
155+
seedWorker(t, ctx, st, resources.ActorRef{Atespace: actorRef.Atespace, Name: "actor-2"})
158156
},
159157
check: func(t *testing.T, ctx context.Context, st store.Interface, err error) {
160158
if err != nil {
161159
t.Fatalf("crashActor() = %v, want nil", err)
162160
}
163-
assertCrashed(t, ctx, st, atespace, actorName)
161+
assertCrashed(t, ctx, st, actorRef)
164162
worker, gerr := st.GetWorker(ctx, "ns", "pool", "pod")
165163
if gerr != nil {
166164
t.Fatalf("GetWorker() = %v, want nil", gerr)
@@ -174,14 +172,14 @@ func TestCrashActor(t *testing.T) {
174172
name: "skips release for actor with no worker binding",
175173
seed: false,
176174
setup: func(t *testing.T, ctx context.Context, st store.Interface) {
177-
seedUnboundActor(t, ctx, st, atespace, actorName)
178-
seedWorker(t, ctx, st, atespace, actorName)
175+
seedUnboundActor(t, ctx, st, actorRef)
176+
seedWorker(t, ctx, st, actorRef)
179177
},
180178
check: func(t *testing.T, ctx context.Context, st store.Interface, err error) {
181179
if err != nil {
182180
t.Fatalf("crashActor() = %v, want nil", err)
183181
}
184-
assertCrashed(t, ctx, st, atespace, actorName)
182+
assertCrashed(t, ctx, st, actorRef)
185183
// Without a binding the worker cannot be looked up, so its
186184
// assignment must be left untouched even though it names
187185
// the crashed actor.
@@ -218,24 +216,21 @@ func TestCrashActor(t *testing.T) {
218216
defer cleanup()
219217

220218
if tt.seed {
221-
seedActor(t, ctx, st, atespace, actorName)
219+
seedActor(t, ctx, st, actorRef)
222220
}
223221
if tt.setup != nil {
224222
tt.setup(t, ctx, st)
225223
}
226224

227-
err := crashActor(ctx, st, atespace, actorName)
225+
err := crashActor(ctx, st, actorRef)
228226
tt.check(t, ctx, st, err)
229227
})
230228
}
231229
}
232230

233231
func TestMaybeCrashActor(t *testing.T) {
234-
const (
235-
atespace = "team-a"
236-
actorName = "actor-1"
237-
wrapMsg = "calling atelet"
238-
)
232+
const wrapMsg = "calling atelet"
233+
actorRef := resources.ActorRef{Atespace: "team-a", Name: "actor-1"}
239234

240235
crashErr := ateerrors.NewGRPCError(context.Background(), codes.NotFound, ateerrors.ReasonTerminalFileSystemError, ateerrors.ActorCrashedMetadata(), errors.New("boom"))
241236
// A structured error carrying a reason but no actorCrashed directive must be
@@ -271,7 +266,7 @@ func TestMaybeCrashActor(t *testing.T) {
271266
if got := status.Code(err); got != codes.DataLoss {
272267
t.Errorf("status code = %v, want %v", got, codes.DataLoss)
273268
}
274-
assertCrashed(t, ctx, st, atespace, actorName)
269+
assertCrashed(t, ctx, st, actorRef)
275270
},
276271
},
277272
{
@@ -305,7 +300,7 @@ func TestMaybeCrashActor(t *testing.T) {
305300
t.Errorf("maybeCrashActor() error = %q, want prefix %q", err, wrapMsg)
306301
}
307302
// The actor must not have been crashed.
308-
got, gerr := st.GetActor(ctx, atespace, actorName)
303+
got, gerr := st.GetActor(ctx, actorRef)
309304
if gerr != nil {
310305
t.Fatalf("GetActor() = %v, want nil", gerr)
311306
}
@@ -329,7 +324,7 @@ func TestMaybeCrashActor(t *testing.T) {
329324
t.Errorf("maybeCrashActor() error = %q, want prefix %q", err, wrapMsg)
330325
}
331326
// The actor must not have been crashed.
332-
got, gerr := st.GetActor(ctx, atespace, actorName)
327+
got, gerr := st.GetActor(ctx, actorRef)
333328
if gerr != nil {
334329
t.Fatalf("GetActor() = %v, want nil", gerr)
335330
}
@@ -347,10 +342,10 @@ func TestMaybeCrashActor(t *testing.T) {
347342
defer cleanup()
348343

349344
if tt.seed {
350-
seedActor(t, ctx, st, atespace, actorName)
345+
seedActor(t, ctx, st, actorRef)
351346
}
352347

353-
err := maybeCrashActor(ctx, st, atespace, actorName, tt.err, wrapMsg)
348+
err := maybeCrashActor(ctx, st, actorRef, tt.err, wrapMsg)
354349
tt.check(t, ctx, st, err)
355350
})
356351
}

0 commit comments

Comments
 (0)