Add HTTP-like status codes, remove unused fields

pull/8868/head
Thomas Stromberg 2020-07-29 13:57:06 -07:00
parent 649b3789d9
commit 59d0d7335a
2 changed files with 102 additions and 50 deletions

View File

@ -52,30 +52,56 @@ var output string
var layout string var layout string
const ( const (
// # Additional states used by kubeconfig: // Additional legacy states:
// Configured means configured // Configured means configured
Configured = "Configured" // ~state.Saved Configured = "Configured" // ~state.Saved
// Misconfigured means misconfigured // Misconfigured means misconfigured
Misconfigured = "Misconfigured" // ~state.Error Misconfigured = "Misconfigured" // ~state.Error
// Nonexistent means the resource does not exist
// # Additional states used for clarity:
// Nonexistent means nonexistent
Nonexistent = "Nonexistent" // ~state.None Nonexistent = "Nonexistent" // ~state.None
// Irrelevant is used for statuses that aren't meaningful for worker nodes // Irrelevant is used for statuses that aren't meaningful for worker nodes
Irrelevant = "Irrelevant" Irrelevant = "Irrelevant"
// New status modes // New status modes, based roughly on HTTP/SMTP standards
OK = "OK" // running + passes health checks // 1xx signifies a transitional state. If retried, it will soon return a 2xx, 4xx, or 5xx
Error = "Error" // running but has an error Starting = 100
Warning = "Warning" // running but has warnings Pausing = 101
Unpausing = 102
Stopping = 110
Deleting = 120
Starting = "Starting" // 2xx signifies that the API Server is able to service requests
Pausing = "Pausing" OK = 200
Unpausing = "Unpausing" Warning = 203
Stopping = "Stopping"
Deleting = "Deleting" // 4xx signifies an error that requires help from the client to resolve
NotFound = 404
Stopped = 405
Paused = 418 // I'm a teapot!
// 5xx signifies a server-side error (that may be retryable)
Error = 500
Unknown = 520
)
var (
codeNames = map[int]string{
100: "Starting",
101: "Pausing",
102: "Unpausing",
110: "Stopping",
103: "Deleting",
200: "OK",
203: "Warning",
404: "NotFound",
405: "Stopped",
418: "Paused",
500: "Error",
520: "Unknown",
}
) )
// Status holds string representations of component states // Status holds string representations of component states
@ -105,17 +131,20 @@ type NodeState struct {
// BaseState holds a component state representation, such as "apiserver" or "kubeconfig" // BaseState holds a component state representation, such as "apiserver" or "kubeconfig"
type BaseState struct { type BaseState struct {
// Name is the name of the object
Name string Name string
Kind string `json:",omitempty"`
Condition string // StatusCode is an HTTP-like status code for this object
ConditionDetail string `json:",omitempty"` // Not yet implemented StatusCode int
// Name is a human-readable name for the status code
StatusName string
// StatusDetail is long human-readable string describing why this particular status code was chosen
StatusDetail string `json:",omitempty"` // Not yet implemented
Step string `json:",omitempty"` // Step is which workflow step the object is at.
Step string `json:",omitempty"`
// StepDetail is a long human-readable string describing the step
StepDetail string `json:",omitempty"` StepDetail string `json:",omitempty"`
Errors []string `json:",omitempty"` // Not yet implemented
Warnings []string `json:",omitempty"` // Not yet implemented
} }
const ( const (
@ -162,7 +191,7 @@ var statusCmd = &cobra.Command{
exit.WithError("retrieving node", err) exit.WithError("retrieving node", err)
} }
st, err := status(api, *cc, *n) st, err := nodeStatus(api, *cc, *n)
if err != nil { if err != nil {
glog.Errorf("status error: %v", err) glog.Errorf("status error: %v", err)
} }
@ -171,7 +200,7 @@ var statusCmd = &cobra.Command{
for _, n := range cc.Nodes { for _, n := range cc.Nodes {
machineName := driver.MachineName(*cc, n) machineName := driver.MachineName(*cc, n)
glog.Infof("checking status of %s ...", machineName) glog.Infof("checking status of %s ...", machineName)
st, err := status(api, *cc, n) st, err := nodeStatus(api, *cc, n)
glog.Infof("%s status: %+v", machineName, st) glog.Infof("%s status: %+v", machineName, st)
if err != nil { if err != nil {
@ -210,6 +239,7 @@ var statusCmd = &cobra.Command{
}, },
} }
// exitCode calcluates the appropriate exit code given a set of status messages
func exitCode(statuses []*Status) int { func exitCode(statuses []*Status) int {
c := 0 c := 0
for _, st := range statuses { for _, st := range statuses {
@ -226,7 +256,8 @@ func exitCode(statuses []*Status) int {
return c return c
} }
func status(api libmachine.API, cc config.ClusterConfig, n config.Node) (*Status, error) { // nodeStatus looks up the status of a node
func nodeStatus(api libmachine.API, cc config.ClusterConfig, n config.Node) (*Status, error) {
controlPlane := n.ControlPlane controlPlane := n.ControlPlane
name := driver.MachineName(cc, n) name := driver.MachineName(cc, n)
@ -363,6 +394,7 @@ func statusJSON(st []*Status, w io.Writer) error {
return err return err
} }
// readEventLog reads cloudevent logs from $MINIKUBE_HOME/profiles/<name>/events.json
func readEventLog(name string) ([]cloudevents.Event, time.Time, error) { func readEventLog(name string) ([]cloudevents.Event, time.Time, error) {
path := localpath.EventLog(name) path := localpath.EventLog(name)
@ -389,38 +421,43 @@ func readEventLog(name string) ([]cloudevents.Event, time.Time, error) {
return events, st.ModTime(), nil return events, st.ModTime(), nil
} }
// clusterState converts Status structs into a ClusterState struct
func clusterState(sts []*Status) ClusterState { func clusterState(sts []*Status) ClusterState {
cs := ClusterState{ cs := ClusterState{
BinaryVersion: version.GetVersion(), BinaryVersion: version.GetVersion(),
BaseState: BaseState{ BaseState: BaseState{
Name: ClusterFlagValue(), Name: ClusterFlagValue(),
Kind: "cluster", StatusCode: statusCode(sts[0].APIServer),
Condition: sts[0].APIServer,
}, },
Components: map[string]BaseState{ Components: map[string]BaseState{
"kubeconfig": {Name: "kubeconfig", Condition: newCondition(sts[0].Kubeconfig)}, "kubeconfig": {Name: "kubeconfig", StatusCode: statusCode(sts[0].Kubeconfig)},
}, },
} }
for _, st := range sts { for _, st := range sts {
ns := NodeState{ ns := NodeState{
BaseState: BaseState{ BaseState: BaseState{
Name: st.Name, Name: st.Name,
Condition: newCondition(st.Host), StatusCode: statusCode(st.Host),
}, },
Components: map[string]BaseState{ Components: map[string]BaseState{
"kubelet": {Name: "kubelet", Kind: "service", Condition: newCondition(st.Kubelet)}, "kubelet": {Name: "kubelet", StatusCode: statusCode(st.Kubelet)},
"apiserver": {Name: "apiserver", Kind: "service", Condition: newCondition(st.APIServer)},
}, },
} }
if st.Worker { if st.APIServer != Irrelevant {
ns.Kind = "worker" ns.Components["apiserver"] = BaseState{Name: "apiserver", StatusCode: statusCode(st.APIServer)}
} else {
ns.Kind = "control-plane"
} }
// Convert status codes to status names
ns.StatusName = codeNames[ns.StatusCode]
for k, v := range ns.Components {
v.StatusName = codeNames[v.StatusCode]
ns.Components[k] = v
}
cs.Nodes = append(cs.Nodes, ns) cs.Nodes = append(cs.Nodes, ns)
} }
@ -430,7 +467,7 @@ func clusterState(sts []*Status) ClusterState {
return cs return cs
} }
var transientCondition string transientCode := 0
var finalStep map[string]string var finalStep map[string]string
for _, ev := range evs { for _, ev := range evs {
@ -445,21 +482,22 @@ func clusterState(sts []*Status) ClusterState {
switch data["name"] { switch data["name"] {
case string(register.InitialSetup): case string(register.InitialSetup):
transientCondition = Starting transientCode = Starting
case string(register.Done): case string(register.Done):
transientCondition = "" transientCode = 0
case string(register.Stopping): case string(register.Stopping):
transientCondition = Stopping glog.Infof("%q == %q", data["name"], register.Stopping)
transientCode = Stopping
case string(register.Deleting): case string(register.Deleting):
transientCondition = Deleting transientCode = Deleting
case string(register.Pausing): case string(register.Pausing):
transientCondition = Pausing transientCode = Pausing
case string(register.Unpausing): case string(register.Unpausing):
transientCondition = Unpausing transientCode = Unpausing
} }
finalStep = data finalStep = data
glog.Infof("transient condition %q for step: %+v", transientCondition, data) glog.Infof("transient code %d (%q) for step: %+v", transientCode, codeNames[transientCode], data)
} }
} }
@ -469,24 +507,34 @@ func clusterState(sts []*Status) ClusterState {
} else { } else {
cs.Step = strings.TrimSpace(finalStep["name"]) cs.Step = strings.TrimSpace(finalStep["name"])
cs.StepDetail = strings.TrimSpace(finalStep["message"]) cs.StepDetail = strings.TrimSpace(finalStep["message"])
if transientCondition != "" { if transientCode != 0 {
cs.Condition = transientCondition cs.StatusCode = transientCode
} }
} }
} }
cs.StatusName = codeNames[cs.StatusCode]
return cs return cs
} }
func newCondition(st string) string { // statusCode returns a status code number given a name
func statusCode(st string) int {
// legacy names
switch st { switch st {
case "Running", "Configured": case "Running", "Configured":
return OK return OK
case "Misconfigured": case "Misconfigured":
return Error return Error
default:
return st
} }
// new names
for code, name := range codeNames {
if name == st {
return code
}
}
return Unknown
} }
func clusterStatusJSON(statuses []*Status, w io.Writer) error { func clusterStatusJSON(statuses []*Status, w io.Writer) error {

View File

@ -60,6 +60,7 @@ func SetEventLogPath(path string) {
eventFile = f eventFile = f
} }
// cloudEvent creates a CloudEvent from a log object & associated data
func cloudEvent(log Log, data map[string]string) cloudevents.Event { func cloudEvent(log Log, data map[string]string) cloudevents.Event {
event := cloudevents.NewEvent() event := cloudevents.NewEvent()
event.SetSource("https://minikube.sigs.k8s.io/") event.SetSource("https://minikube.sigs.k8s.io/")
@ -72,6 +73,7 @@ func cloudEvent(log Log, data map[string]string) cloudevents.Event {
return event return event
} }
// print JSON output to configured writer
func printAsCloudEvent(log Log, data map[string]string) { func printAsCloudEvent(log Log, data map[string]string) {
event := cloudEvent(log, data) event := cloudEvent(log, data)
@ -83,6 +85,7 @@ func printAsCloudEvent(log Log, data map[string]string) {
fmt.Fprintln(outputFile, string(bs)) fmt.Fprintln(outputFile, string(bs))
} }
// print JSON output to configured writer, and record it to disk
func printAndRecordCloudEvent(log Log, data map[string]string) { func printAndRecordCloudEvent(log Log, data map[string]string) {
event := cloudEvent(log, data) event := cloudEvent(log, data)
@ -105,6 +108,7 @@ func storeEvent(bs []byte) {
} }
} }
// record cloud event to disk
func recordCloudEvent(log Log, data map[string]string) { func recordCloudEvent(log Log, data map[string]string) {
if eventFile == nil { if eventFile == nil {
return return