mirror of
https://github.com/beclab/Olares
synced 2026-04-21 13:37:46 +00:00
cli, daemon: add olares-cli status backed by olaresd /system/status (#2917)
* cli, daemon: add `olares-cli status` backed by olaresd /system/status
Adds a new top-level `olares-cli status` command that calls the local
olaresd daemon's `/system/status` HTTP endpoint and prints either a
grouped, annotated human-readable report or raw JSON.
To avoid duplicating the response schema, the daemon's `state` struct
and related enums are extracted into a new shared package
`cli/pkg/daemon/state`. The daemon now type-aliases those types so all
existing daemon call sites keep compiling unchanged.
Made-with: Cursor
* cli: drop unused state.APIResponse envelope type
The HTTP client in cli/pkg/daemon/api/client.go intentionally uses an
inline anonymous envelope with json.RawMessage for the data field so
that --json mode can passthrough the bytes verbatim, so the public
APIResponse{Data State} type defined here had zero references. Remove
it; if a strongly-typed consumer ever shows up, re-add then.
Addresses Cursor Bugbot feedback on PR #2917.
Made-with: Cursor
This commit is contained in:
parent
7841231c2f
commit
61d793e4c6
20 changed files with 1261 additions and 132 deletions
|
|
@ -19,5 +19,6 @@ func NewOSCommands() []*cobra.Command {
|
|||
NewCmdStart(),
|
||||
NewCmdStop(),
|
||||
NewCmdUpgradeOs(),
|
||||
NewCmdStatus(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
270
cli/cmd/ctl/os/status.go
Normal file
270
cli/cmd/ctl/os/status.go
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
package os
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/daemon/api"
|
||||
"github.com/beclab/Olares/cli/pkg/daemon/state"
|
||||
)
|
||||
|
||||
// statusOptions holds the flags accepted by `olares-cli status`.
|
||||
type statusOptions struct {
|
||||
endpoint string
|
||||
json bool
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewCmdStatus returns the cobra command for `olares-cli status`.
|
||||
//
|
||||
// The command is a thin wrapper around the local olaresd daemon's
|
||||
// /system/status endpoint. olaresd binds to 127.0.0.1:18088 and the
|
||||
// endpoint is loopback-only on the daemon side, so the command must
|
||||
// run on the same host as olaresd (typically the master node).
|
||||
func NewCmdStatus() *cobra.Command {
|
||||
opts := &statusOptions{
|
||||
endpoint: api.DefaultEndpoint,
|
||||
timeout: api.DefaultTimeout,
|
||||
}
|
||||
|
||||
long := buildStatusLong()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Print the current Olares system status reported by olaresd",
|
||||
Long: long,
|
||||
Example: ` # Pretty-printed grouped report (default)
|
||||
olares-cli status
|
||||
|
||||
# Raw JSON payload (forwarded verbatim from olaresd)
|
||||
olares-cli status --json | jq
|
||||
|
||||
# Non-default daemon endpoint
|
||||
olares-cli status --endpoint http://127.0.0.1:18088 --timeout 10s`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := runStatus(cmd.Context(), cmd.OutOrStdout(), opts); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&opts.endpoint, "endpoint", opts.endpoint,
|
||||
"Base URL of the local olaresd daemon. Override only when olaresd binds to a non-default address.")
|
||||
cmd.Flags().BoolVar(&opts.json, "json", opts.json,
|
||||
"Print the raw JSON payload from olaresd (the data field), suitable for piping to tools like jq.")
|
||||
cmd.Flags().DurationVar(&opts.timeout, "timeout", opts.timeout,
|
||||
"Maximum time to wait for the olaresd response.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStatus(ctx context.Context, out io.Writer, opts *statusOptions) error {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
client := api.NewClient(opts.endpoint, opts.timeout)
|
||||
st, raw, err := client.GetSystemStatus(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.json {
|
||||
// Pretty-print to keep the output friendly when not piped.
|
||||
var pretty interface{}
|
||||
if jerr := json.Unmarshal(raw, &pretty); jerr == nil {
|
||||
b, merr := json.MarshalIndent(pretty, "", " ")
|
||||
if merr == nil {
|
||||
_, err = fmt.Fprintln(out, string(b))
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = fmt.Fprintln(out, string(raw))
|
||||
return err
|
||||
}
|
||||
|
||||
return printHumanReadable(out, st)
|
||||
}
|
||||
|
||||
// printHumanReadable renders the State as grouped sections, each with
|
||||
// padded labels and inline annotations for state values. The width
|
||||
// is chosen so that values line up when printed in a typical 80-col
|
||||
// terminal without truncating common content.
|
||||
func printHumanReadable(out io.Writer, s *state.State) error {
|
||||
const labelWidth = 20
|
||||
|
||||
w := &writer{out: out, label: labelWidth}
|
||||
|
||||
w.section("Olares")
|
||||
w.kv("State", string(s.TerminusState), s.TerminusState.Describe())
|
||||
w.kv("Olaresd state", string(s.TerminusdState), "")
|
||||
w.kv("Name", strPtr(s.TerminusName), "")
|
||||
w.kv("Version", strPtr(s.TerminusVersion), "")
|
||||
w.kv("Olaresd version", strPtr(s.OlaresdVersion), "")
|
||||
w.kv("Installed at", formatEpoch(s.InstalledTime), "")
|
||||
w.kv("Initialized at", formatEpoch(s.InitializedTime), "")
|
||||
|
||||
w.section("System")
|
||||
w.kv("Device", strPtr(s.DeviceName), "")
|
||||
w.kv("Hostname", strPtr(s.HostName), "")
|
||||
w.kv("OS", joinOSInfo(s.OsType, s.OsArch, s.OsInfo), "")
|
||||
w.kv("OS version", s.OsVersion, "")
|
||||
w.kv("CPU", s.CpuInfo, "")
|
||||
w.kv("Memory", s.Memory, "")
|
||||
w.kv("Disk", s.Disk, "")
|
||||
w.kv("GPU", strPtr(s.GpuInfo), "")
|
||||
|
||||
w.section("Network")
|
||||
w.kv("Wired", yesNo(s.WiredConnected), "")
|
||||
w.kv("Wi-Fi", yesNo(s.WifiConnected), "")
|
||||
w.kv("Wi-Fi SSID", strPtr(s.WifiSSID), "")
|
||||
w.kv("Host IP", s.HostIP, "")
|
||||
w.kv("External IP", s.ExternalIP, "")
|
||||
|
||||
w.section("Install / Uninstall")
|
||||
w.kv("Installing", string(s.InstallingState), s.InstallingProgress)
|
||||
w.kv("Uninstalling", string(s.UninstallingState), s.UninstallingProgress)
|
||||
|
||||
w.section("Upgrade")
|
||||
w.kv("Target", s.UpgradingTarget, "")
|
||||
w.kv("State", string(s.UpgradingState), s.UpgradingProgress)
|
||||
w.kv("Step", s.UpgradingStep, "")
|
||||
w.kv("Last error", s.UpgradingError, "")
|
||||
w.kv("Download state", string(s.UpgradingDownloadState), s.UpgradingDownloadProgress)
|
||||
w.kv("Download step", s.UpgradingDownloadStep, "")
|
||||
w.kv("Download error", s.UpgradingDownloadError, "")
|
||||
if s.UpgradingRetryNum > 0 {
|
||||
w.kv("Retry count", fmt.Sprintf("%d", s.UpgradingRetryNum), "")
|
||||
}
|
||||
if s.UpgradingNextRetryAt != nil {
|
||||
w.kv("Next retry at", s.UpgradingNextRetryAt.Local().Format(time.RFC3339), "")
|
||||
}
|
||||
|
||||
w.section("Logs collection")
|
||||
w.kv("State", string(s.CollectingLogsState), s.CollectingLogsError)
|
||||
|
||||
w.section("Pressures")
|
||||
if len(s.Pressure) == 0 {
|
||||
w.line("(none)")
|
||||
} else {
|
||||
for _, p := range s.Pressure {
|
||||
w.kv(p.Type, p.Message, "")
|
||||
}
|
||||
}
|
||||
|
||||
w.section("Other")
|
||||
w.kv("FRP enabled", s.FRPEnable, "")
|
||||
w.kv("FRP server", s.DefaultFRPServer, "")
|
||||
w.kv("Container mode", strPtr(s.ContainerMode), "")
|
||||
|
||||
return w.err
|
||||
}
|
||||
|
||||
// writer collects formatting helpers in one place so the section
|
||||
// printer above stays declarative.
|
||||
type writer struct {
|
||||
out io.Writer
|
||||
label int
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *writer) writef(format string, a ...interface{}) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
_, w.err = fmt.Fprintf(w.out, format, a...)
|
||||
}
|
||||
|
||||
func (w *writer) section(name string) {
|
||||
w.writef("\n%s\n", name)
|
||||
}
|
||||
|
||||
func (w *writer) line(s string) {
|
||||
w.writef(" %s\n", s)
|
||||
}
|
||||
|
||||
func (w *writer) kv(key, value, note string) {
|
||||
if value == "" {
|
||||
value = "-"
|
||||
}
|
||||
if note != "" {
|
||||
w.writef(" %-*s %s (%s)\n", w.label, key, value, note)
|
||||
} else {
|
||||
w.writef(" %-*s %s\n", w.label, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func strPtr(p *string) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
return *p
|
||||
}
|
||||
|
||||
func yesNo(b bool) string {
|
||||
if b {
|
||||
return "yes"
|
||||
}
|
||||
return "no"
|
||||
}
|
||||
|
||||
func formatEpoch(p *int64) string {
|
||||
if p == nil || *p == 0 {
|
||||
return ""
|
||||
}
|
||||
return time.Unix(*p, 0).Local().Format("2006-01-02 15:04:05 -0700")
|
||||
}
|
||||
|
||||
func joinOSInfo(parts ...string) string {
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
if p != "" {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
// buildStatusLong constructs the cobra Long help text. State
|
||||
// descriptions are sourced from state.TerminusState.Describe() so
|
||||
// that adding a new state here automatically updates the help.
|
||||
func buildStatusLong() string {
|
||||
var b strings.Builder
|
||||
b.WriteString(`Print the current Olares system status reported by the local olaresd daemon.
|
||||
|
||||
This command sends an HTTP GET to the daemon's /system/status endpoint
|
||||
(default: http://127.0.0.1:18088/system/status). The endpoint is
|
||||
loopback-only on the daemon side, so this command must run on the same
|
||||
host as olaresd (typically the master node).
|
||||
|
||||
The default output is a grouped, human-readable report:
|
||||
|
||||
Olares installation lifecycle, version, names, key timestamps
|
||||
System hardware and OS facts about the host
|
||||
Network connectivity and IP addresses
|
||||
Install / Uninstall progress of in-flight install or uninstall
|
||||
Upgrade progress of in-flight upgrade (download + install phases)
|
||||
Logs collection state of the most recent log collection job
|
||||
Pressures active kubelet node pressure conditions, if any
|
||||
Other FRP, container mode, etc.
|
||||
|
||||
Pass --json to get the raw daemon payload instead, useful for scripting.
|
||||
|
||||
Olares system states (TerminusState):
|
||||
|
||||
`)
|
||||
|
||||
for _, s := range state.AllTerminusStates() {
|
||||
fmt.Fprintf(&b, " %-20s %s\n", string(s), s.Describe())
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
138
cli/pkg/daemon/api/client.go
Normal file
138
cli/pkg/daemon/api/client.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// Package api is a thin HTTP client for the local olaresd daemon.
|
||||
//
|
||||
// olaresd listens on TCP port 18088 by default and exposes JSON
|
||||
// endpoints under paths like /system/status, /system/ifs, etc. All
|
||||
// endpoints are loopback-only (RequireLocal middleware on the daemon
|
||||
// side), so the client expects to talk to 127.0.0.1.
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/daemon/state"
|
||||
)
|
||||
|
||||
// DefaultEndpoint is the loopback URL where olaresd listens by
|
||||
// default. Callers normally do not need to override this.
|
||||
const DefaultEndpoint = "http://127.0.0.1:18088"
|
||||
|
||||
// DefaultTimeout is the per-request timeout used when the caller
|
||||
// does not supply one.
|
||||
const DefaultTimeout = 5 * time.Second
|
||||
|
||||
// Client is a small HTTP client for the olaresd daemon. The zero
|
||||
// value uses DefaultEndpoint and DefaultTimeout; callers may
|
||||
// override either field before issuing requests.
|
||||
type Client struct {
|
||||
// Endpoint is the base URL of olaresd, e.g.
|
||||
// "http://127.0.0.1:18088". Trailing slashes are stripped.
|
||||
Endpoint string
|
||||
|
||||
// Timeout bounds the total time spent on a single HTTP request,
|
||||
// including dialing, TLS, and reading the response body.
|
||||
Timeout time.Duration
|
||||
|
||||
// HTTPClient lets tests substitute a custom transport. When
|
||||
// nil, the client constructs one on demand using Timeout.
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewClient returns a Client that hits the given endpoint with the
|
||||
// supplied timeout. Pass an empty endpoint or zero timeout to fall
|
||||
// back to DefaultEndpoint / DefaultTimeout.
|
||||
func NewClient(endpoint string, timeout time.Duration) *Client {
|
||||
return &Client{Endpoint: endpoint, Timeout: timeout}
|
||||
}
|
||||
|
||||
func (c *Client) endpoint() string {
|
||||
ep := c.Endpoint
|
||||
if ep == "" {
|
||||
ep = DefaultEndpoint
|
||||
}
|
||||
return strings.TrimRight(ep, "/")
|
||||
}
|
||||
|
||||
func (c *Client) httpClient() *http.Client {
|
||||
if c.HTTPClient != nil {
|
||||
return c.HTTPClient
|
||||
}
|
||||
t := c.Timeout
|
||||
if t <= 0 {
|
||||
t = DefaultTimeout
|
||||
}
|
||||
return &http.Client{Timeout: t}
|
||||
}
|
||||
|
||||
// GetSystemStatus calls GET /system/status and returns:
|
||||
// - the parsed State struct for programmatic consumption;
|
||||
// - the raw `data` JSON bytes so callers that want to forward
|
||||
// the response (for example, the CLI's --json mode) can do so
|
||||
// without a re-marshal round trip.
|
||||
//
|
||||
// The error message always contains the endpoint URL and the HTTP
|
||||
// status (when applicable) so users can tell the difference between
|
||||
// "olaresd is down" and "olaresd returned an error".
|
||||
func (c *Client) GetSystemStatus(ctx context.Context) (*state.State, []byte, error) {
|
||||
u, err := url.Parse(c.endpoint())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid olaresd endpoint %q: %w", c.endpoint(), err)
|
||||
}
|
||||
u.Path = "/system/status"
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("build request to %s: %w", u.String(), err)
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := c.httpClient().Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("request olaresd at %s (is olaresd running?): %w", u.String(), err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("read response from %s: %w", u.String(), err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("olaresd %s returned HTTP %d: %s", u.String(), resp.StatusCode, truncate(string(body), 200))
|
||||
}
|
||||
|
||||
// olaresd wraps payloads as {code, message, data}. We need the
|
||||
// raw `data` slice both to populate State and to expose it
|
||||
// verbatim to --json callers.
|
||||
var raw struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &raw); err != nil {
|
||||
return nil, nil, fmt.Errorf("decode response envelope from %s: %w", u.String(), err)
|
||||
}
|
||||
if raw.Code != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("olaresd returned code=%d message=%q", raw.Code, raw.Message)
|
||||
}
|
||||
|
||||
var s state.State
|
||||
if err := json.Unmarshal(raw.Data, &s); err != nil {
|
||||
return nil, nil, fmt.Errorf("decode state payload from %s: %w", u.String(), err)
|
||||
}
|
||||
|
||||
return &s, []byte(raw.Data), nil
|
||||
}
|
||||
|
||||
func truncate(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
return s
|
||||
}
|
||||
return s[:n] + "..."
|
||||
}
|
||||
202
cli/pkg/daemon/state/constants.go
Normal file
202
cli/pkg/daemon/state/constants.go
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
package state
|
||||
|
||||
// TerminusDState is the lifecycle state of the olaresd daemon process.
|
||||
type TerminusDState string
|
||||
|
||||
const (
|
||||
// Initialize means olaresd has just started and is still
|
||||
// bootstrapping its watchers and configuration.
|
||||
Initialize TerminusDState = "initialize"
|
||||
|
||||
// Running means olaresd has finished initialization and is
|
||||
// serving requests normally.
|
||||
Running TerminusDState = "running"
|
||||
)
|
||||
|
||||
// ProcessingState is the lifecycle of a long-running operation that
|
||||
// olaresd reports progress for (install, uninstall, upgrade, log
|
||||
// collection).
|
||||
type ProcessingState string
|
||||
|
||||
const (
|
||||
// Completed means the operation finished successfully.
|
||||
Completed ProcessingState = "completed"
|
||||
|
||||
// Failed means the operation finished with an error. Inspect the
|
||||
// associated *Error field for details.
|
||||
Failed ProcessingState = "failed"
|
||||
|
||||
// InProgress means the operation is currently running.
|
||||
InProgress ProcessingState = "in-progress"
|
||||
)
|
||||
|
||||
// TerminusState is the high-level state machine value for the Olares
|
||||
// system as observed from this node. Use Describe() to obtain a
|
||||
// human-readable, one-line summary suitable for end-user output.
|
||||
type TerminusState string
|
||||
|
||||
const (
|
||||
// NotInstalled means Olares is not installed on this node.
|
||||
NotInstalled TerminusState = "not-installed"
|
||||
|
||||
// Installing means an installation is currently in progress.
|
||||
Installing TerminusState = "installing"
|
||||
|
||||
// InstallFailed means the most recent installation attempt
|
||||
// failed. Re-run install or uninstall to recover.
|
||||
InstallFailed TerminusState = "install-failed"
|
||||
|
||||
// Uninitialized means Olares is installed but the admin user has
|
||||
// not completed initial activation yet.
|
||||
Uninitialized TerminusState = "uninitialized"
|
||||
|
||||
// Initializing means the admin user is going through the initial
|
||||
// activation flow.
|
||||
Initializing TerminusState = "initializing"
|
||||
|
||||
// InitializeFailed means the initial activation failed.
|
||||
InitializeFailed TerminusState = "initialize-failed"
|
||||
|
||||
// TerminusRunning means Olares is fully installed, activated,
|
||||
// and all key pods are healthy.
|
||||
TerminusRunning TerminusState = "terminus-running"
|
||||
|
||||
// InvalidIpAddress means the node's IP has changed since
|
||||
// installation; run change-ip to fix it.
|
||||
InvalidIpAddress TerminusState = "invalid-ip-address"
|
||||
|
||||
// SystemError means one or more critical pods are not running,
|
||||
// or the cluster API is unreachable.
|
||||
SystemError TerminusState = "system-error"
|
||||
|
||||
// SelfRepairing means olaresd is automatically attempting to
|
||||
// recover from a system error.
|
||||
SelfRepairing TerminusState = "self-repairing"
|
||||
|
||||
// IPChanging means a change-ip operation is currently running.
|
||||
IPChanging TerminusState = "ip-changing"
|
||||
|
||||
// IPChangeFailed means the most recent change-ip attempt failed.
|
||||
IPChangeFailed TerminusState = "ip-change-failed"
|
||||
|
||||
// AddingNode means a worker node is currently being joined.
|
||||
AddingNode TerminusState = "adding-node"
|
||||
|
||||
// RemovingNode means a worker node is currently being removed.
|
||||
RemovingNode TerminusState = "removing-node"
|
||||
|
||||
// Uninstalling means an uninstall is currently in progress.
|
||||
Uninstalling TerminusState = "uninstalling"
|
||||
|
||||
// Upgrading means an upgrade install phase is currently running.
|
||||
// The dedicated download phase does not flip TerminusState.
|
||||
Upgrading TerminusState = "upgrading"
|
||||
|
||||
// DiskModifing means a storage reconfiguration is in progress.
|
||||
DiskModifing TerminusState = "disk-modifing"
|
||||
|
||||
// Shutdown means the system is in the process of shutting down.
|
||||
Shutdown TerminusState = "shutdown"
|
||||
|
||||
// Restarting means the node has been up for less than the
|
||||
// stabilization window (3 minutes for healthy systems, 10 for
|
||||
// degraded ones), so reported pod state may be stale.
|
||||
Restarting TerminusState = "restarting"
|
||||
|
||||
// Checking means olaresd has not yet completed the first status
|
||||
// probe. This is the default value before WatchStatus runs.
|
||||
Checking TerminusState = "checking"
|
||||
|
||||
// NetworkNotReady means no usable internal IPv4 address was
|
||||
// detected on this node.
|
||||
NetworkNotReady TerminusState = "network-not-ready"
|
||||
)
|
||||
|
||||
// String returns the wire value of the state, allowing TerminusState
|
||||
// to satisfy fmt.Stringer.
|
||||
func (s TerminusState) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Describe returns a one-line, end-user oriented explanation of the
|
||||
// state value. Empty values are rendered as "unknown state". Unknown
|
||||
// values are returned as-is so the CLI can still display them.
|
||||
func (s TerminusState) Describe() string {
|
||||
switch s {
|
||||
case NotInstalled:
|
||||
return "Olares is not installed on this node"
|
||||
case Installing:
|
||||
return "Olares is currently being installed"
|
||||
case InstallFailed:
|
||||
return "the most recent install attempt failed"
|
||||
case Uninitialized:
|
||||
return "Olares is installed but the admin user has not been activated yet"
|
||||
case Initializing:
|
||||
return "the admin user activation is in progress"
|
||||
case InitializeFailed:
|
||||
return "the admin user activation failed"
|
||||
case TerminusRunning:
|
||||
return "Olares is running normally"
|
||||
case InvalidIpAddress:
|
||||
return "the node IP changed since install; run change-ip to recover"
|
||||
case SystemError:
|
||||
return "one or more critical pods are not running"
|
||||
case SelfRepairing:
|
||||
return "olaresd is attempting automatic recovery"
|
||||
case IPChanging:
|
||||
return "a change-ip operation is in progress"
|
||||
case IPChangeFailed:
|
||||
return "the most recent change-ip attempt failed"
|
||||
case AddingNode:
|
||||
return "a worker node is being joined"
|
||||
case RemovingNode:
|
||||
return "a worker node is being removed"
|
||||
case Uninstalling:
|
||||
return "Olares is being uninstalled"
|
||||
case Upgrading:
|
||||
return "an upgrade is being applied"
|
||||
case DiskModifing:
|
||||
return "the storage layout is being modified"
|
||||
case Shutdown:
|
||||
return "the system is shutting down"
|
||||
case Restarting:
|
||||
return "the node was just restarted, status will stabilize shortly"
|
||||
case Checking:
|
||||
return "olaresd has not finished the first status probe yet"
|
||||
case NetworkNotReady:
|
||||
return "no usable internal IPv4 address detected"
|
||||
case "":
|
||||
return "unknown state"
|
||||
default:
|
||||
return string(s)
|
||||
}
|
||||
}
|
||||
|
||||
// AllTerminusStates returns the full list of TerminusState values in
|
||||
// a stable, documentation-friendly order. It is used by the CLI's
|
||||
// long help text and by the docs generator.
|
||||
func AllTerminusStates() []TerminusState {
|
||||
return []TerminusState{
|
||||
Checking,
|
||||
NetworkNotReady,
|
||||
NotInstalled,
|
||||
Installing,
|
||||
InstallFailed,
|
||||
Uninitialized,
|
||||
Initializing,
|
||||
InitializeFailed,
|
||||
TerminusRunning,
|
||||
Restarting,
|
||||
InvalidIpAddress,
|
||||
IPChanging,
|
||||
IPChangeFailed,
|
||||
SystemError,
|
||||
SelfRepairing,
|
||||
AddingNode,
|
||||
RemovingNode,
|
||||
Uninstalling,
|
||||
Upgrading,
|
||||
DiskModifing,
|
||||
Shutdown,
|
||||
}
|
||||
}
|
||||
234
cli/pkg/daemon/state/types.go
Normal file
234
cli/pkg/daemon/state/types.go
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
// Package state defines the data types exposed by the local olaresd
|
||||
// daemon's GET /system/status endpoint.
|
||||
//
|
||||
// These types are intentionally placed in the cli module so that both
|
||||
// the olaresd daemon and the olares-cli command line tool can share
|
||||
// the same wire format. The daemon (which already imports this module
|
||||
// via its go.mod) re-exports these types as aliases, while the CLI
|
||||
// uses them directly to unmarshal the HTTP response.
|
||||
//
|
||||
// Only data types belong here. Business logic that depends on
|
||||
// daemon-internal packages (state validators, status probing, etc.)
|
||||
// must remain in the daemon module.
|
||||
package state
|
||||
|
||||
import "time"
|
||||
|
||||
// State is the full system status snapshot maintained by olaresd.
|
||||
// It is refreshed every 5s by the daemon's status watcher and served
|
||||
// as the `data` field of the GET /system/status response.
|
||||
//
|
||||
// All fields use JSON tags that match the wire format byte for byte;
|
||||
// do not rename JSON keys without updating every consumer (CLI,
|
||||
// frontend, mDNS clients, etc.).
|
||||
type State struct {
|
||||
// TerminusdState is the lifecycle state of the olaresd daemon
|
||||
// itself. Possible values: "initialize" (just started, still
|
||||
// bootstrapping) or "running" (fully initialized).
|
||||
TerminusdState TerminusDState `json:"terminusdState"`
|
||||
|
||||
// TerminusState is the high-level state of the Olares system.
|
||||
// It drives both UI display and command admission control. See
|
||||
// the TerminusState constants below for the full enumeration
|
||||
// and call Describe() to obtain a one-line explanation.
|
||||
TerminusState TerminusState `json:"terminusState"`
|
||||
|
||||
// TerminusName is the Olares ID of the admin user, e.g.
|
||||
// "alice@olares.cn". It is read from the local release file when
|
||||
// available and refreshed from the cluster once Olares is up.
|
||||
TerminusName *string `json:"terminusName,omitempty"`
|
||||
|
||||
// TerminusVersion is the installed Olares version (semver), e.g.
|
||||
// "1.12.0".
|
||||
TerminusVersion *string `json:"terminusVersion,omitempty"`
|
||||
|
||||
// InstalledTime is the Unix epoch (seconds) at which Olares
|
||||
// finished installing on this node. Nil before install completes.
|
||||
InstalledTime *int64 `json:"installedTime,omitempty"`
|
||||
|
||||
// InitializedTime is the Unix epoch (seconds) at which the admin
|
||||
// user finished the initial activation. Nil before activation.
|
||||
InitializedTime *int64 `json:"initializedTime,omitempty"`
|
||||
|
||||
// OlaresdVersion is the running olaresd binary version. Useful
|
||||
// for diagnosing version drift between olaresd and the rest of
|
||||
// Olares after a partial upgrade.
|
||||
OlaresdVersion *string `json:"olaresdVersion,omitempty"`
|
||||
|
||||
// InstallFinishedTime is daemon-internal: the wall clock time at
|
||||
// which the most recent install finished. Used to derive
|
||||
// InstalledTime when the cluster is not reachable yet. Excluded
|
||||
// from the wire format.
|
||||
InstallFinishedTime *time.Time `json:"-"`
|
||||
|
||||
// DeviceName is the user-friendly device name (model / chassis
|
||||
// name) detected from the host.
|
||||
DeviceName *string `json:"device_name,omitempty"`
|
||||
|
||||
// HostName is the kernel hostname of the node.
|
||||
HostName *string `json:"host_name,omitempty"`
|
||||
|
||||
// OsType is the OS family, e.g. "linux" or "darwin".
|
||||
OsType string `json:"os_type"`
|
||||
|
||||
// OsArch is the CPU architecture, e.g. "amd64" or "arm64".
|
||||
OsArch string `json:"os_arch"`
|
||||
|
||||
// OsInfo is a human-readable OS distribution string, e.g.
|
||||
// "Ubuntu 22.04".
|
||||
OsInfo string `json:"os_info"`
|
||||
|
||||
// OsVersion is the OS version string, e.g. "22.04".
|
||||
OsVersion string `json:"os_version"`
|
||||
|
||||
// CpuInfo is the CPU model name as reported by the OS.
|
||||
CpuInfo string `json:"cpu_info"`
|
||||
|
||||
// GpuInfo is the GPU model name when one is detected.
|
||||
GpuInfo *string `json:"gpu_info,omitempty"`
|
||||
|
||||
// Memory is the total physical memory, formatted as "<n> G".
|
||||
Memory string `json:"memory"`
|
||||
|
||||
// Disk is the total filesystem size of the node's data partition,
|
||||
// formatted as "<n> G".
|
||||
Disk string `json:"disk"`
|
||||
|
||||
// WifiConnected is true when the active default route is over
|
||||
// Wi-Fi. The JSON key is "wifiConnected".
|
||||
WifiConnected bool `json:"wifiConnected"`
|
||||
|
||||
// WifiSSID is the SSID of the connected Wi-Fi network, when
|
||||
// WifiConnected is true.
|
||||
WifiSSID *string `json:"wifiSSID,omitempty"`
|
||||
|
||||
// WiredConnected is true when the node has an active Ethernet
|
||||
// connection.
|
||||
WiredConnected bool `json:"wiredConnected"`
|
||||
|
||||
// HostIP is the internal LAN IPv4 address that Olares uses to
|
||||
// register itself in /etc/hosts and to reach other nodes.
|
||||
HostIP string `json:"hostIp"`
|
||||
|
||||
// ExternalIP is the public IPv4 address as observed by an
|
||||
// external probe. Refreshed at most once per minute.
|
||||
ExternalIP string `json:"externalIp"`
|
||||
|
||||
// ExternalIPProbeTime is daemon-internal: when the external IP
|
||||
// probe last ran. Excluded from the wire format.
|
||||
ExternalIPProbeTime time.Time `json:"-"`
|
||||
|
||||
// InstallingState reports the progress of an installation in
|
||||
// flight: "in-progress", "completed", "failed", or empty.
|
||||
InstallingState ProcessingState `json:"installingState"`
|
||||
|
||||
// InstallingProgress is a free-form human-readable description
|
||||
// of the current installation step.
|
||||
InstallingProgress string `json:"installingProgress"`
|
||||
|
||||
// InstallingProgressNum is daemon-internal: the latest numeric
|
||||
// install progress percentage. Excluded from the wire format.
|
||||
InstallingProgressNum int `json:"-"`
|
||||
|
||||
// UninstallingState mirrors InstallingState for the uninstall
|
||||
// flow.
|
||||
UninstallingState ProcessingState `json:"uninstallingState"`
|
||||
|
||||
// UninstallingProgress is a free-form description of the
|
||||
// current uninstall step.
|
||||
UninstallingProgress string `json:"uninstallingProgress"`
|
||||
|
||||
// UninstallingProgressNum is daemon-internal: the latest numeric
|
||||
// uninstall progress percentage. Excluded from the wire format.
|
||||
UninstallingProgressNum int `json:"-"`
|
||||
|
||||
// UpgradingTarget is the target version of the in-flight
|
||||
// upgrade, e.g. "1.13.0". Empty when no upgrade is queued.
|
||||
UpgradingTarget string `json:"upgradingTarget"`
|
||||
|
||||
// UpgradingRetryNum is the number of times the upgrader has
|
||||
// retried after a transient failure.
|
||||
UpgradingRetryNum int `json:"upgradingRetryNum"`
|
||||
|
||||
// UpgradingNextRetryAt is the wall-clock time at which the next
|
||||
// retry will fire, when retries are pending.
|
||||
UpgradingNextRetryAt *time.Time `json:"upgradingNextRetryAt,omitempty"`
|
||||
|
||||
// UpgradingState is the lifecycle of the install phase of the
|
||||
// upgrade ("in-progress", "completed", "failed", or empty).
|
||||
UpgradingState ProcessingState `json:"upgradingState"`
|
||||
|
||||
// UpgradingStep is the name of the current upgrade step.
|
||||
UpgradingStep string `json:"upgradingStep"`
|
||||
|
||||
// UpgradingProgress is the free-form progress message for the
|
||||
// current upgrade step.
|
||||
UpgradingProgress string `json:"upgradingProgress"`
|
||||
|
||||
// UpgradingProgressNum is daemon-internal: the latest numeric
|
||||
// upgrade progress percentage. Excluded from the wire format.
|
||||
UpgradingProgressNum int `json:"-"`
|
||||
|
||||
// UpgradingError is the most recent error seen during upgrade.
|
||||
// Empty when no error has occurred.
|
||||
UpgradingError string `json:"upgradingError"`
|
||||
|
||||
// UpgradingDownloadState is the lifecycle of the download phase
|
||||
// of the upgrade. Olares splits download and install into two
|
||||
// phases so that downloads can complete in the background
|
||||
// without changing TerminusState to "upgrading".
|
||||
UpgradingDownloadState ProcessingState `json:"upgradingDownloadState"`
|
||||
|
||||
// UpgradingDownloadStep is the name of the current download step.
|
||||
UpgradingDownloadStep string `json:"upgradingDownloadStep"`
|
||||
|
||||
// UpgradingDownloadProgress is the free-form progress message
|
||||
// for the current download step.
|
||||
UpgradingDownloadProgress string `json:"upgradingDownloadProgress"`
|
||||
|
||||
// UpgradingDownloadProgressNum is daemon-internal: the latest
|
||||
// numeric download progress percentage. Excluded from the wire
|
||||
// format.
|
||||
UpgradingDownloadProgressNum int `json:"-"`
|
||||
|
||||
// UpgradingDownloadError is the most recent error from the
|
||||
// download phase. Empty when no error has occurred.
|
||||
UpgradingDownloadError string `json:"upgradingDownloadError"`
|
||||
|
||||
// CollectingLogsState is the lifecycle of the most recent log
|
||||
// collection job triggered through olaresd.
|
||||
CollectingLogsState ProcessingState `json:"collectingLogsState"`
|
||||
|
||||
// CollectingLogsError is the error from the most recent log
|
||||
// collection job, when it failed.
|
||||
CollectingLogsError string `json:"collectingLogsError"`
|
||||
|
||||
// DefaultFRPServer is the FRP server address used when frp is
|
||||
// enabled. Sourced from the FRP_SERVER env var.
|
||||
DefaultFRPServer string `json:"defaultFrpServer"`
|
||||
|
||||
// FRPEnable indicates whether the FRP-based reverse tunnel is
|
||||
// turned on. Sourced from the FRP_ENABLE env var.
|
||||
FRPEnable string `json:"frpEnable"`
|
||||
|
||||
// ContainerMode is set when olaresd is running inside a
|
||||
// container, mirroring the CONTAINER_MODE env var.
|
||||
ContainerMode *string `json:"containerMode,omitempty"`
|
||||
|
||||
// Pressure lists the kubernetes node-condition pressures
|
||||
// currently active on this node (memory pressure, disk pressure,
|
||||
// PID pressure, etc.). Empty when the node is healthy.
|
||||
Pressure []NodePressure `json:"pressures,omitempty"`
|
||||
}
|
||||
|
||||
// NodePressure represents a non-Ready kubernetes node condition that
|
||||
// is currently true on this node, e.g. MemoryPressure, DiskPressure,
|
||||
// PIDPressure, NetworkUnavailable.
|
||||
type NodePressure struct {
|
||||
// Type is the kubernetes node condition type, e.g.
|
||||
// "MemoryPressure".
|
||||
Type string `json:"type"`
|
||||
|
||||
// Message is the human-readable explanation provided by kubelet.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
|
@ -5,13 +5,13 @@ go 1.24.11
|
|||
replace (
|
||||
bytetrade.io/web3os/backups-sdk => github.com/Above-Os/backups-sdk v0.1.17
|
||||
bytetrade.io/web3os/bfl => github.com/beclab/bfl v0.3.36
|
||||
github.com/beclab/Olares/cli => ../cli
|
||||
github.com/labstack/echo/v4 => github.com/eball/echo/v4 v4.13.4-patch
|
||||
k8s.io/api => k8s.io/api v0.34.0
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.34.0
|
||||
k8s.io/client-go => k8s.io/client-go v0.34.0
|
||||
kubesphere.io/api => ../../kubesphere-ext/staging/src/kubesphere.io/api/
|
||||
sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.19.6
|
||||
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/beclab/Olares/cli v0.0.0-20251230161135-5264df60cc33
|
||||
github.com/beclab/Olares/framework/app-service v0.0.0-20251225061130-909b7656fd70
|
||||
github.com/beclab/Olares/framework/app-service v0.0.0-20260311124303-23a6533bc2ad
|
||||
github.com/containerd/containerd v1.7.29
|
||||
github.com/distribution/distribution/v3 v3.0.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
|
|
@ -46,7 +46,7 @@ require (
|
|||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/shirou/gopsutil/v4 v4.25.7
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/pflag v1.0.7
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/txn2/txeh v1.5.5
|
||||
github.com/vishvananda/netlink v1.3.0
|
||||
go.opentelemetry.io/otel/trace v1.40.0
|
||||
|
|
@ -173,7 +173,7 @@ require (
|
|||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
|
||||
|
|
|
|||
|
|
@ -24,10 +24,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
|||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/beclab/Olares/cli v0.0.0-20251230161135-5264df60cc33 h1:WYuUPOT/p26aCDJGJEDai1v7YM6QHiaFDusBVynnbBY=
|
||||
github.com/beclab/Olares/cli v0.0.0-20251230161135-5264df60cc33/go.mod h1:ixhzBK5XIovsRB5djk44TChsOK4wum2q4y/hZxJKlNw=
|
||||
github.com/beclab/Olares/framework/app-service v0.0.0-20251225061130-909b7656fd70 h1:U3z6m0hokD1gzl788BrUdxCbDyAjdOBBXA8ilYgn6VQ=
|
||||
github.com/beclab/Olares/framework/app-service v0.0.0-20251225061130-909b7656fd70/go.mod h1:D9wl7y3obLqXMqfubMROMgdxWAwInnKNrFC//d0nyIA=
|
||||
github.com/beclab/Olares/framework/app-service v0.0.0-20260311124303-23a6533bc2ad h1:nmQCNbJNtgTqcusySeeyd9LQOK2jyk78QAjzmvyyPYg=
|
||||
github.com/beclab/Olares/framework/app-service v0.0.0-20260311124303-23a6533bc2ad/go.mod h1:D9wl7y3obLqXMqfubMROMgdxWAwInnKNrFC//d0nyIA=
|
||||
github.com/beclab/bfl v0.3.36 h1:PgeSPGc+XoONiwFsKq9xX8rqcL4kVM1G/ut0lYYj/js=
|
||||
github.com/beclab/bfl v0.3.36/go.mod h1:A82u38MxYk1C3Lqnm4iUUK4hBeY9HHIs+xU4V93OnJk=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
|
@ -331,8 +329,8 @@ github.com/soypat/cyw43439 v0.0.0-20240609122733-da9153086796 h1:1/r2URInjjFtWqT
|
|||
github.com/soypat/cyw43439 v0.0.0-20240609122733-da9153086796/go.mod h1:1Otjk6PRhfzfcVHeWMEeku/VntFqWghUwuSQyivb2vE=
|
||||
github.com/soypat/seqs v0.0.0-20240527012110-1201bab640ef h1:phH95I9wANjTYw6bSYLZDQfNvao+HqYDom8owbNa0P4=
|
||||
github.com/soypat/seqs v0.0.0-20240527012110-1201bab640ef/go.mod h1:oCVCNGCHMKoBj97Zp9znLbQ1nHxpkmOY9X+UAGzOxc8=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
|
@ -386,8 +384,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6h
|
|||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ func (h *Handlers) RunCommand(next func(ctx *fiber.Ctx, cmd commands.Interface)
|
|||
|
||||
return func(ctx *fiber.Ctx) error {
|
||||
c := cmdNew()
|
||||
err := state.CurrentState.TerminusState.ValidateOp(c)
|
||||
err := state.ValidateOp(state.CurrentState.TerminusState, c)
|
||||
if err != nil {
|
||||
return h.ErrJSON(ctx, http.StatusForbidden, err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
clistate "github.com/beclab/Olares/cli/pkg/daemon/state"
|
||||
"github.com/beclab/Olares/daemon/internel/watcher"
|
||||
"github.com/beclab/Olares/daemon/pkg/commands"
|
||||
"github.com/beclab/Olares/daemon/pkg/nets"
|
||||
|
|
@ -19,68 +20,11 @@ import (
|
|||
"github.com/pbnjay/memory"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
TerminusdState TerminusDState `json:"terminusdState"`
|
||||
TerminusState TerminusState `json:"terminusState"`
|
||||
TerminusName *string `json:"terminusName,omitempty"`
|
||||
TerminusVersion *string `json:"terminusVersion,omitempty"`
|
||||
InstalledTime *int64 `json:"installedTime,omitempty"`
|
||||
InitializedTime *int64 `json:"initializedTime,omitempty"`
|
||||
OlaresdVersion *string `json:"olaresdVersion,omitempty"`
|
||||
InstallFinishedTime *time.Time `json:"-"`
|
||||
|
||||
// sys info
|
||||
DeviceName *string `json:"device_name,omitempty"`
|
||||
HostName *string `json:"host_name,omitempty"`
|
||||
OsType string `json:"os_type"`
|
||||
OsArch string `json:"os_arch"`
|
||||
OsInfo string `json:"os_info"`
|
||||
OsVersion string `json:"os_version"`
|
||||
CpuInfo string `json:"cpu_info"`
|
||||
GpuInfo *string `json:"gpu_info,omitempty"`
|
||||
Memory string `json:"memory"`
|
||||
Disk string `json:"disk"`
|
||||
|
||||
// network info
|
||||
WikiConnected bool `json:"wifiConnected"`
|
||||
WifiSSID *string `json:"wifiSSID,omitempty"`
|
||||
WiredConnected bool `json:"wiredConnected"`
|
||||
HostIP string `json:"hostIp"`
|
||||
ExternalIP string `json:"externalIp"`
|
||||
ExternalIPProbeTime time.Time `json:"-"`
|
||||
|
||||
// installing / uninstalling / upgrading state
|
||||
InstallingState ProcessingState `json:"installingState"`
|
||||
InstallingProgress string `json:"installingProgress"`
|
||||
InstallingProgressNum int `json:"-"`
|
||||
UninstallingState ProcessingState `json:"uninstallingState"`
|
||||
UninstallingProgress string `json:"uninstallingProgress"`
|
||||
UninstallingProgressNum int `json:"-"`
|
||||
UpgradingTarget string `json:"upgradingTarget"`
|
||||
UpgradingRetryNum int `json:"upgradingRetryNum"`
|
||||
UpgradingNextRetryAt *time.Time `json:"upgradingNextRetryAt,omitempty"`
|
||||
UpgradingState ProcessingState `json:"upgradingState"`
|
||||
UpgradingStep string `json:"upgradingStep"`
|
||||
UpgradingProgress string `json:"upgradingProgress"`
|
||||
UpgradingProgressNum int `json:"-"`
|
||||
UpgradingError string `json:"upgradingError"`
|
||||
|
||||
UpgradingDownloadState ProcessingState `json:"upgradingDownloadState"`
|
||||
UpgradingDownloadStep string `json:"upgradingDownloadStep"`
|
||||
UpgradingDownloadProgress string `json:"upgradingDownloadProgress"`
|
||||
UpgradingDownloadProgressNum int `json:"-"`
|
||||
UpgradingDownloadError string `json:"upgradingDownloadError"`
|
||||
|
||||
CollectingLogsState ProcessingState `json:"collectingLogsState"`
|
||||
CollectingLogsError string `json:"collectingLogsError"`
|
||||
|
||||
DefaultFRPServer string `json:"defaultFrpServer"`
|
||||
FRPEnable string `json:"frpEnable"`
|
||||
|
||||
ContainerMode *string `json:"containerMode,omitempty"`
|
||||
|
||||
Pressure []utils.NodePressure `json:"pressures,omitempty"`
|
||||
}
|
||||
// state is a daemon-local alias of the canonical wire-format struct
|
||||
// defined in cli/pkg/daemon/state. Keeping it as an alias means
|
||||
// existing daemon code keeps using the same field names without
|
||||
// changes, while the CLI shares the exact same data type.
|
||||
type state = clistate.State
|
||||
|
||||
var CurrentState state
|
||||
var StateTrigger chan struct{}
|
||||
|
|
@ -92,11 +36,15 @@ func init() {
|
|||
StateTrigger = make(chan struct{})
|
||||
}
|
||||
|
||||
func (c *state) ChangeTerminusStateTo(s TerminusState) {
|
||||
// ChangeTerminusStateTo updates the global TerminusState under the
|
||||
// shared mutex. It used to be a method on state, but methods cannot
|
||||
// be defined on a type alias whose underlying type lives in another
|
||||
// package, so it is now a package-level function.
|
||||
func ChangeTerminusStateTo(s TerminusState) {
|
||||
TerminusStateMu.Lock()
|
||||
defer TerminusStateMu.Unlock()
|
||||
|
||||
c.TerminusState = s
|
||||
CurrentState.TerminusState = s
|
||||
}
|
||||
|
||||
func bToGb(b uint64) string {
|
||||
|
|
@ -193,7 +141,7 @@ func CheckCurrentStatus(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// clear value
|
||||
CurrentState.WikiConnected = false
|
||||
CurrentState.WifiConnected = false
|
||||
CurrentState.WifiSSID = nil
|
||||
CurrentState.WiredConnected = false
|
||||
for _, i := range ips {
|
||||
|
|
@ -201,7 +149,7 @@ func CheckCurrentStatus(ctx context.Context) error {
|
|||
if d, ok := devices[i.Iface.Name]; ok {
|
||||
switch d.Type {
|
||||
case "wifi":
|
||||
CurrentState.WikiConnected = true
|
||||
CurrentState.WifiConnected = true
|
||||
CurrentState.WifiSSID = &d.Connection
|
||||
case "ethernet":
|
||||
CurrentState.WiredConnected = true
|
||||
|
|
@ -212,7 +160,7 @@ func CheckCurrentStatus(ctx context.Context) error {
|
|||
|
||||
// for macos
|
||||
if strings.HasPrefix(i.Iface.Name, "wl") {
|
||||
CurrentState.WikiConnected = true
|
||||
CurrentState.WifiConnected = true
|
||||
CurrentState.WifiSSID = utils.WifiName()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
package state
|
||||
|
||||
type TerminusDState string
|
||||
import clistate "github.com/beclab/Olares/cli/pkg/daemon/state"
|
||||
|
||||
// TerminusDState is the lifecycle state of olaresd itself. The
|
||||
// canonical definition lives in the cli module and is re-exported
|
||||
// here as an alias so the daemon can use the unqualified name.
|
||||
type TerminusDState = clistate.TerminusDState
|
||||
|
||||
const (
|
||||
Initialize TerminusDState = "initialize"
|
||||
Running TerminusDState = "running"
|
||||
Initialize = clistate.Initialize
|
||||
Running = clistate.Running
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,52 +3,57 @@ package state
|
|||
import (
|
||||
"errors"
|
||||
|
||||
clistate "github.com/beclab/Olares/cli/pkg/daemon/state"
|
||||
"github.com/beclab/Olares/daemon/pkg/commands"
|
||||
)
|
||||
|
||||
type ProcessingState string
|
||||
// Wire-format types live in the cli module so that olaresd and the
|
||||
// olares-cli command share a single source of truth. Daemon code keeps
|
||||
// using the unqualified names below via these type aliases.
|
||||
|
||||
type ProcessingState = clistate.ProcessingState
|
||||
|
||||
const (
|
||||
Completed ProcessingState = "completed"
|
||||
Failed ProcessingState = "failed"
|
||||
InProgress ProcessingState = "in-progress"
|
||||
Completed = clistate.Completed
|
||||
Failed = clistate.Failed
|
||||
InProgress = clistate.InProgress
|
||||
)
|
||||
|
||||
type TerminusState string
|
||||
type TerminusState = clistate.TerminusState
|
||||
|
||||
const (
|
||||
NotInstalled TerminusState = "not-installed"
|
||||
Installing TerminusState = "installing"
|
||||
InstallFailed TerminusState = "install-failed"
|
||||
Uninitialized TerminusState = "uninitialized"
|
||||
Initializing TerminusState = "initializing"
|
||||
InitializeFailed TerminusState = "initialize-failed"
|
||||
TerminusRunning TerminusState = "terminus-running"
|
||||
InvalidIpAddress TerminusState = "invalid-ip-address"
|
||||
SystemError TerminusState = "system-error"
|
||||
SelfRepairing TerminusState = "self-repairing"
|
||||
IPChanging TerminusState = "ip-changing"
|
||||
IPChangeFailed TerminusState = "ip-change-failed"
|
||||
AddingNode TerminusState = "adding-node"
|
||||
RemovingNode TerminusState = "removing-node"
|
||||
Uninstalling TerminusState = "uninstalling"
|
||||
Upgrading TerminusState = "upgrading"
|
||||
DiskModifing TerminusState = "disk-modifing"
|
||||
Shutdown TerminusState = "shutdown"
|
||||
Restarting TerminusState = "restarting"
|
||||
Checking TerminusState = "checking"
|
||||
NetworkNotReady TerminusState = "network-not-ready"
|
||||
NotInstalled = clistate.NotInstalled
|
||||
Installing = clistate.Installing
|
||||
InstallFailed = clistate.InstallFailed
|
||||
Uninitialized = clistate.Uninitialized
|
||||
Initializing = clistate.Initializing
|
||||
InitializeFailed = clistate.InitializeFailed
|
||||
TerminusRunning = clistate.TerminusRunning
|
||||
InvalidIpAddress = clistate.InvalidIpAddress
|
||||
SystemError = clistate.SystemError
|
||||
SelfRepairing = clistate.SelfRepairing
|
||||
IPChanging = clistate.IPChanging
|
||||
IPChangeFailed = clistate.IPChangeFailed
|
||||
AddingNode = clistate.AddingNode
|
||||
RemovingNode = clistate.RemovingNode
|
||||
Uninstalling = clistate.Uninstalling
|
||||
Upgrading = clistate.Upgrading
|
||||
DiskModifing = clistate.DiskModifing
|
||||
Shutdown = clistate.Shutdown
|
||||
Restarting = clistate.Restarting
|
||||
Checking = clistate.Checking
|
||||
NetworkNotReady = clistate.NetworkNotReady
|
||||
)
|
||||
|
||||
func (s TerminusState) String() string {
|
||||
return string(s)
|
||||
// ValidateOp returns nil if the operation is allowed in the given
|
||||
// state, or a descriptive error otherwise. It replaces the previous
|
||||
// (TerminusState).ValidateOp method, since methods cannot be defined
|
||||
// on an alias of a type from another package.
|
||||
func ValidateOp(s TerminusState, op commands.Interface) error {
|
||||
return getValidator(s).ValidateOp(op)
|
||||
}
|
||||
|
||||
func (s TerminusState) ValidateOp(op commands.Interface) error {
|
||||
return s.getValidator().ValidateOp(op)
|
||||
}
|
||||
|
||||
func (s TerminusState) getValidator() Validator {
|
||||
func getValidator(s TerminusState) Validator {
|
||||
switch s {
|
||||
case NotInstalled:
|
||||
return &NotInstalledValidator{}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ func (i *changeIp) Execute(ctx context.Context, p any) (res any, err error) {
|
|||
klog.Error("cannot backup prev ip, ", err)
|
||||
}
|
||||
|
||||
state.CurrentState.ChangeTerminusStateTo(state.IPChanging)
|
||||
state.ChangeTerminusStateTo(state.IPChanging)
|
||||
|
||||
// remove PREV_IP_CHANGE_FAILED tag file if exists
|
||||
if _, err = os.Stat(commands.PREV_IP_CHANGE_FAILED); err == nil {
|
||||
|
|
@ -114,7 +114,7 @@ func (i *changeIp) watch(ctx context.Context) {
|
|||
// double check
|
||||
if i.tailLog() {
|
||||
klog.Info("change ip command finished, change state")
|
||||
state.CurrentState.ChangeTerminusStateTo(successState)
|
||||
state.ChangeTerminusStateTo(successState)
|
||||
return
|
||||
} else {
|
||||
klog.Warning("check log file, process not succeed")
|
||||
|
|
@ -123,12 +123,12 @@ func (i *changeIp) watch(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
klog.Warning("change ip killed")
|
||||
state.CurrentState.ChangeTerminusStateTo(state.SystemError)
|
||||
state.ChangeTerminusStateTo(state.SystemError)
|
||||
return
|
||||
case <-ticker.C:
|
||||
if i.tailLog() {
|
||||
klog.Info("watch log succeed")
|
||||
state.CurrentState.ChangeTerminusStateTo(successState)
|
||||
state.ChangeTerminusStateTo(successState)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -175,7 +175,7 @@ func (i *changeIp) tailLog() (finished bool) {
|
|||
func (i *changeIp) setFailedState() {
|
||||
if i.isRetryChange {
|
||||
klog.Error("retry ip change failed")
|
||||
state.CurrentState.ChangeTerminusStateTo(state.SystemError)
|
||||
state.ChangeTerminusStateTo(state.SystemError)
|
||||
} else {
|
||||
// create a ip change failed tag file, make the ip-change command can
|
||||
// be resumed if the device get reboot
|
||||
|
|
@ -184,6 +184,6 @@ func (i *changeIp) setFailedState() {
|
|||
klog.Error("write ip change failed tag file error, ", err)
|
||||
}
|
||||
|
||||
state.CurrentState.ChangeTerminusStateTo(state.IPChangeFailed)
|
||||
state.ChangeTerminusStateTo(state.IPChangeFailed)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func (i *install) watch(ctx context.Context) {
|
|||
return
|
||||
} else {
|
||||
state.CurrentState.InstallingState = state.Failed
|
||||
state.CurrentState.ChangeTerminusStateTo(state.InstallFailed)
|
||||
state.ChangeTerminusStateTo(state.InstallFailed)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func (i *uninstall) Execute(ctx context.Context, p any) (res any, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
state.CurrentState.ChangeTerminusStateTo(state.Uninstalling)
|
||||
state.ChangeTerminusStateTo(state.Uninstalling)
|
||||
state.CurrentState.UninstallingState = state.InProgress
|
||||
state.CurrentState.UninstallingProgress = "1%"
|
||||
state.CurrentState.UninstallingProgressNum = 1
|
||||
|
|
@ -67,7 +67,7 @@ func (i *uninstall) watch(ctx context.Context) {
|
|||
}
|
||||
|
||||
if !installed {
|
||||
state.CurrentState.ChangeTerminusStateTo(state.NotInstalled)
|
||||
state.ChangeTerminusStateTo(state.NotInstalled)
|
||||
state.CurrentState.UninstallingState = state.Completed
|
||||
state.CurrentState.UninstallingProgress = "100%"
|
||||
state.CurrentState.UninstallingProgressNum = 100
|
||||
|
|
@ -124,7 +124,7 @@ func (i *uninstall) tailLog() (finished bool) {
|
|||
state.CurrentState.UninstallingState = state.InProgress
|
||||
} else {
|
||||
state.CurrentState.UninstallingState = state.Completed
|
||||
state.CurrentState.ChangeTerminusStateTo(state.NotInstalled)
|
||||
state.ChangeTerminusStateTo(state.NotInstalled)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ func GetNodesPressure(ctx context.Context, client kubernetes.Interface) (map[str
|
|||
for _, node := range nodes.Items {
|
||||
for _, condition := range node.Status.Conditions {
|
||||
if condition.Type != corev1.NodeReady && condition.Status == corev1.ConditionTrue {
|
||||
status[node.Name] = append(status[node.Name], NodePressure{Type: condition.Type, Message: condition.Message})
|
||||
status[node.Name] = append(status[node.Name], NodePressure{Type: string(condition.Type), Message: condition.Message})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
clistate "github.com/beclab/Olares/cli/pkg/daemon/state"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
|
|
@ -15,7 +15,9 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
type NodePressure struct {
|
||||
Type corev1.NodeConditionType `json:"type"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
// NodePressure is a daemon-local alias of the canonical wire type
|
||||
// shared with the olares-cli module. The Type field is plain string
|
||||
// rather than corev1.NodeConditionType because the JSON wire format
|
||||
// uses a string anyway and the cli module avoids depending on
|
||||
// k8s.io/api/core/v1.
|
||||
type NodePressure = clistate.NodePressure
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ To get detailed help for any command, run `olares-cli help`.
|
|||
| `prepare` | `olares-cli prepare [options]` | Prepares the environment for the installation process, including setting up essential services and configurations of Olares. |
|
||||
| `release` | `olares-cli release [options]` | Packages Olares installation resources for distribution or deployment. |
|
||||
| `start` | `olares-cli start [options]` | Starts Olares services and components. |
|
||||
| `status` | `olares-cli status [options]` | Queries the local olaresd daemon and prints the current Olares system status. |
|
||||
| `stop` | `olares-cli stop [options]` | Stops Olares services and components. |
|
||||
| `uninstall` | `olares-cli uninstall [options]` | Uninstalls Olares completely, or roll back the installation to a specific phase. |
|
||||
| `upgrade` | `olares-cli upgrade <subcommand> [options]` | Upgrades Olares and checks upgrade readiness and compatibility.|
|
||||
|
|
|
|||
162
docs/developer/install/cli/status.md
Normal file
162
docs/developer/install/cli/status.md
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
# `status`
|
||||
|
||||
## Synopsis
|
||||
|
||||
The `status` command prints the current Olares system state by calling the local olaresd daemon's `/system/status` HTTP endpoint.
|
||||
|
||||
The endpoint is bound to `127.0.0.1:18088` and only accepts loopback traffic, so this command must run on the same host as the daemon (typically the master node).
|
||||
|
||||
```bash
|
||||
olares-cli status [options]
|
||||
```
|
||||
|
||||
By default, the output is grouped into human-readable sections:
|
||||
|
||||
- **Olares**: installation lifecycle, version, names, key timestamps.
|
||||
- **System**: hardware and OS facts about the host.
|
||||
- **Network**: wired/Wi-Fi connectivity, internal and external IP addresses.
|
||||
- **Install / Uninstall**: progress of an in-flight install or uninstall.
|
||||
- **Upgrade**: progress of an in-flight upgrade (download and install phases).
|
||||
- **Logs collection**: state of the most recent log collection job.
|
||||
- **Pressures**: active kubelet node pressure conditions, if any.
|
||||
- **Other**: FRP, container mode, etc.
|
||||
|
||||
Use `--json` to receive the raw payload returned by olaresd, suitable for scripting or piping to tools like `jq`.
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Usage | Required | Default |
|
||||
|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------|----------|-----------------------------|
|
||||
| `--endpoint` | Base URL of the local olaresd daemon. Override only when olaresd is bound to a non-default address. | No | `http://127.0.0.1:18088` |
|
||||
| `--json` | Print the raw JSON payload returned by olaresd (the `data` field), suitable for piping to tools like `jq`. | No | `false` |
|
||||
| `--timeout` | Maximum time to wait for the olaresd response. | No | `5s` |
|
||||
| `--help` | Show command help. | No | N/A |
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Pretty-printed grouped report (default)
|
||||
olares-cli status
|
||||
|
||||
# Raw JSON payload (forwarded verbatim from olaresd)
|
||||
olares-cli status --json | jq
|
||||
|
||||
# Custom daemon endpoint and longer timeout
|
||||
olares-cli status --endpoint http://127.0.0.1:18088 --timeout 10s
|
||||
```
|
||||
|
||||
## Field reference
|
||||
|
||||
The table below lists the fields returned by `olaresd` (the `data` object of the JSON response) and the labels they appear under in the grouped output.
|
||||
|
||||
### Olares
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|-------------------|--------------------|----------------------------------------------------------------------------------------------------------|
|
||||
| State | `terminusState` | High-level system state. See [State values](#state-values). |
|
||||
| Olaresd state | `terminusdState` | Lifecycle of the olaresd daemon itself: `initialize` while bootstrapping, `running` once ready. |
|
||||
| Name | `terminusName` | Olares ID of the admin user, e.g. `alice@olares.cn`. |
|
||||
| Version | `terminusVersion` | Installed Olares version (semver). |
|
||||
| Olaresd version | `olaresdVersion` | Running olaresd binary version. Useful for spotting drift after partial upgrades. |
|
||||
| Installed at | `installedTime` | Unix epoch (seconds) when Olares finished installing on this node. |
|
||||
| Initialized at | `initializedTime` | Unix epoch (seconds) when the admin user finished initial activation. |
|
||||
|
||||
### System
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|-------------|---------------|----------------------------------------------------------------------|
|
||||
| Device | `device_name` | User-friendly device or chassis name. |
|
||||
| Hostname | `host_name` | Kernel hostname. |
|
||||
| OS | `os_type` / `os_arch` / `os_info` | OS family, CPU architecture, distro string. |
|
||||
| OS version | `os_version` | OS version string, e.g. `22.04`. |
|
||||
| CPU | `cpu_info` | CPU model name. |
|
||||
| Memory | `memory` | Total physical memory, formatted as `<N> G`. |
|
||||
| Disk | `disk` | Total filesystem size of the data partition, formatted as `<N> G`. |
|
||||
| GPU | `gpu_info` | GPU model name when one is detected. |
|
||||
|
||||
### Network
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|-------------|------------------|----------------------------------------------------------------------|
|
||||
| Wired | `wiredConnected` | `yes` when an Ethernet connection is active. |
|
||||
| Wi-Fi | `wifiConnected` | `yes` when the active default route is over Wi-Fi. |
|
||||
| Wi-Fi SSID | `wifiSSID` | SSID of the connected Wi-Fi network, when applicable. |
|
||||
| Host IP | `hostIp` | Internal LAN IPv4 address used by Olares to reach other nodes. |
|
||||
| External IP | `externalIp` | Public IPv4 address as observed by an external probe (refreshed at most once per minute). |
|
||||
|
||||
### Install / Uninstall
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|----------------|-------------------------|-------------------------------------------------------------------------------|
|
||||
| Installing | `installingState` | Lifecycle of the in-flight install: `in-progress`, `completed`, `failed`. |
|
||||
| | `installingProgress` | Free-form description of the current install step (shown as the inline note). |
|
||||
| Uninstalling | `uninstallingState` | Lifecycle of the in-flight uninstall. |
|
||||
| | `uninstallingProgress` | Free-form description of the current uninstall step. |
|
||||
|
||||
### Upgrade
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|----------------|--------------------------------|-----------------------------------------------------------------------------------|
|
||||
| Target | `upgradingTarget` | Target version of the in-flight upgrade. |
|
||||
| State | `upgradingState` | Lifecycle of the install phase of the upgrade. |
|
||||
| | `upgradingProgress` | Free-form progress message (inline note). |
|
||||
| Step | `upgradingStep` | Name of the current upgrade step. |
|
||||
| Last error | `upgradingError` | Most recent error from the install phase. |
|
||||
| Download state | `upgradingDownloadState` | Lifecycle of the download phase. |
|
||||
| | `upgradingDownloadProgress` | Free-form progress message for the download (inline note). |
|
||||
| Download step | `upgradingDownloadStep` | Name of the current download step. |
|
||||
| Download error | `upgradingDownloadError` | Most recent error from the download phase. |
|
||||
| Retry count | `upgradingRetryNum` | Number of times the upgrader has retried after a transient failure (only shown when > 0). |
|
||||
| Next retry at | `upgradingNextRetryAt` | Wall-clock time at which the next retry will fire (only shown when set). |
|
||||
|
||||
### Logs collection
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|-------------|--------------------------|------------------------------------------------------------------|
|
||||
| State | `collectingLogsState` | Lifecycle of the most recent log collection job triggered through olaresd. |
|
||||
| | `collectingLogsError` | Error from the most recent log collection job (inline note). |
|
||||
|
||||
### Pressures
|
||||
|
||||
The `pressures` array lists kubelet node-condition pressures that are currently true on this node. The grouped output shows `(none)` when the node is healthy.
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|---------|-----------|------------------------------------------------------------------------|
|
||||
| Type | `type` | Kubernetes node condition type, e.g. `MemoryPressure`, `DiskPressure`. |
|
||||
| Message | `message` | Human-readable explanation provided by kubelet. |
|
||||
|
||||
### Other
|
||||
|
||||
| Field | JSON key | Meaning |
|
||||
|----------------|---------------------|------------------------------------------------------------------------|
|
||||
| FRP enabled | `frpEnable` | Whether the FRP-based reverse tunnel is turned on (sourced from `FRP_ENABLE`). |
|
||||
| FRP server | `defaultFrpServer` | FRP server address (sourced from `FRP_SERVER`). |
|
||||
| Container mode | `containerMode` | Set when olaresd is running inside a container (sourced from `CONTAINER_MODE`). |
|
||||
|
||||
## State values
|
||||
|
||||
The `terminusState` field can take one of the following values. The CLI sources its descriptions from the same enumeration, so the table below stays in sync with the CLI output.
|
||||
|
||||
| Value | Meaning |
|
||||
|----------------------|--------------------------------------------------------------------------|
|
||||
| `checking` | olaresd has not finished the first status probe yet. |
|
||||
| `network-not-ready` | No usable internal IPv4 address detected. |
|
||||
| `not-installed` | Olares is not installed on this node. |
|
||||
| `installing` | Olares is currently being installed. |
|
||||
| `install-failed` | The most recent install attempt failed. |
|
||||
| `uninitialized` | Olares is installed but the admin user has not been activated yet. |
|
||||
| `initializing` | The admin user activation is in progress. |
|
||||
| `initialize-failed` | The admin user activation failed. |
|
||||
| `terminus-running` | Olares is running normally. |
|
||||
| `restarting` | The node was just restarted; status will stabilize shortly. |
|
||||
| `invalid-ip-address` | The node IP changed since install; run `change-ip` to recover. |
|
||||
| `ip-changing` | A `change-ip` operation is in progress. |
|
||||
| `ip-change-failed` | The most recent `change-ip` attempt failed. |
|
||||
| `system-error` | One or more critical pods are not running. |
|
||||
| `self-repairing` | olaresd is attempting automatic recovery. |
|
||||
| `adding-node` | A worker node is being joined. |
|
||||
| `removing-node` | A worker node is being removed. |
|
||||
| `uninstalling` | Olares is being uninstalled. |
|
||||
| `upgrading` | An upgrade is being applied. |
|
||||
| `disk-modifing` | The storage layout is being modified. |
|
||||
| `shutdown` | The system is shutting down. |
|
||||
|
|
@ -60,6 +60,7 @@ Olares 命令行工具使用如下语法:
|
|||
| `prepare` | `olares-cli prepare [选项]` | 为安装过程准备环境,包括设置 Olares 的基础服务和配置。 |
|
||||
| `release` | `olares-cli release [选项]` | 打包 Olares 安装资源以供分发或部署。|
|
||||
| `start` | `olares-cli start [选项]` | 启动 Olares 服务和组件。 |
|
||||
| `status` | `olares-cli status [选项]` | 查询本机 olaresd 守护进程,输出当前 Olares 系统状态。 |
|
||||
| `stop` | `olares-cli stop [选项]` | 停止 Olares 服务和组件。 |
|
||||
| `uninstall` | `olares-cli uninstall [选项]` | 完全卸载 Olares,或将安装回滚到特定阶段。 |
|
||||
| `upgrade` | `olares-cli upgrade <子命令> [选项]` | 升级 Olares,检查升级准备情况与兼容性。 |
|
||||
|
|
|
|||
162
docs/zh/developer/install/cli/status.md
Normal file
162
docs/zh/developer/install/cli/status.md
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
# `status`
|
||||
|
||||
## 命令说明
|
||||
|
||||
`status` 命令通过调用本机 olaresd 守护进程的 `/system/status` HTTP 接口,输出当前 Olares 系统的状态。
|
||||
|
||||
该接口绑定在 `127.0.0.1:18088`,仅接受本地回环流量,因此 `status` 命令必须与 olaresd 运行在同一台机器(通常是主节点)上。
|
||||
|
||||
```bash
|
||||
olares-cli status [选项]
|
||||
```
|
||||
|
||||
默认输出按以下分组展示,便于人工阅读:
|
||||
|
||||
- **Olares**:安装生命周期、版本、用户名、关键时间戳。
|
||||
- **System**:主机的硬件和操作系统信息。
|
||||
- **Network**:有线/Wi-Fi 连接状态、内/外网 IP 地址。
|
||||
- **Install / Uninstall**:正在进行的安装或卸载进度。
|
||||
- **Upgrade**:正在进行的升级进度(包括下载阶段和安装阶段)。
|
||||
- **Logs collection**:最近一次日志收集任务的状态。
|
||||
- **Pressures**:节点上当前激活的 kubelet 节点压力条件(若有)。
|
||||
- **Other**:FRP、容器模式等其他信息。
|
||||
|
||||
加上 `--json` 可以输出 olaresd 返回的原始 JSON,便于脚本化处理或与 `jq` 等工具配合使用。
|
||||
|
||||
## 选项
|
||||
|
||||
| 选项 | 用途 | 是否必需 | 默认值 |
|
||||
|--------------|----------------------------------------------------------------------------------------------|----------|--------------------------|
|
||||
| `--endpoint` | 本机 olaresd 守护进程的基础 URL。仅当 olaresd 监听在非默认地址时才需要修改。 | 否 | `http://127.0.0.1:18088` |
|
||||
| `--json` | 直接输出 olaresd 返回的原始 JSON(即响应中的 `data` 字段),适合配合 `jq` 等工具使用。 | 否 | `false` |
|
||||
| `--timeout` | 等待 olaresd 响应的最长时间。 | 否 | `5s` |
|
||||
| `--help` | 显示命令帮助。 | 否 | 无 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
```bash
|
||||
# 默认输出:分组的人工可读报表
|
||||
olares-cli status
|
||||
|
||||
# 原始 JSON 输出,原样转发自 olaresd
|
||||
olares-cli status --json | jq
|
||||
|
||||
# 指定守护进程地址并延长超时时间
|
||||
olares-cli status --endpoint http://127.0.0.1:18088 --timeout 10s
|
||||
```
|
||||
|
||||
## 字段参考
|
||||
|
||||
下表列出 olaresd 返回的字段(即 JSON 响应中 `data` 对象的字段),以及它们在分组输出中显示的标签。
|
||||
|
||||
### Olares
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|------------------|--------------------|--------------------------------------------------------------------------------------------|
|
||||
| State | `terminusState` | 系统的高层状态,详见 [状态值列表](#状态值列表)。 |
|
||||
| Olaresd state | `terminusdState` | olaresd 守护进程自身的生命周期:启动初始化时为 `initialize`,初始化完成后为 `running`。 |
|
||||
| Name | `terminusName` | 管理员的 Olares ID,例如 `alice@olares.cn`。 |
|
||||
| Version | `terminusVersion` | 已安装的 Olares 版本(语义化版本号)。 |
|
||||
| Olaresd version | `olaresdVersion` | 当前运行的 olaresd 二进制版本。可用于排查升级后的版本漂移。 |
|
||||
| Installed at | `installedTime` | Olares 安装完成时间(Unix 时间戳,单位秒)。 |
|
||||
| Initialized at | `initializedTime` | 管理员完成初始激活的时间(Unix 时间戳,单位秒)。 |
|
||||
|
||||
### System
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|------------|---------------|--------------------------------------------|
|
||||
| Device | `device_name` | 用户友好的设备/机型名称。 |
|
||||
| Hostname | `host_name` | 内核报告的主机名。 |
|
||||
| OS | `os_type` / `os_arch` / `os_info` | 操作系统类型、CPU 架构、发行版描述。 |
|
||||
| OS version | `os_version` | 操作系统版本号,例如 `22.04`。 |
|
||||
| CPU | `cpu_info` | CPU 型号。 |
|
||||
| Memory | `memory` | 物理内存总量,格式为 `<N> G`。 |
|
||||
| Disk | `disk` | 数据分区的文件系统总容量,格式为 `<N> G`。 |
|
||||
| GPU | `gpu_info` | 检测到的 GPU 型号(若有)。 |
|
||||
|
||||
### Network
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|-------------|------------------|---------------------------------------------------------------------|
|
||||
| Wired | `wiredConnected` | 检测到有线连接时为 `yes`。 |
|
||||
| Wi-Fi | `wifiConnected` | 默认路由走 Wi-Fi 时为 `yes`。 |
|
||||
| Wi-Fi SSID | `wifiSSID` | 已连接 Wi-Fi 的 SSID。 |
|
||||
| Host IP | `hostIp` | Olares 用于互联的内网 IPv4 地址。 |
|
||||
| External IP | `externalIp` | 通过外部探测获取的公网 IPv4 地址(每分钟最多刷新一次)。 |
|
||||
|
||||
### Install / Uninstall
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|---------------|-------------------------|-------------------------------------------------------------------|
|
||||
| Installing | `installingState` | 进行中的安装任务的生命周期:`in-progress`、`completed`、`failed`。 |
|
||||
| | `installingProgress` | 当前安装步骤的描述(在分组输出中以括号形式跟随显示)。 |
|
||||
| Uninstalling | `uninstallingState` | 进行中的卸载任务的生命周期。 |
|
||||
| | `uninstallingProgress` | 当前卸载步骤的描述。 |
|
||||
|
||||
### Upgrade
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|----------------|--------------------------------|----------------------------------------------------------------------------|
|
||||
| Target | `upgradingTarget` | 进行中升级的目标版本。 |
|
||||
| State | `upgradingState` | 升级安装阶段的生命周期。 |
|
||||
| | `upgradingProgress` | 升级安装阶段的进度描述(括号显示)。 |
|
||||
| Step | `upgradingStep` | 当前升级步骤的名称。 |
|
||||
| Last error | `upgradingError` | 升级安装阶段最近一次报错信息。 |
|
||||
| Download state | `upgradingDownloadState` | 升级下载阶段的生命周期。 |
|
||||
| | `upgradingDownloadProgress` | 升级下载阶段的进度描述(括号显示)。 |
|
||||
| Download step | `upgradingDownloadStep` | 当前下载步骤的名称。 |
|
||||
| Download error | `upgradingDownloadError` | 升级下载阶段最近一次报错信息。 |
|
||||
| Retry count | `upgradingRetryNum` | 升级被自动重试的次数(仅当大于 0 时显示)。 |
|
||||
| Next retry at | `upgradingNextRetryAt` | 下一次重试的预定时间(仅当存在时显示)。 |
|
||||
|
||||
### Logs collection
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|------------|--------------------------|-----------------------------------------------------------------------|
|
||||
| State | `collectingLogsState` | 通过 olaresd 触发的最近一次日志收集任务的生命周期。 |
|
||||
| | `collectingLogsError` | 最近一次日志收集任务的错误信息(括号显示)。 |
|
||||
|
||||
### Pressures
|
||||
|
||||
`pressures` 数组列出当前节点上为真的 kubelet 节点压力条件。当节点健康时,分组输出中会显示 `(none)`。
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|---------|-----------|---------------------------------------------------------------------|
|
||||
| Type | `type` | Kubernetes 节点条件类型,例如 `MemoryPressure`、`DiskPressure`。 |
|
||||
| Message | `message` | kubelet 给出的可读说明。 |
|
||||
|
||||
### Other
|
||||
|
||||
| 字段 | JSON Key | 含义 |
|
||||
|----------------|---------------------|---------------------------------------------------------------------|
|
||||
| FRP enabled | `frpEnable` | FRP 反向通道是否启用(来自环境变量 `FRP_ENABLE`)。 |
|
||||
| FRP server | `defaultFrpServer` | FRP 服务器地址(来自环境变量 `FRP_SERVER`)。 |
|
||||
| Container mode | `containerMode` | olaresd 运行在容器内时设置(来自环境变量 `CONTAINER_MODE`)。 |
|
||||
|
||||
## 状态值列表
|
||||
|
||||
`terminusState` 字段可能取以下值。CLI 也使用同一份枚举生成描述,因此下表始终与 CLI 输出保持一致。
|
||||
|
||||
| 取值 | 含义 |
|
||||
|-----------------------|-----------------------------------------------------------------|
|
||||
| `checking` | olaresd 还未完成首次状态探测。 |
|
||||
| `network-not-ready` | 未检测到可用的内网 IPv4 地址。 |
|
||||
| `not-installed` | 当前节点未安装 Olares。 |
|
||||
| `installing` | Olares 正在安装中。 |
|
||||
| `install-failed` | 最近一次安装失败。 |
|
||||
| `uninitialized` | Olares 已安装,但管理员账户尚未激活。 |
|
||||
| `initializing` | 管理员账户正在激活中。 |
|
||||
| `initialize-failed` | 管理员账户激活失败。 |
|
||||
| `terminus-running` | Olares 运行正常。 |
|
||||
| `restarting` | 节点刚刚重启,状态会在短时间内稳定。 |
|
||||
| `invalid-ip-address` | 节点 IP 已变更,需要执行 `change-ip` 恢复。 |
|
||||
| `ip-changing` | `change-ip` 操作正在进行。 |
|
||||
| `ip-change-failed` | 最近一次 `change-ip` 操作失败。 |
|
||||
| `system-error` | 关键 Pod 未正常运行。 |
|
||||
| `self-repairing` | olaresd 正在尝试自动修复。 |
|
||||
| `adding-node` | 正在加入 worker 节点。 |
|
||||
| `removing-node` | 正在移除 worker 节点。 |
|
||||
| `uninstalling` | Olares 正在卸载中。 |
|
||||
| `upgrading` | 升级正在执行中。 |
|
||||
| `disk-modifing` | 存储布局正在调整中。 |
|
||||
| `shutdown` | 系统正在关机。 |
|
||||
Loading…
Reference in a new issue