Compare commits

...

2 Commits

Author SHA1 Message Date
Paul Payne
393306de12 Instance-namespace utility endpoints. 2025-10-14 21:04:40 +00:00
Paul Payne
8d19fbd549 Fix dashboard token command. 2025-10-14 18:55:43 +00:00
4 changed files with 78 additions and 12 deletions

View File

@@ -2,3 +2,52 @@
- Go 1.21+ - Go 1.21+
- GNU Make (for build automation) - GNU Make (for build automation)
## Patterns
### Instance-scoped Commands
CLI commands that operate on a specific Wild Cloud instance should follow this pattern:
```go
// In cmd/utility.go
var dashboardTokenCmd = &cobra.Command{
Use: "token",
Short: "Get dashboard token",
RunE: func(cmd *cobra.Command, args []string) error {
// 1. Get instance from CLI context
instanceName, err := getInstanceName()
if err != nil {
return err
}
// 2. Call instance-scoped API endpoint with instance in URL
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/utilities/dashboard/token", instanceName))
if err != nil {
return err
}
// 3. Process response
data := resp.GetMap("data")
if data == nil {
return fmt.Errorf("no data in response")
}
token, ok := data["token"].(string)
if !ok {
return fmt.Errorf("no token in response")
}
// 4. Display result
fmt.Println(token)
return nil
},
}
```
#### Key Principles
1. **Get instance from context**: Use `getInstanceName()` to get the current instance from CLI context
2. **Instance in URL path**: Include the instance name in the API endpoint URL path
3. **Stateless API calls**: Don't rely on server-side session state - pass instance explicitly
4. **Handle errors gracefully**: Return clear error messages if instance is not set or API call fails

View File

@@ -26,7 +26,7 @@ var operationGetCmd = &cobra.Command{
return err return err
} }
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/operations/%s?instance=%s", args[0], inst)) resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/operations/%s", inst, args[0]))
if err != nil { if err != nil {
return err return err
} }
@@ -43,7 +43,12 @@ var operationListCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "List operations", Short: "List operations",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
resp, err := apiClient.Get("/api/v1/operations") inst, err := getInstanceName()
if err != nil {
return err
}
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/operations", inst))
if err != nil { if err != nil {
return err return err
} }
@@ -90,7 +95,7 @@ func streamOperationOutput(opID string) error {
} }
// Connect to SSE stream // Connect to SSE stream
url := fmt.Sprintf("%s/api/v1/operations/%s/stream?instance=%s", baseURL, opID, inst) url := fmt.Sprintf("%s/api/v1/instances/%s/operations/%s/stream", baseURL, inst, opID)
client := sse.NewClient(url) client := sse.NewClient(url)
events := make(chan *sse.Event) events := make(chan *sse.Event)
@@ -105,7 +110,7 @@ func streamOperationOutput(opID string) error {
ticker := time.NewTicker(500 * time.Millisecond) ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop() defer ticker.Stop()
for range ticker.C { for range ticker.C {
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/operations/%s?instance=%s", opID, inst)) resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/operations/%s", inst, opID))
if err == nil { if err == nil {
status := resp.GetString("status") status := resp.GetString("status")
if status == "completed" || status == "failed" { if status == "completed" || status == "failed" {

View File

@@ -38,7 +38,11 @@ var dashboardTokenCmd = &cobra.Command{
Use: "token", Use: "token",
Short: "Get dashboard token", Short: "Get dashboard token",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
resp, err := apiClient.Get("/api/v1/utilities/dashboard/token") instanceName, err := getInstanceName()
if err != nil {
return err
}
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/utilities/dashboard/token", instanceName))
if err != nil { if err != nil {
return err return err
} }
@@ -68,7 +72,12 @@ var nodeIPCmd = &cobra.Command{
Use: "node-ip", Use: "node-ip",
Short: "Get control plane IP", Short: "Get control plane IP",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
resp, err := apiClient.Get("/api/v1/utilities/controlplane/ip") inst, err := getInstanceName()
if err != nil {
return err
}
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/utilities/controlplane/ip", inst))
if err != nil { if err != nil {
return err return err
} }

View File

@@ -24,7 +24,9 @@ var versionCmd = &cobra.Command{
// If connected to daemon, show cluster versions // If connected to daemon, show cluster versions
if apiClient != nil { if apiClient != nil {
resp, err := apiClient.Get("/api/v1/utilities/version") inst, err := getInstanceName()
if err == nil {
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/utilities/version", inst))
if err == nil { if err == nil {
if k8s, ok := resp.Data["kubernetes"].(string); ok { if k8s, ok := resp.Data["kubernetes"].(string); ok {
fmt.Printf("Kubernetes: %s\n", k8s) fmt.Printf("Kubernetes: %s\n", k8s)
@@ -34,5 +36,6 @@ var versionCmd = &cobra.Command{
} }
} }
} }
}
}, },
} }