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
const (
// # Additional states used by kubeconfig:
// Additional legacy states:
// Configured means configured
Configured = "Configured" // ~state.Saved
// Misconfigured means misconfigured
Misconfigured = "Misconfigured" // ~state.Error
// # Additional states used for clarity:
// Nonexistent means nonexistent
// Nonexistent means the resource does not exist
Nonexistent = "Nonexistent" // ~state.None
// Irrelevant is used for statuses that aren't meaningful for worker nodes
Irrelevant = "Irrelevant"
// New status modes
OK = "OK" // running + passes health checks
Error = "Error" // running but has an error
Warning = "Warning" // running but has warnings
// New status modes, based roughly on HTTP/SMTP standards
// 1xx signifies a transitional state. If retried, it will soon return a 2xx, 4xx, or 5xx
Starting = 100
Pausing = 101
Unpausing = 102
Stopping = 110
Deleting = 120
Starting = "Starting"
Pausing = "Pausing"
Unpausing = "Unpausing"
Stopping = "Stopping"
Deleting = "Deleting"
// 2xx signifies that the API Server is able to service requests
OK = 200
Warning = 203
// 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
@ -105,17 +131,20 @@ type NodeState struct {
// BaseState holds a component state representation, such as "apiserver" or "kubeconfig"
type BaseState struct {
// Name is the name of the object
Name string
Kind string `json:",omitempty"`
Condition string
ConditionDetail string `json:",omitempty"` // Not yet implemented
// StatusCode is an HTTP-like status code for this object
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"`
Errors []string `json:",omitempty"` // Not yet implemented
Warnings []string `json:",omitempty"` // Not yet implemented
}
const (
@ -162,7 +191,7 @@ var statusCmd = &cobra.Command{
exit.WithError("retrieving node", err)
}
st, err := status(api, *cc, *n)
st, err := nodeStatus(api, *cc, *n)
if err != nil {
glog.Errorf("status error: %v", err)
}
@ -171,7 +200,7 @@ var statusCmd = &cobra.Command{
for _, n := range cc.Nodes {
machineName := driver.MachineName(*cc, n)
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)
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 {
c := 0
for _, st := range statuses {
@ -226,7 +256,8 @@ func exitCode(statuses []*Status) int {
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
name := driver.MachineName(cc, n)
@ -363,6 +394,7 @@ func statusJSON(st []*Status, w io.Writer) error {
return err
}
// readEventLog reads cloudevent logs from $MINIKUBE_HOME/profiles/<name>/events.json
func readEventLog(name string) ([]cloudevents.Event, time.Time, error) {
path := localpath.EventLog(name)
@ -389,38 +421,43 @@ func readEventLog(name string) ([]cloudevents.Event, time.Time, error) {
return events, st.ModTime(), nil
}
// clusterState converts Status structs into a ClusterState struct
func clusterState(sts []*Status) ClusterState {
cs := ClusterState{
BinaryVersion: version.GetVersion(),
BaseState: BaseState{
Name: ClusterFlagValue(),
Kind: "cluster",
Condition: sts[0].APIServer,
Name: ClusterFlagValue(),
StatusCode: statusCode(sts[0].APIServer),
},
Components: map[string]BaseState{
"kubeconfig": {Name: "kubeconfig", Condition: newCondition(sts[0].Kubeconfig)},
"kubeconfig": {Name: "kubeconfig", StatusCode: statusCode(sts[0].Kubeconfig)},
},
}
for _, st := range sts {
ns := NodeState{
BaseState: BaseState{
Name: st.Name,
Condition: newCondition(st.Host),
Name: st.Name,
StatusCode: statusCode(st.Host),
},
Components: map[string]BaseState{
"kubelet": {Name: "kubelet", Kind: "service", Condition: newCondition(st.Kubelet)},
"apiserver": {Name: "apiserver", Kind: "service", Condition: newCondition(st.APIServer)},
"kubelet": {Name: "kubelet", StatusCode: statusCode(st.Kubelet)},
},
}
if st.Worker {
ns.Kind = "worker"
} else {
ns.Kind = "control-plane"
if st.APIServer != Irrelevant {
ns.Components["apiserver"] = BaseState{Name: "apiserver", StatusCode: statusCode(st.APIServer)}
}
// 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)
}
@ -430,7 +467,7 @@ func clusterState(sts []*Status) ClusterState {
return cs
}
var transientCondition string
transientCode := 0
var finalStep map[string]string
for _, ev := range evs {
@ -445,21 +482,22 @@ func clusterState(sts []*Status) ClusterState {
switch data["name"] {
case string(register.InitialSetup):
transientCondition = Starting
transientCode = Starting
case string(register.Done):
transientCondition = ""
transientCode = 0
case string(register.Stopping):
transientCondition = Stopping
glog.Infof("%q == %q", data["name"], register.Stopping)
transientCode = Stopping
case string(register.Deleting):
transientCondition = Deleting
transientCode = Deleting
case string(register.Pausing):
transientCondition = Pausing
transientCode = Pausing
case string(register.Unpausing):
transientCondition = Unpausing
transientCode = Unpausing
}
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 {
cs.Step = strings.TrimSpace(finalStep["name"])
cs.StepDetail = strings.TrimSpace(finalStep["message"])
if transientCondition != "" {
cs.Condition = transientCondition
if transientCode != 0 {
cs.StatusCode = transientCode
}
}
}
cs.StatusName = codeNames[cs.StatusCode]
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 {
case "Running", "Configured":
return OK
case "Misconfigured":
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 {

View File

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