-
Notifications
You must be signed in to change notification settings - Fork 5
feat(cli): add kernel status command
#123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d58d21a
551d394
c19a361
44667b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "net/http" | ||
| "os" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/pterm/pterm" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| type statusComponent struct { | ||
| Name string `json:"name"` | ||
| Status string `json:"status"` | ||
| } | ||
|
|
||
| type statusGroup struct { | ||
| Name string `json:"name"` | ||
| Status string `json:"status"` | ||
| Components []statusComponent `json:"components"` | ||
| } | ||
|
|
||
| type statusResponse struct { | ||
| Status string `json:"status"` | ||
| Groups []statusGroup `json:"groups"` | ||
| } | ||
|
|
||
| const defaultBaseURL = "https://api.onkernel.com" | ||
|
|
||
| var statusCmd = &cobra.Command{ | ||
| Use: "status", | ||
| Short: "Check the operational status of Kernel services", | ||
| RunE: runStatus, | ||
| } | ||
|
|
||
| func init() { | ||
| statusCmd.Flags().StringP("output", "o", "", "Output format (json)") | ||
| } | ||
|
|
||
| func getBaseURL() string { | ||
| if u := os.Getenv("KERNEL_BASE_URL"); strings.TrimSpace(u) != "" { | ||
| return strings.TrimRight(u, "/") | ||
| } | ||
| return defaultBaseURL | ||
| } | ||
|
|
||
| func runStatus(cmd *cobra.Command, args []string) error { | ||
| output, _ := cmd.Flags().GetString("output") | ||
|
|
||
| client := &http.Client{Timeout: 10 * time.Second} | ||
| resp, err := client.Get(getBaseURL() + "/status") | ||
| if err != nil { | ||
| pterm.Error.Println("Could not reach Kernel API. Check https://status.kernel.sh for updates.") | ||
| return fmt.Errorf("request failed: %w", err) | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double error output from printing then returning errorMedium Severity
Additional Locations (1) |
||
| defer resp.Body.Close() | ||
|
|
||
| if resp.StatusCode < 200 || resp.StatusCode >= 300 { | ||
| pterm.Error.Println("Could not reach Kernel API. Check https://status.kernel.sh for updates.") | ||
| return fmt.Errorf("status request failed: %s", resp.Status) | ||
| } | ||
|
|
||
| var status statusResponse | ||
| if err := json.NewDecoder(resp.Body).Decode(&status); err != nil { | ||
| return fmt.Errorf("invalid response: %w", err) | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if output == "json" { | ||
| enc := json.NewEncoder(os.Stdout) | ||
| enc.SetIndent("", " ") | ||
| return enc.Encode(status) | ||
| } | ||
|
|
||
| printStatus(status) | ||
| return nil | ||
| } | ||
|
|
||
| // Colors match the dashboard's api-status-indicator.tsx | ||
| var statusDisplay = map[string]struct { | ||
| label string | ||
| rgb pterm.RGB | ||
| }{ | ||
| "operational": {label: "Operational", rgb: pterm.NewRGB(31, 163, 130)}, | ||
| "degraded_performance": {label: "Degraded Performance", rgb: pterm.NewRGB(245, 158, 11)}, | ||
| "partial_outage": {label: "Partial Outage", rgb: pterm.NewRGB(242, 85, 51)}, | ||
| "full_outage": {label: "Major Outage", rgb: pterm.NewRGB(239, 68, 68)}, | ||
| "maintenance": {label: "Maintenance", rgb: pterm.NewRGB(36, 99, 235)}, | ||
| "unknown": {label: "Unknown", rgb: pterm.NewRGB(128, 128, 128)}, | ||
| } | ||
|
|
||
| func getStatusDisplay(status string) (string, pterm.RGB) { | ||
| if d, ok := statusDisplay[status]; ok { | ||
| return d.label, d.rgb | ||
| } | ||
| return "Unknown", pterm.NewRGB(128, 128, 128) | ||
| } | ||
|
|
||
| func coloredDot(rgb pterm.RGB) string { | ||
| return rgb.Sprint("●") | ||
| } | ||
|
|
||
| func printStatus(resp statusResponse) { | ||
| label, rgb := getStatusDisplay(resp.Status) | ||
| header := fmt.Sprintf("Kernel Status: %s", rgb.Sprint(label)) | ||
| pterm.Println() | ||
| pterm.Println(" " + header) | ||
|
|
||
| for _, group := range resp.Groups { | ||
| pterm.Println() | ||
| if len(group.Components) == 0 { | ||
| groupLabel, groupColor := getStatusDisplay(group.Status) | ||
| pterm.Printf(" %s %s %s\n", coloredDot(groupColor), pterm.Bold.Sprint(group.Name), groupLabel) | ||
| } else { | ||
| pterm.Println(" " + pterm.Bold.Sprint(group.Name)) | ||
| for _, comp := range group.Components { | ||
| compLabel, compColor := getStatusDisplay(comp.Status) | ||
| pterm.Printf(" %s %-20s %s\n", coloredDot(compColor), comp.Name, compLabel) | ||
| } | ||
| } | ||
| } | ||
| pterm.Println() | ||
| } | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicated base URL resolution logic across files
Low Severity
The
getBaseURL()function anddefaultBaseURLconstant duplicate the base URL resolution logic already present inline incmd/deploy.go(lines 150–153), which also readsKERNEL_BASE_URLand falls back to"https://api.onkernel.com". The two implementations also differ subtly —getBaseURLtrims trailing slashes whiledeploy.godoes not — creating a risk of inconsistent behavior and requiring updates in multiple places if the default URL changes.Additional Locations (1)
cmd/status.go#L42-L48