From 79823b87be238e6b68de36115cbbf708452a9b8c Mon Sep 17 00:00:00 2001 From: DerBurri Date: Wed, 23 Sep 2026 10:14:36 +0200 Subject: [PATCH 1/3] feat: add status command that displays readiness information Signed-off-by: DerBurri --- cli/cmd/codesphere/status_codesphere.go | 76 +++++++++ cli/cmd/root.go | 3 +- cli/cmd/status.go | 30 ++++ docs/README.md | 1 + internal/codesphere/status/logo.txt | 12 ++ internal/codesphere/status/status.go | 205 ++++++++++++++++++++++++ 6 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 cli/cmd/codesphere/status_codesphere.go create mode 100644 cli/cmd/status.go create mode 100644 internal/codesphere/status/logo.txt create mode 100644 internal/codesphere/status/status.go diff --git a/cli/cmd/codesphere/status_codesphere.go b/cli/cmd/codesphere/status_codesphere.go new file mode 100644 index 000000000..db9d645ef --- /dev/null +++ b/cli/cmd/codesphere/status_codesphere.go @@ -0,0 +1,76 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package codesphere + +import ( + "fmt" + + csio "github.com/codesphere-cloud/cs-go/pkg/io" + "github.com/codesphere-cloud/oms/cli/cmd/util" + "github.com/codesphere-cloud/oms/internal/codesphere" + "github.com/codesphere-cloud/oms/internal/codesphere/status" + "github.com/spf13/cobra" +) + +// StatusCodesphereOpts configures the status report of a Codesphere installation. +type StatusCodesphereOpts = status.Options + +// StatusCodesphereCmd represents the status codesphere command. +type StatusCodesphereCmd struct { + cmd *cobra.Command + Opts *StatusCodesphereOpts +} + +// RunE prints the status report and fails the command if the installation is not ready. +func (c *StatusCodesphereCmd) RunE(cmd *cobra.Command, _ []string) error { + client, err := codesphere.NewClient(c.Opts.BaseURL, c.Opts.Token) + if err != nil { + return fmt.Errorf("failed to create Codesphere client: %w", err) + } + + c.Opts.Client = client + + report := status.Fetch(cmd.Context(), c.Opts) + status.Print(cmd.OutOrStdout(), c.Opts.BaseURL, report) + + if !report.Ready { + return fmt.Errorf("codesphere installation is not ready") + } + + return nil +} + +// AddStatusCmd adds the status codesphere command to the given parent command. +func AddStatusCmd(parent *cobra.Command, _ *util.GlobalOptions) { + c := StatusCodesphereCmd{ + cmd: &cobra.Command{ + Use: "codesphere", + Short: "Check the status of a Codesphere installation", + Long: csio.Long(`Check whether a Codesphere installation is reachable and ready to use, + by querying the Codesphere API.`), + Example: util.FormatExamples("status codesphere", []csio.Example{ + { + Cmd: "--baseurl https://codesphere.example.com/api --token YOUR_TOKEN", + Desc: "Check the status of a Codesphere installation", + }, + { + Cmd: "--baseurl https://codesphere.example.com/api --token YOUR_TOKEN --wait", + Desc: "Block and retry until the Codesphere installation is ready", + }, + }), + }, + Opts: &StatusCodesphereOpts{}, + } + c.cmd.Flags().StringVar(&c.Opts.BaseURL, "baseurl", "", "Base URL of the Codesphere API") + c.cmd.Flags().StringVar(&c.Opts.Token, "token", "", "API token for authentication") + c.cmd.Flags().BoolVar(&c.Opts.Wait, "wait", false, "Block and retry until the installation is ready") + c.cmd.Flags().DurationVar(&c.Opts.Timeout, "timeout", status.DefaultTimeout, "Timeout when waiting for the installation to become ready") + + util.MarkFlagRequired(c.cmd, "baseurl") + util.MarkFlagRequired(c.cmd, "token") + + c.cmd.RunE = c.RunE + + util.AddCmd(parent, c.cmd) +} diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 88b97e9b0..59b148d75 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -75,8 +75,9 @@ func GetRootCmd() *cobra.Command { apikey.AddRegisterCmd(rootCmd, opts) AddRevokeCmd(rootCmd, opts) - // Smoke test commands + // Test commands AddSmoketestCmd(rootCmd, opts) + AddStatusCmd(rootCmd, opts) // Resource creation commands AddCreateCmd(rootCmd, opts) diff --git a/cli/cmd/status.go b/cli/cmd/status.go new file mode 100644 index 000000000..878760db0 --- /dev/null +++ b/cli/cmd/status.go @@ -0,0 +1,30 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "github.com/codesphere-cloud/cs-go/pkg/io" + "github.com/codesphere-cloud/oms/cli/cmd/codesphere" + "github.com/codesphere-cloud/oms/cli/cmd/util" + "github.com/spf13/cobra" +) + +// StatusCmd represents the status command +type StatusCmd struct { + cmd *cobra.Command +} + +// AddStatusCmd adds the status command and its subcommands to the root command. +func AddStatusCmd(rootCmd *cobra.Command, opts *util.GlobalOptions) { + status := StatusCmd{ + cmd: &cobra.Command{ + Use: "status", + Short: "Check the status of Codesphere components", + Long: io.Long(`Check whether Codesphere installations or components are up and ready.`), + }, + } + util.AddCmd(rootCmd, status.cmd) + + codesphere.AddStatusCmd(status.cmd, opts) +} diff --git a/docs/README.md b/docs/README.md index b83edc490..af234d792 100644 --- a/docs/README.md +++ b/docs/README.md @@ -31,6 +31,7 @@ like downloading new versions. * [oms register](oms_register.md) - Register a new API key * [oms revoke](oms_revoke.md) - Revoke resources available through OMS * [oms smoketest](oms_smoketest.md) - Run smoke tests for Codesphere components +* [oms status](oms_status.md) - Check the status of Codesphere components * [oms template](oms_template.md) - Render OMS configuration templates * [oms update](oms_update.md) - Update OMS related resources * [oms version](oms_version.md) - Print version diff --git a/internal/codesphere/status/logo.txt b/internal/codesphere/status/logo.txt new file mode 100644 index 000000000..8cd22616a --- /dev/null +++ b/internal/codesphere/status/logo.txt @@ -0,0 +1,12 @@ + ▄▄▄████▄▄▄ + ▄████▀▀▀▀▀▀████▄ + ▄███▀ ▄▄ ▀███▄ + ▄██▀ ▄████████▄ ▀██▄ + ██▀ ███▀ ▀███ ▀██ +███ ▄██ ██▄ ███ +███ ▀██ ██▀ ███ + ██▄ ███▄ ▄███ ▄██ + ▀██▄ ▀████████▀ ▄██▀ + ▀███▄ ▀▀ ▄███▀ + ▀████▄▄▄▄▄▄████▀ + ▀▀▀████▀▀▀ diff --git a/internal/codesphere/status/status.go b/internal/codesphere/status/status.go new file mode 100644 index 000000000..d19fe1b2d --- /dev/null +++ b/internal/codesphere/status/status.go @@ -0,0 +1,205 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Package status reports whether a Codesphere installation is reachable and +// ready. It backs both the status command and the status test playlist step. +package status + +import ( + "context" + _ "embed" + "fmt" + "io" + "net/url" + "regexp" + "strings" + "time" + + "github.com/codesphere-cloud/oms/internal/codesphere" +) + +const ( + // DefaultTimeout bounds how long Fetch waits for the installation to + // become ready when Options.Wait is set. + DefaultTimeout = 5 * time.Minute + // pollInterval is how long Fetch waits between two readiness checks. + pollInterval = 5 * time.Second + + ansiReset = "\x1b[0m" + ansiBold = "\x1b[1m" + ansiCyan = "\x1b[36m" + ansiGreen = "\x1b[32m" + ansiRed = "\x1b[31m" +) + +// logoArt is the Codesphere mark printed next to the status report, +// neofetch-style. It lives in its own file so it can be edited as artwork: +// pasting block characters straight into a Go string literal breaks the build, +// and gofmt has opinions about what it finds in one. +// +// The file carries its own SGR escapes, so the mark can be recolored without +// touching this package. Every colored run is closed with a reset, which is +// why Print measures the lines with visibleWidth rather than len. +// +// The brand palette is truecolor rather than a palette index, so the ring +// keeps its hue regardless of how a terminal theme maps the 16 base colors: +// the outer ring is #7a4eeb (purple) and the inner ring #2ed9d0 (turquoise). +// +//go:embed logo.txt +var logoArt string + +// ansiPattern matches the SGR escape sequences the artwork colors itself with. +var ansiPattern = regexp.MustCompile("\x1b\\[[0-9;]*m") + +// logo is the mark split into the lines Print renders it by. +var logo = logoLines(logoArt) + +// logoLines splits the embedded artwork, tolerating CRLF line endings and the +// trailing newline every sane editor leaves behind. +func logoLines(art string) []string { + lines := strings.Split(strings.Trim(art, "\n"), "\n") + for i, l := range lines { + lines[i] = strings.TrimRight(l, "\r") + } + + return lines +} + +// visibleWidth is the number of cells a line occupies on screen, ignoring the +// SGR escapes that take up bytes but no space. +func visibleWidth(s string) int { + return len([]rune(ansiPattern.ReplaceAllString(s, ""))) +} + +// Options configures the status report of a Codesphere installation. +type Options struct { + BaseURL string + Token string + Wait bool + Timeout time.Duration + Client codesphere.Client +} + +// Report is the outcome of a readiness check. +type Report struct { + Ready bool + Latency time.Duration + Teams int + Plans int + Attempts int + Err error +} + +// Fetch pings the Codesphere API with a cheap, side-effect-free call +// (ListWorkspacePlans) to determine readiness. With Wait set, it retries on +// failure until the installation becomes ready or opts.Timeout elapses. +func Fetch(ctx context.Context, opts *Options) *Report { + timeout := opts.Timeout + if timeout <= 0 { + timeout = DefaultTimeout + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + report := &Report{} + for { + report.Attempts++ + + start := time.Now() + plans, err := opts.Client.ListWorkspacePlans() + report.Latency = time.Since(start) + + if err == nil { + report.Ready = true + + report.Plans = len(plans) + if teams, terr := opts.Client.ListTeams(""); terr == nil { + report.Teams = len(teams) + } + + return report + } + + report.Err = err + + if !opts.Wait { + return report + } + + select { + case <-ctx.Done(): + return report + case <-time.After(pollInterval): + } + } +} + +// Print renders a neofetch-style report: a small ASCII logo alongside +// key/value status lines. +func Print(w io.Writer, baseURL string, r *Report) { + host := baseURL + if u, err := url.Parse(baseURL); err == nil && u.Host != "" { + host = u.Host + } + + statusColor, statusText := ansiGreen, "Ready" + if !r.Ready { + statusColor, statusText = ansiRed, "Not Ready" + } + + header := fmt.Sprintf("%s%scodesphere%s@%s", ansiBold, ansiCyan, ansiReset, host) + rule := strings.Repeat("-", len("codesphere@")+len(host)) + + lines := []string{ + header, + rule, + fmt.Sprintf("%sStatus%s: %s%s%s", ansiBold, ansiReset, statusColor, statusText, ansiReset), + fmt.Sprintf("%sLatency%s: %s", ansiBold, ansiReset, r.Latency.Round(time.Millisecond)), + } + if r.Ready { + lines = append(lines, + fmt.Sprintf("%sTeams%s: %d", ansiBold, ansiReset, r.Teams), + fmt.Sprintf("%sPlans%s: %d", ansiBold, ansiReset, r.Plans), + ) + } else { + lines = append(lines, fmt.Sprintf("%sError%s: %s", ansiBold, ansiReset, r.Err)) + } + + if r.Attempts > 1 { + lines = append(lines, fmt.Sprintf("%sAttempts%s: %d", ansiBold, ansiReset, r.Attempts)) + } + + rows := len(logo) + if len(lines) > rows { + rows = len(lines) + } + + // Pad the logo to a fixed width so the status lines form a straight column. + logoWidth := 0 + for _, l := range logo { + if n := visibleWidth(l); n > logoWidth { + logoWidth = n + } + } + + _, _ = fmt.Fprintln(w) + + for i := 0; i < rows; i++ { + logoLine := "" + if i < len(logo) { + logoLine = logo[i] + } + + logoLine += strings.Repeat(" ", logoWidth-visibleWidth(logoLine)) + + statLine := "" + if i < len(lines) { + statLine = lines[i] + } + + _, _ = fmt.Fprintf(w, " %s %s\n", logoLine, statLine) + } + + _, _ = fmt.Fprintln(w) +} From cbcb12038d77685af51cb390a19dc32876feb3e7 Mon Sep 17 00:00:00 2001 From: DerBurri <7892993+DerBurri@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:17:57 +0000 Subject: [PATCH 2/3] chore(docs): Auto-update docs and licenses Signed-off-by: DerBurri <7892993+DerBurri@users.noreply.github.com> --- docs/oms.md | 1 + docs/oms_status.md | 19 ++++++++++++++++++ docs/oms_status_codesphere.md | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 docs/oms_status.md create mode 100644 docs/oms_status_codesphere.md diff --git a/docs/oms.md b/docs/oms.md index b83edc490..af234d792 100644 --- a/docs/oms.md +++ b/docs/oms.md @@ -31,6 +31,7 @@ like downloading new versions. * [oms register](oms_register.md) - Register a new API key * [oms revoke](oms_revoke.md) - Revoke resources available through OMS * [oms smoketest](oms_smoketest.md) - Run smoke tests for Codesphere components +* [oms status](oms_status.md) - Check the status of Codesphere components * [oms template](oms_template.md) - Render OMS configuration templates * [oms update](oms_update.md) - Update OMS related resources * [oms version](oms_version.md) - Print version diff --git a/docs/oms_status.md b/docs/oms_status.md new file mode 100644 index 000000000..6183fb6ce --- /dev/null +++ b/docs/oms_status.md @@ -0,0 +1,19 @@ +## oms status + +Check the status of Codesphere components + +### Synopsis + +Check whether Codesphere installations or components are up and ready. + +### Options + +``` + -h, --help help for status +``` + +### SEE ALSO + +* [oms](oms.md) - Codesphere Operations Management System (OMS) +* [oms status codesphere](oms_status_codesphere.md) - Check the status of a Codesphere installation + diff --git a/docs/oms_status_codesphere.md b/docs/oms_status_codesphere.md new file mode 100644 index 000000000..b21f9c04f --- /dev/null +++ b/docs/oms_status_codesphere.md @@ -0,0 +1,38 @@ +## oms status codesphere + +Check the status of a Codesphere installation + +### Synopsis + +Check whether a Codesphere installation is reachable and ready to use, +by querying the Codesphere API. + +``` +oms status codesphere [flags] +``` + +### Examples + +``` +# Check the status of a Codesphere installation +$ oms status codesphere --baseurl https://codesphere.example.com/api --token YOUR_TOKEN + +# Block and retry until the Codesphere installation is ready +$ oms status codesphere --baseurl https://codesphere.example.com/api --token YOUR_TOKEN --wait + +``` + +### Options + +``` + --baseurl string Base URL of the Codesphere API + -h, --help help for codesphere + --timeout duration Timeout when waiting for the installation to become ready (default 5m0s) + --token string API token for authentication + --wait Block and retry until the installation is ready +``` + +### SEE ALSO + +* [oms status](oms_status.md) - Check the status of Codesphere components + From e19df454653e9b2c9e698ab50f3232be5cb9c4ce Mon Sep 17 00:00:00 2001 From: DerBurri <7892993+DerBurri@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:19:58 +0000 Subject: [PATCH 3/3] chore(docs): Auto-update docs and licenses Signed-off-by: DerBurri <7892993+DerBurri@users.noreply.github.com> --- docs/oms_status.md | 6 ++++++ docs/oms_status_codesphere.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/docs/oms_status.md b/docs/oms_status.md index 6183fb6ce..6fe270642 100644 --- a/docs/oms_status.md +++ b/docs/oms_status.md @@ -12,6 +12,12 @@ Check whether Codesphere installations or components are up and ready. -h, --help help for status ``` +### Options inherited from parent commands + +``` + --verbose Enable verbose output +``` + ### SEE ALSO * [oms](oms.md) - Codesphere Operations Management System (OMS) diff --git a/docs/oms_status_codesphere.md b/docs/oms_status_codesphere.md index b21f9c04f..9f2da2b22 100644 --- a/docs/oms_status_codesphere.md +++ b/docs/oms_status_codesphere.md @@ -32,6 +32,12 @@ $ oms status codesphere --baseurl https://codesphere.example.com/api --token YOU --wait Block and retry until the installation is ready ``` +### Options inherited from parent commands + +``` + --verbose Enable verbose output +``` + ### SEE ALSO * [oms status](oms_status.md) - Check the status of Codesphere components