feat: accept Vault identity JWTs for writes, add set-environment route
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

This commit is contained in:
2026-10-23 21:35:57 +11:00
parent 835e207c9b
commit 8c1bfda4c5
19 changed files with 724 additions and 38 deletions
+2 -1
View File
@@ -37,6 +37,7 @@ Usage:
encapi-cli node list
encapi-cli node get <certname>
encapi-cli node set <certname> --role <r> --env <e> [--param k=v ...]
encapi-cli node set-env <certname> <environment>
encapi-cli node delete <certname>
encapi-cli role list
encapi-cli role get <name>
@@ -55,7 +56,7 @@ Params:
Environment:
ENCAPI_URL encapi base URL (default http://localhost:8000)
ENCAPI_WRITE_TOKEN bearer token, required for writes
ENCAPI_WRITE_TOKEN bearer token (static token or Vault identity JWT), required for writes
`
// Run executes the CLI and returns a process exit code.
+17 -1
View File
@@ -115,7 +115,7 @@ func run(t *testing.T, env Env, args ...string) (int, string, string) {
func serverEnv(t *testing.T) (Env, *memStore) {
t.Helper()
store := newMem()
srv := server.New(store, distro.Noop{}, "tok")
srv := server.New(store, distro.Noop{}, server.Auth{WriteToken: "tok"})
ts := httptest.NewServer(srv.Router())
t.Cleanup(ts.Close)
return Env{URL: ts.URL, Token: "tok"}, store
@@ -158,6 +158,22 @@ func TestNodeSetAndClassify(t *testing.T) {
}
}
func TestNodeSetEnvKeepsRole(t *testing.T) {
env, store := serverEnv(t)
if code, _, errb := run(t, env, "node", "set", "web1.example", "--role", "roles::base", "--env", "production"); code != 0 {
t.Fatalf("node set exit %d: %s", code, errb)
}
if code, _, errb := run(t, env, "node", "set-env", "web1.example", "testing"); code != 0 {
t.Fatalf("set-env exit %d: %s", code, errb)
}
if n := store.nodes["web1.example"]; n.Environment != "testing" || n.Role != "roles::base" {
t.Errorf("node = %+v", n)
}
if code, _, _ := run(t, env, "node", "set-env", "ghost", "testing"); code == 0 {
t.Error("set-env on unknown node should fail")
}
}
func TestNodeSetRequiresFlags(t *testing.T) {
env, _ := serverEnv(t)
code, _, _ := run(t, env, "node", "set", "h")
+12 -1
View File
@@ -12,7 +12,7 @@ import (
func nodeCmd(ctx context.Context, c *client.Client, args []string, stdout, stderr io.Writer) int {
if len(args) < 1 {
fmt.Fprintln(stderr, "usage: encapi-cli node <list|get|set|delete> ...")
fmt.Fprintln(stderr, "usage: encapi-cli node <list|get|set|set-env|delete> ...")
return 2
}
switch args[0] {
@@ -36,6 +36,17 @@ func nodeCmd(ctx context.Context, c *client.Client, args []string, stdout, stder
return 0
case "set":
return nodeSet(ctx, c, args[1:], stdout, stderr)
case "set-env":
if len(args) != 3 {
fmt.Fprintln(stderr, "usage: encapi-cli node set-env <certname> <environment>")
return 2
}
n, err := c.SetNodeEnvironment(ctx, args[1], args[2])
if err != nil {
return fail(stderr, err)
}
printYAML(stdout, n)
return 0
case "delete":
if len(args) != 2 {
fmt.Fprintln(stderr, "usage: encapi-cli node delete <certname>")