Skip to content

Commit cbde8b1

Browse files
juli4nHavenXia
authored andcommitted
Update kubectl-ate to use actor name instead of id.
This is a follow up of the API refactor. Now the CLI shows some common fields (e.g. `create_timestamp`) but also shows actor *name* instead of *id*.
1 parent 354ca16 commit cbde8b1

15 files changed

Lines changed: 364 additions & 187 deletions

File tree

cmd/kubectl-ate/README.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,29 +83,29 @@ kubectl ate get actors -a <atespace>
8383
# List actors across all atespaces
8484
kubectl ate get actors -A
8585

86-
# Get a specific actor by ID and output as raw YAML
87-
kubectl ate get actor <actor-id> --atespace <atespace> -o yaml
86+
# Get a specific actor by name and output as raw YAML
87+
kubectl ate get actor <actor-name> --atespace <atespace> -o yaml
8888

8989
# List all physical workers and see which actors are assigned to them
9090
kubectl ate get workers
9191
```
9292

93-
> **Note:** `get actors` requires either `--atespace <name>` / `-a <name>` (one atespace) or `-A`/`--all-atespaces` (all atespaces) — there is no default atespace. Getting a single actor always requires `--atespace`/`-a`, since an actor is addressed by `(atespace, id)`. `-a` (lower-case) scopes to one atespace; `-A` (upper-case) spans all.
93+
> **Note:** `get actors` requires either `--atespace <name>` / `-a <name>` (one atespace) or `-A`/`--all-atespaces` (all atespaces) — there is no default atespace. Getting a single actor always requires `--atespace`/`-a`, since an actor is addressed by `(atespace, name)`. `-a` (lower-case) scopes to one atespace; `-A` (upper-case) spans all.
9494
9595
> **Note:** Actors and workers are not Kubernetes CRDs — they live in the Substrate control plane (valkey/redis), not `etcd`. `kubectl get actor` and `kubectl get worker` will not return anything; only `kubectl ate get …` queries the control plane. `kubectl get actortemplate` and `kubectl get workerpool` *do* work, because those are CRDs.
9696
9797
#### `kubectl ate get actor` output columns
9898

9999
| Column | Meaning |
100100
|---|---|
101-
| `ATESPACE` | The atespace the actor belongs to. Part of the actor's identity; folded into the storage key as `actor:<atespace>:<id>`. |
102-
| `TEMPLATE NS` | The namespace of the `ActorTemplate` the actor was created from (distinct from `ATESPACE`). |
103-
| `TEMPLATE` | The `ActorTemplate` name. |
104-
| `ID` | Actor ID. User-provided for application actors; UUID for the golden actor that each template materialises during `ResumeGoldenActor`. |
101+
| `ATESPACE` | The atespace the actor belongs to. Part of the actor's identity; folded into the storage key as `actor:<atespace>:<name>`. |
102+
| `NAME` | The actor's name. User-provided for application actors; UUID for the golden actor that each template materialises during `ResumeGoldenActor`. |
103+
| `TEMPLATE` | The `ActorTemplate` the actor was created from, as `<namespace>/<name>` (the template namespace is distinct from `ATESPACE`). |
105104
| `STATUS` | One of `STATUS_RESUMING`, `STATUS_RUNNING`, `STATUS_SUSPENDING`, `STATUS_SUSPENDED`. |
106105
| `ATEOM POD` | The worker pod (namespace/name) currently hosting the actor. Empty while suspended. |
107106
| `ATEOM IP` | The pod IP of that worker. Empty while suspended. |
108107
| `VERSION` | Monotonic integer that increments on every state transition (resume / suspend / checkpoint). Useful for distinguishing snapshots. |
108+
| `AGE` | Time elapsed since the actor was created. |
109109

110110
#### `kubectl ate get worker` output columns
111111

@@ -115,7 +115,7 @@ kubectl ate get workers
115115
| `POOL` | The `WorkerPool` name. |
116116
| `POD` | The worker pod name. |
117117
| `STATUS` | `FREE` (idle, ready to receive an actor) or `ASSIGNED` (currently hosting an actor). |
118-
| `ASSIGNED ACTOR` | If `STATUS=ASSIGNED`, the actor reference `<namespace>/<template>/<actor-id>`. |
118+
| `ASSIGNED ACTOR` | If `STATUS=ASSIGNED`, the actor reference `<namespace>/<template>/<actor-name>`. |
119119

120120
### Atespaces
121121

@@ -137,9 +137,16 @@ kubectl ate delete atespace <atespace>
137137

138138
> **Note:** `create actor … -a <atespace>` requires the atespace to already exist, otherwise it fails with `FailedPrecondition`. `delete atespace` only removes an **empty** atespace; delete its actors first (cascade delete is not yet supported).
139139
140+
#### `kubectl ate get atespace` output columns
141+
142+
| Column | Meaning |
143+
|---|---|
144+
| `NAME` | The atespace name. Globally unique — atespaces are global-scoped. |
145+
| `AGE` | Time elapsed since the atespace was created. |
146+
140147
### Actor Lifecycle
141148
Manage the execution state of your workloads.
142-
*(Note: Actors are identified by a user-provided ID, which must be a valid DNS-1123 label)*
149+
*(Note: Actors are identified by a user-provided name, which must be a valid DNS-1123 label)*
143150

144151
```bash
145152
# Create a new actor deriving from a specific ActorTemplate.
@@ -159,7 +166,7 @@ kubectl ate delete actor my-actor -a <atespace>
159166

160167
### Logs
161168

162-
`kubectl ate logs` requires a resource-type subcommand; running `kubectl ate logs <id>` on its own prints help. The only supported resource type is `actors`:
169+
`kubectl ate logs` requires a resource-type subcommand; running `kubectl ate logs <actor-name>` on its own prints help. The only supported resource type is `actors`:
163170

164171
```bash
165172
# Stream logs for an actor (follows by default; aggregated across worker

cmd/kubectl-ate/internal/cmd/create_actor.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var templateFlag string
2828
var atespaceFlag string
2929

3030
var createActorCmd = &cobra.Command{
31-
Use: "actor [actor-id]",
31+
Use: "actor <actor-name>",
3232
Short: "Create an actor",
3333
Args: cobra.ExactArgs(1),
3434
RunE: func(cmd *cobra.Command, args []string) error {
@@ -39,7 +39,7 @@ var createActorCmd = &cobra.Command{
3939
}
4040
defer apiClient.Close()
4141

42-
actorID := args[0]
42+
actorName := args[0]
4343
parts := strings.Split(templateFlag, "/")
4444
if len(parts) != 2 {
4545
return fmt.Errorf("malformed --template: %s (expected <namespace>/<name>)", templateFlag)
@@ -49,7 +49,7 @@ var createActorCmd = &cobra.Command{
4949
Actor: &ateapipb.Actor{
5050
Metadata: &ateapipb.ResourceMetadata{
5151
Atespace: atespaceFlag,
52-
Name: actorID,
52+
Name: actorName,
5353
},
5454
ActorTemplateNamespace: parts[0],
5555
ActorTemplateName: parts[1],

cmd/kubectl-ate/internal/cmd/delete_actor.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
var deleteAtespaceFlag string
2626

2727
var deleteActorCmd = &cobra.Command{
28-
Use: "actor [actor-id]",
28+
Use: "actor <actor-name>",
2929
Short: "Delete an actor",
3030
Args: cobra.ExactArgs(1),
3131
RunE: func(cmd *cobra.Command, args []string) error {
@@ -36,15 +36,15 @@ var deleteActorCmd = &cobra.Command{
3636
}
3737
defer c.Close()
3838

39-
id := args[0]
39+
name := args[0]
4040
_, err = c.ControlClient.DeleteActor(ctx, &ateapipb.DeleteActorRequest{
41-
Actor: &ateapipb.ObjectRef{Atespace: deleteAtespaceFlag, Name: id},
41+
Actor: &ateapipb.ObjectRef{Atespace: deleteAtespaceFlag, Name: name},
4242
})
4343
if err != nil {
4444
return err
4545
}
4646

47-
fmt.Printf("actor %q deleted\n", id)
47+
fmt.Printf("actor %q deleted\n", name)
4848
return nil
4949
},
5050
}

cmd/kubectl-ate/internal/cmd/get_actors.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ var (
2929
)
3030

3131
var getActorsCmd = &cobra.Command{
32-
Use: "actors [actor-id]",
32+
Use: "actors <actor-name>",
3333
Aliases: []string{"actor"},
3434
Short: "List all actors or get a specific actor",
3535
RunE: func(cmd *cobra.Command, args []string) error {
@@ -44,7 +44,7 @@ var getActorsCmd = &cobra.Command{
4444

4545
// 2. Handle Get Single Actor
4646
if len(args) > 0 {
47-
// A single actor is addressed by (atespace, id), so the atespace is
47+
// A single actor is addressed by (atespace, name), so the atespace is
4848
// mandatory and "all atespaces" is meaningless here.
4949
if getActorsAllAtespaces {
5050
return fmt.Errorf("-A/--all-atespaces cannot be used when getting a specific actor; pass --atespace")

cmd/kubectl-ate/internal/cmd/logs_actors.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ var followLogs bool
4242
var logsAtespaceFlag string
4343

4444
var logsActorsCmd = &cobra.Command{
45-
Use: "actors <actor-id>",
45+
Use: "actors <actor-name>",
4646
Aliases: []string{"actor"},
4747
Short: "Stream logs for a specific actor",
4848
Args: cobra.ExactArgs(1),
@@ -90,7 +90,7 @@ type LogsActorRunner struct {
9090
}
9191

9292
// Run executes the logs command.
93-
func (r *LogsActorRunner) Run(ctx context.Context, actorID string) error {
93+
func (r *LogsActorRunner) Run(ctx context.Context, actorName string) error {
9494
if r.pollInterval <= 0 {
9595
r.pollInterval = 2 * time.Second
9696
}
@@ -103,13 +103,13 @@ func (r *LogsActorRunner) Run(ctx context.Context, actorID string) error {
103103

104104
defer r.apiClient.Close()
105105
if r.follow {
106-
return r.runFollow(ctx, actorID)
106+
return r.runFollow(ctx, actorName)
107107
}
108-
return r.runOneShot(ctx, actorID)
108+
return r.runOneShot(ctx, actorName)
109109
}
110110

111-
func (r *LogsActorRunner) runOneShot(ctx context.Context, actorID string) error {
112-
actor, err := r.apiClient.GetActor(ctx, &ateapipb.GetActorRequest{Actor: &ateapipb.ObjectRef{Atespace: r.atespace, Name: actorID}})
111+
func (r *LogsActorRunner) runOneShot(ctx context.Context, actorName string) error {
112+
actor, err := r.apiClient.GetActor(ctx, &ateapipb.GetActorRequest{Actor: &ateapipb.ObjectRef{Atespace: r.atespace, Name: actorName}})
113113
if err != nil {
114114
return fmt.Errorf("failed to get actor: %w", err)
115115
}
@@ -118,7 +118,7 @@ func (r *LogsActorRunner) runOneShot(ctx context.Context, actorID string) error
118118
namespace := actor.GetAteomPodNamespace()
119119

120120
if podName == "" || namespace == "" || actor.GetStatus() != ateapipb.Actor_STATUS_RUNNING {
121-
return fmt.Errorf("actor %s is not currently running on any worker pod", actorID)
121+
return fmt.Errorf("actor %s is not currently running on any worker pod", actorName)
122122
}
123123

124124
opts := &corev1.PodLogOptions{
@@ -136,15 +136,15 @@ func (r *LogsActorRunner) runOneShot(ctx context.Context, actorID string) error
136136
scanner.Buffer(buf, 1024*1024) // Support up to 1MB lines
137137
for scanner.Scan() {
138138
line := scanner.Text()
139-
filterAndDisplayLogLine(line, actorID, r.stdout)
139+
filterAndDisplayLogLine(line, actorName, r.stdout)
140140
}
141141
if err := scanner.Err(); err != nil {
142142
return fmt.Errorf("error reading log stream: %w", err)
143143
}
144144
return nil
145145
}
146146

147-
func (r *LogsActorRunner) runFollow(ctx context.Context, actorID string) error {
147+
func (r *LogsActorRunner) runFollow(ctx context.Context, actorName string) error {
148148
var lastWorkerPod string
149149
var lastSeenTime time.Time
150150

@@ -155,10 +155,10 @@ func (r *LogsActorRunner) runFollow(ctx context.Context, actorID string) error {
155155
default:
156156
}
157157

158-
actor, err := r.apiClient.GetActor(ctx, &ateapipb.GetActorRequest{Actor: &ateapipb.ObjectRef{Atespace: r.atespace, Name: actorID}})
158+
actor, err := r.apiClient.GetActor(ctx, &ateapipb.GetActorRequest{Actor: &ateapipb.ObjectRef{Atespace: r.atespace, Name: actorName}})
159159
if err != nil {
160160
if status.Code(err) == codes.NotFound {
161-
return fmt.Errorf("actor %s not found: %w", actorID, err)
161+
return fmt.Errorf("actor %s not found: %w", actorName, err)
162162
}
163163
select {
164164
case <-ctx.Done():
@@ -206,14 +206,14 @@ func (r *LogsActorRunner) runFollow(ctx context.Context, actorID string) error {
206206
}
207207

208208
var wg sync.WaitGroup
209-
r.startMigrationMonitor(streamCtx, streamCancel, &wg, actorID, podName)
209+
r.startMigrationMonitor(streamCtx, streamCancel, &wg, actorName, podName)
210210

211211
scanner := bufio.NewScanner(stream)
212212
buf := make([]byte, 0, 64*1024)
213213
scanner.Buffer(buf, 1024*1024) // Support up to 1MB lines
214214
for scanner.Scan() {
215215
line := scanner.Text()
216-
logTime, _ := filterAndDisplayLogLine(line, actorID, r.stdout)
216+
logTime, _ := filterAndDisplayLogLine(line, actorName, r.stdout)
217217
if !logTime.IsZero() {
218218
lastSeenTime = logTime
219219
}
@@ -249,7 +249,7 @@ func (r *LogsActorRunner) startMigrationMonitor(
249249
ctx context.Context,
250250
cancel context.CancelFunc,
251251
wg *sync.WaitGroup,
252-
actorID string,
252+
actorName string,
253253
currentPod string,
254254
) {
255255
wg.Add(1)
@@ -262,7 +262,7 @@ func (r *LogsActorRunner) startMigrationMonitor(
262262
case <-ctx.Done():
263263
return
264264
case <-ticker.C:
265-
resp, err := r.apiClient.GetActor(ctx, &ateapipb.GetActorRequest{Actor: &ateapipb.ObjectRef{Atespace: r.atespace, Name: actorID}})
265+
resp, err := r.apiClient.GetActor(ctx, &ateapipb.GetActorRequest{Actor: &ateapipb.ObjectRef{Atespace: r.atespace, Name: actorName}})
266266
if err == nil {
267267
act := resp
268268
if act.GetStatus() != ateapipb.Actor_STATUS_RUNNING || act.GetAteomPodName() != currentPod {
@@ -278,7 +278,7 @@ func (r *LogsActorRunner) startMigrationMonitor(
278278

279279
func runLogsActor(cmd *cobra.Command, args []string) error {
280280
ctx := cmd.Context()
281-
actorID := args[0]
281+
actorName := args[0]
282282

283283
apiClient, err := ateclient.NewClient(ctx, kubeconfig, k8sContext, endpoint, traceEnabled)
284284
if err != nil {
@@ -303,10 +303,10 @@ func runLogsActor(cmd *cobra.Command, args []string) error {
303303
tickerInterval: 2 * time.Second,
304304
}
305305

306-
return runner.Run(ctx, actorID)
306+
return runner.Run(ctx, actorName)
307307
}
308308

309-
func filterAndDisplayLogLine(line, targetActorID string, w io.Writer) (time.Time, bool) {
309+
func filterAndDisplayLogLine(line, targetActorName string, w io.Writer) (time.Time, bool) {
310310
var m map[string]any
311311
dec := json.NewDecoder(strings.NewReader(line))
312312
dec.UseNumber()
@@ -323,19 +323,19 @@ func filterAndDisplayLogLine(line, targetActorID string, w io.Writer) (time.Time
323323
}
324324
}
325325

326-
var actorID string
326+
var actorName string
327327
for _, labelKey := range []string{"logging.googleapis.com/labels", "labels"} {
328328
if labelsAny, ok := m[labelKey]; ok {
329329
if labels, ok := labelsAny.(map[string]any); ok {
330-
if id, ok := labels["ate.dev/actor_id"].(string); ok && id != "" {
331-
actorID = id
330+
if name, ok := labels["ate.dev/actor_name"].(string); ok && name != "" {
331+
actorName = name
332332
break
333333
}
334334
}
335335
}
336336
}
337337

338-
matched := (actorID != "" && actorID == targetActorID)
338+
matched := (actorName != "" && actorName == targetActorName)
339339

340340
if !matched {
341341
return logTime, false

0 commit comments

Comments
 (0)