Skip to content

Commit c1ab095

Browse files
juli4nHavenXia
authored andcommitted
Update atenet component to use actor *name* instead of *id*.
1 parent 669965a commit c1ab095

13 files changed

Lines changed: 108 additions & 136 deletions

File tree

cmd/ateapi/internal/controlapi/list_actors.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ func (s *Service) ListActors(ctx context.Context, req *ateapipb.ListActorsReques
4848
func validateListActorsRequest(req *ateapipb.ListActorsRequest) error {
4949
// An empty atespace is allowed here and means "all atespaces"(used by `kubectl ate get actors -A`).
5050
// A non-empty atespace is validated and scopes the listing to that atespace.
51-
if req.GetAtespace() != "" {
52-
if err := resources.ValidateAtespace(req.GetAtespace()); err != nil {
53-
return err
54-
}
51+
if req.GetAtespace() != "" && !resources.IsValidResourceName(req.GetAtespace()) {
52+
return fmt.Errorf("invalid atespace %q: must be a valid resource name", req.GetAtespace())
5553
}
5654
pageSize := req.GetPageSize()
5755
if pageSize < 0 {

cmd/atenet/internal/dns/corefile.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ func buildTemplate() string {
3939
directives = append(directives, "ready :8181")
4040
directives = append(directives, "reload")
4141

42-
// Construct match pattern for <ActorID>.<atespace>.<dnsDomain>. Both the
43-
// actor id and the atespace are DNS-1123 labels (same regex).
42+
// Construct match pattern for <ActorName>.<atespace>.<dnsDomain>. Both the
43+
// actor name and the atespace are DNS-1123 labels (same regex).
4444
directives = append(directives, fmt.Sprintf("template IN A %s {", resources.ActorDNSSuffix))
4545
// Escape the suffix's dots so they match literally; the final \. matches the FQDN's trailing dot.
4646
escapedSuffix := strings.ReplaceAll(resources.ActorDNSSuffix, ".", `\.`)
47-
directives = append(directives, fmt.Sprintf(` match "^%s\.%s\.%s\.$"`, resources.ActorIDRegexPattern, resources.ActorIDRegexPattern, escapedSuffix))
47+
directives = append(directives, fmt.Sprintf(` match "^%s\.%s\.%s\.$"`, resources.ResourceNameRegexPattern, resources.ResourceNameRegexPattern, escapedSuffix))
4848
// Note the %s -- this will be filled with the router IP.
4949
directives = append(directives, ` answer "{{ .Name }} 60 IN A %s"`)
5050
directives = append(directives, "}")

cmd/atenet/internal/dns/corefile_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestMakeCoreFile(t *testing.T) {
3838
"ready :8181",
3939
"reload",
4040
"template IN A actors.resources.substrate.ate.dev {",
41-
`match "^` + resources.ActorIDRegexPattern + `\.` + resources.ActorIDRegexPattern + `\.actors\.resources\.substrate\.ate\.dev\.$"`,
41+
`match "^` + resources.ResourceNameRegexPattern + `\.` + resources.ResourceNameRegexPattern + `\.actors\.resources\.substrate\.ate\.dev\.$"`,
4242
`answer "{{ .Name }} 60 IN A 10.240.0.10"`,
4343
},
4444
},

cmd/atenet/internal/router/errors.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ func newReqError(code envoy_type.StatusCode, format string, args ...any) error {
3232
}
3333

3434
// actorNotFoundErr returns a 404 reqError identifying the missing actor.
35-
func actorNotFoundErr(actorID string) error {
36-
return newReqError(envoy_type.StatusCode_NotFound, "actor %q not found", actorID)
35+
func actorNotFoundErr(actorName string) error {
36+
return newReqError(envoy_type.StatusCode_NotFound, "actor %q not found", actorName)
3737
}
3838

3939
// invalidHostErr returns a 404 reqError explaining why the request host was
@@ -53,7 +53,7 @@ func invalidHostErr(host string, cause error) error {
5353
//
5454
// Unrecognized errors collapse to 500 with a generic body to avoid leaking
5555
// server-side detail (stack traces, internal IDs) to clients.
56-
func mapResumeError(actorID string, err error) error {
56+
func mapResumeError(actorName string, err error) error {
5757
if err == nil {
5858
return nil
5959
}
@@ -62,31 +62,31 @@ func mapResumeError(actorID string, err error) error {
6262
switch status.Code(err) {
6363
case codes.NotFound:
6464
re.statusCode = int(envoy_type.StatusCode_NotFound)
65-
re.msg = fmt.Sprintf("actor %q not found", actorID)
65+
re.msg = fmt.Sprintf("actor %q not found", actorName)
6666
case codes.FailedPrecondition:
6767
// Preserve the gRPC description for FailedPrecondition only: it carries
6868
// actionable client-facing context (e.g. "no free workers available")
6969
// and is not security-sensitive.
7070
re.statusCode = int(envoy_type.StatusCode_ServiceUnavailable)
71-
re.msg = fmt.Sprintf("actor %q unavailable: %s", actorID, status.Convert(err).Message())
71+
re.msg = fmt.Sprintf("actor %q unavailable: %s", actorName, status.Convert(err).Message())
7272
case codes.Unavailable:
7373
re.statusCode = int(envoy_type.StatusCode_ServiceUnavailable)
74-
re.msg = fmt.Sprintf("actor %q unavailable", actorID)
74+
re.msg = fmt.Sprintf("actor %q unavailable", actorName)
7575
case codes.DeadlineExceeded:
7676
re.statusCode = int(envoy_type.StatusCode_GatewayTimeout)
77-
re.msg = fmt.Sprintf("actor %q request timed out", actorID)
77+
re.msg = fmt.Sprintf("actor %q request timed out", actorName)
7878
case codes.PermissionDenied:
7979
re.statusCode = int(envoy_type.StatusCode_Forbidden)
80-
re.msg = fmt.Sprintf("actor %q access denied", actorID)
80+
re.msg = fmt.Sprintf("actor %q access denied", actorName)
8181
case codes.Unauthenticated:
8282
re.statusCode = int(envoy_type.StatusCode_Unauthorized)
83-
re.msg = fmt.Sprintf("actor %q authentication required", actorID)
83+
re.msg = fmt.Sprintf("actor %q authentication required", actorName)
8484
case codes.ResourceExhausted:
8585
re.statusCode = int(envoy_type.StatusCode_TooManyRequests)
86-
re.msg = fmt.Sprintf("actor %q rate limited", actorID)
86+
re.msg = fmt.Sprintf("actor %q rate limited", actorName)
8787
default:
8888
re.statusCode = int(envoy_type.StatusCode_InternalServerError)
89-
re.msg = fmt.Sprintf("error resuming actor %q", actorID)
89+
re.msg = fmt.Sprintf("error resuming actor %q", actorName)
9090
}
9191
return re
9292
}

cmd/atenet/internal/router/errors_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func TestInvalidHostErr(t *testing.T) {
8282
func TestMapResumeError(t *testing.T) {
8383
t.Parallel()
8484

85-
const actorID = "ctr6"
85+
const actorName = "ctr6"
8686

8787
tests := []struct {
8888
name string
@@ -150,7 +150,7 @@ func TestMapResumeError(t *testing.T) {
150150
t.Run(tc.name, func(t *testing.T) {
151151
t.Parallel()
152152

153-
got := mapResumeError(actorID, tc.err)
153+
got := mapResumeError(actorName, tc.err)
154154
if got == nil {
155155
t.Fatal("mapResumeError returned nil")
156156
}

cmd/atenet/internal/router/extproc.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,16 @@ func (s *ExtProcServer) handleRequestHeaders(
142142
ctx, span := otel.Tracer(routerServiceName).Start(ctx, "ExtProc.RequestHeaders")
143143
defer span.End()
144144

145-
atespace, actorID, err := parseActorRef(metadata.host)
145+
atespace, actorName, err := parseActorRef(metadata.host)
146146
if err != nil {
147147
// Host is invalid, respond with 404.
148148
return nil, metadata, "", "", "", invalidHostErr(metadata.host, err)
149149
}
150150

151-
slog.InfoContext(ctx, "ResumeActor", slog.String("atespace", atespace), slog.String("actorID", actorID))
152-
actor, err := s.resumer.ResumeActor(ctx, atespace, actorID)
151+
slog.InfoContext(ctx, "ResumeActor", slog.String("atespace", atespace), slog.String("actor", actorName))
152+
actor, err := s.resumer.ResumeActor(ctx, atespace, actorName)
153153
if err != nil {
154-
return nil, metadata, "", "", "", mapResumeError(actorID, err)
154+
return nil, metadata, "", "", "", mapResumeError(actorName, err)
155155
}
156156

157157
// Actor template identity, used as low-cardinality route-latency metric
@@ -162,19 +162,19 @@ func (s *ExtProcServer) handleRequestHeaders(
162162
workerIP := actor.GetAteomPodIp()
163163
slog.InfoContext(ctx, "ResumeActor result",
164164
slog.String("atespace", atespace),
165-
slog.String("actorID", actorID),
165+
slog.String("actor", actorName),
166166
slog.String("status", actor.GetStatus().String()),
167167
slog.String("workerIP", workerIP))
168168

169169
if ip := net.ParseIP(workerIP); ip == nil {
170170
return nil, metadata, "", tmplNs, tmplName, newReqError(envoy_type.StatusCode_InternalServerError,
171-
"actor %q routing failed", actorID)
171+
"actor %q routing failed", actorName)
172172
}
173173

174174
// TODO(bowei) -- handle more than port 80 on the actor.
175175
targetAddr := net.JoinHostPort(workerIP, "80")
176176

177-
slog.InfoContext(ctx, "Route ok", slog.String("actorID", actorID), slog.String("targetAddr", targetAddr))
177+
slog.InfoContext(ctx, "Route ok", slog.String("actor", actorName), slog.String("targetAddr", targetAddr))
178178

179179
// Route by rewriting the :authority header.
180180
mutation := &extprocv3.HeaderMutation{}

cmd/atenet/internal/router/extproc_in.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ func newRequestMetadata(headers []*corev3.HeaderValue) *requestMetadata {
5656
}
5757
}
5858

59-
// parseActorRef extracts the (atespace, actor id) an incoming request is
59+
// parseActorRef extracts the (atespace, actor name) an incoming request is
6060
// addressed to from its Host/:authority, which has the form
61-
// "<actor_id>.<atespace>.actors.resources.substrate.ate.dev" (optionally with a
62-
// port). The atespace is required because an actor id is only unique within its
61+
// "<actor_name>.<atespace>.actors.resources.substrate.ate.dev" (optionally with a
62+
// port). The atespace is required because an actor name is only unique within its
6363
// atespace.
64-
func parseActorRef(host string) (atespace, actorID string, err error) {
64+
func parseActorRef(host string) (atespace, actorName string, err error) {
6565
if strings.Contains(host, ":") {
6666
host, _, err = net.SplitHostPort(host)
6767
if err != nil {

cmd/atenet/internal/router/resumer.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ func NewActorResumer(apiClient ateapipb.ControlClient) *ActorResumer {
4242

4343
// ResumeActor ensures the requested actor is running. It deduplicates concurrent
4444
// requests within the process and retries when needed. The actor is addressed by
45-
// (atespace, actorID) since an actor id is only unique within its atespace.
46-
func (r *ActorResumer) ResumeActor(ctx context.Context, atespace, actorID string) (*ateapipb.Actor, error) {
45+
// (atespace, actorName) since an actor name is only unique within its atespace.
46+
func (r *ActorResumer) ResumeActor(ctx context.Context, atespace, actorName string) (*ateapipb.Actor, error) {
4747
ctx, span := otel.Tracer(routerServiceName).Start(ctx, "ResumeActor",
4848
trace.WithAttributes(
4949
attribute.String("atespace", atespace),
50-
attribute.String("actor_id", actorID),
50+
attribute.String("actor", actorName),
5151
))
5252
defer span.End()
5353

54-
ch := r.flight.DoChan(atespace+"/"+actorID, func() (interface{}, error) {
54+
ch := r.flight.DoChan(atespace+"/"+actorName, func() (interface{}, error) {
5555
// We detach the context from the first caller using a fixed background timeout.
5656
// This guarantees that if Caller 1 disconnects or times out, the underlying
5757
// resume operation continues running for Caller 2 and Caller 3 without failing.
@@ -70,7 +70,7 @@ func (r *ActorResumer) ResumeActor(ctx context.Context, atespace, actorID string
7070
err := wait.ExponentialBackoffWithContext(bgCtx, backoff, func(ctx context.Context) (bool, error) {
7171
var err error
7272
resumeResp, err = r.apiClient.ResumeActor(ctx, &ateapipb.ResumeActorRequest{
73-
Actor: &ateapipb.ObjectRef{Atespace: atespace, Name: actorID},
73+
Actor: &ateapipb.ObjectRef{Atespace: atespace, Name: actorName},
7474
})
7575
if err == nil {
7676
return true, nil

cmd/atenet/internal/router/resumer_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func (m *resumerMockClient) ResumeActor(ctx context.Context, in *ateapipb.Resume
3939
}
4040

4141
func TestActorResumer_ResumeActor(t *testing.T) {
42-
const testActorID = "actor-a"
42+
const testActorName = "actor-a"
4343
const testAtespace = "team-a"
4444
const expectedIP = "10.0.0.52"
4545

@@ -50,7 +50,7 @@ func TestActorResumer_ResumeActor(t *testing.T) {
5050
resumeCalled++
5151
return &ateapipb.ResumeActorResponse{
5252
Actor: &ateapipb.Actor{
53-
Metadata: &ateapipb.ResourceMetadata{Name: testActorID},
53+
Metadata: &ateapipb.ResourceMetadata{Name: testActorName},
5454
Status: ateapipb.Actor_STATUS_RUNNING,
5555
AteomPodIp: expectedIP,
5656
},
@@ -59,7 +59,7 @@ func TestActorResumer_ResumeActor(t *testing.T) {
5959
}
6060

6161
resumer := NewActorResumer(mock)
62-
actor, err := resumer.ResumeActor(context.Background(), testAtespace, testActorID)
62+
actor, err := resumer.ResumeActor(context.Background(), testAtespace, testActorName)
6363
if err != nil {
6464
t.Fatalf("unexpected error: %v", err)
6565
}
@@ -81,7 +81,7 @@ func TestActorResumer_ResumeActor(t *testing.T) {
8181
}
8282
return &ateapipb.ResumeActorResponse{
8383
Actor: &ateapipb.Actor{
84-
Metadata: &ateapipb.ResourceMetadata{Name: testActorID},
84+
Metadata: &ateapipb.ResourceMetadata{Name: testActorName},
8585
Status: ateapipb.Actor_STATUS_RUNNING,
8686
AteomPodIp: expectedIP,
8787
},
@@ -90,7 +90,7 @@ func TestActorResumer_ResumeActor(t *testing.T) {
9090
}
9191

9292
resumer := NewActorResumer(mock)
93-
actor, err := resumer.ResumeActor(context.Background(), testAtespace, testActorID)
93+
actor, err := resumer.ResumeActor(context.Background(), testAtespace, testActorName)
9494
if err != nil {
9595
t.Fatalf("unexpected error: %v", err)
9696
}
@@ -110,7 +110,7 @@ func TestActorResumer_ResumeActor(t *testing.T) {
110110
}
111111

112112
resumer := NewActorResumer(mock)
113-
_, err := resumer.ResumeActor(context.Background(), testAtespace, testActorID)
113+
_, err := resumer.ResumeActor(context.Background(), testAtespace, testActorName)
114114
if got := status.Code(err); got != codes.NotFound {
115115
t.Errorf("expected gRPC code NotFound, got %v (err=%v)", got, err)
116116
}
@@ -128,7 +128,7 @@ func TestActorResumer_ResumeActor(t *testing.T) {
128128
time.Sleep(20 * time.Millisecond)
129129
return &ateapipb.ResumeActorResponse{
130130
Actor: &ateapipb.Actor{
131-
Metadata: &ateapipb.ResourceMetadata{Name: testActorID},
131+
Metadata: &ateapipb.ResourceMetadata{Name: testActorName},
132132
Status: ateapipb.Actor_STATUS_RUNNING,
133133
AteomPodIp: expectedIP,
134134
},
@@ -147,7 +147,7 @@ func TestActorResumer_ResumeActor(t *testing.T) {
147147
for i := 0; i < concurrentRequests; i++ {
148148
go func(idx int) {
149149
defer wg.Done()
150-
results[idx], errs[idx] = resumer.ResumeActor(context.Background(), testAtespace, testActorID)
150+
results[idx], errs[idx] = resumer.ResumeActor(context.Background(), testAtespace, testActorName)
151151
}(i)
152152
}
153153
wg.Wait()

internal/resources/actor.go

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,80 +16,46 @@ package resources
1616

1717
import (
1818
"fmt"
19-
"regexp"
2019
"strings"
2120
)
2221

2322
const (
24-
// ActorIDRegexPattern is the regular expression pattern for matching valid actor IDs.
25-
ActorIDRegexPattern = `[a-z0-9]([-a-z0-9]*[a-z0-9])?`
23+
// ResourceNameRegexPattern is the regular expression pattern for a valid
24+
// Substrate resource name.
25+
ResourceNameRegexPattern = `[a-z0-9]([-a-z0-9]*[a-z0-9])?`
2626
// ActorDNSSuffix is suffix to the DNS name for direct access to Actor
27-
// "<actor id>.<atespace>.actors.resources.substrate.ate.dev."
27+
// "<actor_name>.<atespace>.actors.resources.substrate.ate.dev."
2828
ActorDNSSuffix = "actors.resources.substrate.ate.dev"
2929
// GoldenActorAtespace is the reserved system atespace that per-template golden
3030
// actors live in.
3131
GoldenActorAtespace = "ate-golden"
3232
)
3333

34-
var actorIDRegex = regexp.MustCompile("^" + ActorIDRegexPattern + "$")
35-
36-
// TODO: unify actor/atespace validation across the control API RPCs — some only
37-
// reject empty strings (get/pause/resume/suspend), others run the full validator.
38-
39-
// ValidateActorID validates whether the provided actor ID is valid or not.
40-
// Actor IDs must be valid DNS-1123 labels.
41-
//
42-
// 1. Must be between 1 and 63 characters in length.
43-
// 2. Must start with a lower-case alphanumeric character (a-z, 0-9).
44-
// 3. Must contain only lower-case alphanumeric characters and hyphens (a-z, 0-9, -).
45-
// 4. Must end with a lower-case alphanumeric character (cannot end with a hyphen).
46-
func ValidateActorID(id string) error {
47-
if len(id) > 63 {
48-
return fmt.Errorf("invalid actor_id: must be no more than 63 characters")
49-
}
50-
if !actorIDRegex.MatchString(id) {
51-
return fmt.Errorf("invalid actor_id: must start and end with a lower case alphanumeric character, and consist only of lower case alphanumeric characters or '-'")
52-
}
53-
return nil
54-
}
55-
56-
// ValidateAtespace validates whether the provided atespace name is valid. An
57-
// atespace must be a valid DNS-1123 label (same rules as an actor ID above).
58-
func ValidateAtespace(atespace string) error {
59-
if len(atespace) > 63 {
60-
return fmt.Errorf("invalid atespace: must be no more than 63 characters")
61-
}
62-
if !actorIDRegex.MatchString(atespace) {
63-
return fmt.Errorf("invalid atespace: must start and end with a lower case alphanumeric character, and consist only of lower case alphanumeric characters or '-'")
64-
}
65-
return nil
66-
}
67-
6834
// ActorDNSName returns the mesh DNS name an actor is reachable at:
69-
// "<actor_id>.<atespace>.actors.resources.substrate.ate.dev". The atespace is
70-
// part of the name because an actor id is only unique within its atespace.
71-
func ActorDNSName(atespace, actorID string) string {
72-
return actorID + "." + atespace + "." + ActorDNSSuffix
35+
// "<actor_name>.<atespace>.actors.resources.substrate.ate.dev". The atespace is
36+
// part of the name because an actor name is only unique within its atespace.
37+
func ActorDNSName(atespace, actorName string) string {
38+
return actorName + "." + atespace + "." + ActorDNSSuffix
7339
}
7440

7541
// ParseActorDNSName parses a mesh DNS name of the form
76-
// "<actor_id>.<atespace>.actors.resources.substrate.ate.dev" (a trailing dot is
77-
// tolerated) into its atespace and actor id, validating both. It does not accept
78-
// a host:port; callers must strip the port first.
79-
func ParseActorDNSName(name string) (atespace, actorID string, err error) {
42+
// "<actor_name>.<atespace>.actors.resources.substrate.ate.dev" (a trailing dot
43+
// is tolerated) into its atespace and actor name, validating both. It does not
44+
// accept a host:port; callers must strip the port first.
45+
func ParseActorDNSName(name string) (atespace, actorName string, err error) {
8046
rest, found := strings.CutSuffix(strings.TrimSuffix(name, "."), "."+ActorDNSSuffix)
8147
if !found {
8248
return "", "", fmt.Errorf("invalid actor DNS name: must end with %s, got %q", ActorDNSSuffix, name)
8349
}
84-
actorID, atespace, found = strings.Cut(rest, ".")
50+
actorName, atespace, found = strings.Cut(rest, ".")
8551
if !found {
86-
return "", "", fmt.Errorf("invalid actor DNS name: expected <actor_id>.<atespace>.%s, got %q", ActorDNSSuffix, name)
52+
return "", "", fmt.Errorf("invalid actor DNS name: expected <actor_name>.<atespace>.%s, got %q", ActorDNSSuffix, name)
8753
}
88-
if err := ValidateActorID(actorID); err != nil {
89-
return "", "", err
54+
if !IsValidResourceName(actorName) {
55+
return "", "", fmt.Errorf("invalid actor DNS name %q: %q is not a valid actor name", name, actorName)
9056
}
91-
if err := ValidateAtespace(atespace); err != nil {
92-
return "", "", err
57+
if !IsValidResourceName(atespace) {
58+
return "", "", fmt.Errorf("invalid actor DNS name %q: %q is not a valid atespace", name, atespace)
9359
}
94-
return atespace, actorID, nil
60+
return atespace, actorName, nil
9561
}

0 commit comments

Comments
 (0)