Add paused state to apiserver status

pull/5962/head
Thomas Stromberg 2020-01-22 09:48:16 -08:00
parent 09b2780fb2
commit e6999caea3
2 changed files with 108 additions and 80 deletions

View File

@ -23,8 +23,10 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/state"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config"
@ -40,16 +42,13 @@ import (
var statusFormat string var statusFormat string
var output string var output string
// KubeconfigStatus represents the kubeconfig status const (
var KubeconfigStatus = struct { // Additional states used by kubeconfig
Configured string Configured = "Configured" // analagous to state.Saved
Misconfigured string Misconfigured = "Misconfigured" // analagous to state.Error
}{ )
Configured: `Configured`,
Misconfigured: `Misconfigured`,
}
// Status represents the status // Status holds string representations of libmachine.state.State
type Status struct { type Status struct {
Host string Host string
Kubelet string Kubelet string
@ -81,7 +80,6 @@ var statusCmd = &cobra.Command{
exit.UsageT("Cannot use both --output and --format options") exit.UsageT("Cannot use both --output and --format options")
} }
var returnCode = 0
api, err := machine.NewAPIClient() api, err := machine.NewAPIClient()
if err != nil { if err != nil {
exit.WithCodeT(exit.Unavailable, "Error getting client: {{.error}}", out.V{"error": err}) exit.WithCodeT(exit.Unavailable, "Error getting client: {{.error}}", out.V{"error": err})
@ -89,81 +87,91 @@ var statusCmd = &cobra.Command{
defer api.Close() defer api.Close()
machineName := viper.GetString(config.MachineProfile) machineName := viper.GetString(config.MachineProfile)
st, err := status(api, machineName)
hostSt, err := cluster.GetHostStatus(api, machineName)
if err != nil { if err != nil {
exit.WithError("Error getting host status", err) glog.Errorf("status error: %v", err)
}
kubeletSt := state.None.String()
kubeconfigSt := state.None.String()
apiserverSt := state.None.String()
if hostSt == state.Running.String() {
clusterBootstrapper, err := getClusterBootstrapper(api, viper.GetString(cmdcfg.Bootstrapper))
if err != nil {
exit.WithError("Error getting bootstrapper", err)
}
kubeletSt, err = clusterBootstrapper.GetKubeletStatus()
if err != nil {
glog.Warningf("kubelet err: %v", err)
returnCode |= clusterNotRunningStatusFlag
} else if kubeletSt != state.Running.String() {
returnCode |= clusterNotRunningStatusFlag
}
ip, err := cluster.GetHostDriverIP(api, machineName)
if err != nil {
glog.Errorln("Error host driver ip status:", err)
}
apiserverPort, err := kubeconfig.Port(machineName)
if err != nil {
// Fallback to presuming default apiserver port
apiserverPort = constants.APIServerPort
}
apiserverSt, err = clusterBootstrapper.GetAPIServerStatus(ip, apiserverPort)
if err != nil {
glog.Errorln("Error apiserver status:", err)
} else if apiserverSt != state.Running.String() {
returnCode |= clusterNotRunningStatusFlag
}
ks, err := kubeconfig.IsClusterInConfig(ip, machineName)
if err != nil {
glog.Errorln("Error kubeconfig status:", err)
}
if ks {
kubeconfigSt = KubeconfigStatus.Configured
} else {
kubeconfigSt = KubeconfigStatus.Misconfigured
returnCode |= k8sNotRunningStatusFlag
}
} else {
returnCode |= minikubeNotRunningStatusFlag
}
status := Status{
Host: hostSt,
Kubelet: kubeletSt,
APIServer: apiserverSt,
Kubeconfig: kubeconfigSt,
} }
switch strings.ToLower(output) { switch strings.ToLower(output) {
case "text": case "text":
printStatusText(status) printStatusText(st)
case "json": case "json":
printStatusJSON(status) printStatusJSON(st)
default: default:
exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output)) exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output))
} }
os.Exit(returnCode) os.Exit(exitCode(st))
}, },
} }
func exitCode(st *Status) int {
c := 0
if st.Host != state.Running.String() {
c |= minikubeNotRunningStatusFlag
}
if st.APIServer != state.Running.String() || st.Kubelet != state.Running.String() {
c |= clusterNotRunningStatusFlag
}
if st.Kubeconfig != Configured {
c |= k8sNotRunningStatusFlag
}
return c
}
func status(api libmachine.API, name string) (*Status, error) {
st := &Status{}
hs, err := cluster.GetHostStatus(api, name)
if err != nil {
return st, errors.Wrap(err, "host")
}
st.Host = hs
if st.Host != state.Running.String() {
return st, nil
}
bs, err := getClusterBootstrapper(api, viper.GetString(cmdcfg.Bootstrapper))
if err != nil {
return st, errors.Wrap(err, "bootstrapper")
}
st.Kubelet, err = bs.GetKubeletStatus()
if err != nil {
glog.Warningf("kubelet err: %v", err)
st.Kubelet = state.Error.String()
}
ip, err := cluster.GetHostDriverIP(api, name)
if err != nil {
glog.Errorln("Error host driver ip status:", err)
st.APIServer = state.Error.String()
return st, err
}
port, err := kubeconfig.Port(name)
if err != nil {
glog.Warningf("unable to get port: %v", err)
port = constants.APIServerPort
}
st.APIServer, err = bs.GetAPIServerStatus(ip, port)
if err != nil {
glog.Errorln("Error apiserver status:", err)
st.APIServer = state.Error.String()
}
ks, err := kubeconfig.IsClusterInConfig(ip, name)
if err != nil {
glog.Errorln("Error kubeconfig status:", err)
}
if ks {
st.Kubeconfig = Configured
} else {
st.Kubeconfig = Misconfigured
}
return st, nil
}
func init() { func init() {
statusCmd.Flags().StringVarP(&statusFormat, "format", "f", defaultStatusFormat, statusCmd.Flags().StringVarP(&statusFormat, "format", "f", defaultStatusFormat,
`Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ `Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/
@ -172,25 +180,24 @@ For the list accessible variables for the template, see the struct values here:
`minikube status --output OUTPUT. json, text`) `minikube status --output OUTPUT. json, text`)
} }
var printStatusText = func(status Status) { var printStatusText = func(st *Status) {
tmpl, err := template.New("status").Parse(statusFormat) tmpl, err := template.New("status").Parse(statusFormat)
if err != nil { if err != nil {
exit.WithError("Error creating status template", err) exit.WithError("Error creating status template", err)
} }
err = tmpl.Execute(os.Stdout, status) err = tmpl.Execute(os.Stdout, st)
if err != nil { if err != nil {
exit.WithError("Error executing status template", err) exit.WithError("Error executing status template", err)
} }
if status.Kubeconfig == KubeconfigStatus.Misconfigured { if st.Kubeconfig == Misconfigured {
out.WarningT("Warning: Your kubectl is pointing to stale minikube-vm.\nTo fix the kubectl context, run `minikube update-context`") out.WarningT("Warning: Your kubectl is pointing to stale minikube-vm.\nTo fix the kubectl context, run `minikube update-context`")
} }
} }
var printStatusJSON = func(status Status) { var printStatusJSON = func(st *Status) {
js, err := json.Marshal(st)
jsonString, err := json.Marshal(status)
if err != nil { if err != nil {
exit.WithError("Error converting status to json", err) exit.WithError("Error converting status to json", err)
} }
out.String(string(jsonString)) out.String(string(js))
} }

View File

@ -18,6 +18,7 @@ package kubeadm
import ( import (
"os/exec" "os/exec"
"path"
"fmt" "fmt"
"net" "net"
@ -77,9 +78,11 @@ func NewBootstrapper(api libmachine.API) (*Bootstrapper, error) {
func (k *Bootstrapper) GetKubeletStatus() (string, error) { func (k *Bootstrapper) GetKubeletStatus() (string, error) {
rr, err := k.c.RunCmd(exec.Command("sudo", "systemctl", "is-active", "kubelet")) rr, err := k.c.RunCmd(exec.Command("sudo", "systemctl", "is-active", "kubelet"))
if err != nil { if err != nil {
return "", errors.Wrapf(err, "getting kublet status. command: %q", rr.Command()) // Do not return now, as we still have parsing to do!
glog.Warningf("%s returned error: %v", rr.Command(), err)
} }
s := strings.TrimSpace(rr.Stdout.String()) s := strings.TrimSpace(rr.Stdout.String())
glog.Infof("kubelet is-active: %s", s)
switch s { switch s {
case "active": case "active":
return state.Running.String(), nil return state.Running.String(), nil
@ -93,6 +96,24 @@ func (k *Bootstrapper) GetKubeletStatus() (string, error) {
// GetAPIServerStatus returns the api-server status // GetAPIServerStatus returns the api-server status
func (k *Bootstrapper) GetAPIServerStatus(ip net.IP, apiserverPort int) (string, error) { func (k *Bootstrapper) GetAPIServerStatus(ip net.IP, apiserverPort int) (string, error) {
// sudo, in case hidepid is set
rr, err := k.c.RunCmd(exec.Command("sudo", "pgrep", "kube-apiserver"))
if err != nil {
return state.Stopped.String(), nil
}
pid := strings.TrimSpace(rr.Stdout.String())
// Check if apiserver is in an uninteruptible sleep
rr, err = k.c.RunCmd(exec.Command("sudo", "cut", "-d", " ", "-f3", path.Join("/proc", pid, "stat")))
if err != nil {
return state.Error.String(), err
}
st := strings.TrimSpace(rr.Stdout.String())
glog.Infof("apiserver process state: %s", st)
if st == "S" {
return state.Paused.String(), nil
}
return kverify.APIServerStatus(ip, apiserverPort) return kverify.APIServerStatus(ip, apiserverPort)
} }