diff --git a/cmd/minikube/cmd/status.go b/cmd/minikube/cmd/status.go index a1099bce0e..04781770ee 100644 --- a/cmd/minikube/cmd/status.go +++ b/cmd/minikube/cmd/status.go @@ -17,16 +17,24 @@ limitations under the License. package cmd import ( - "fmt" "os" + "text/template" "github.com/docker/machine/libmachine" + "github.com/docker/machine/libmachine/state" "github.com/golang/glog" "github.com/spf13/cobra" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/constants" ) +var statusFormat string + +type Status struct { + MinikubeStatus string + LocalkubeStatus string +} + // statusCmd represents the status command var statusCmd = &cobra.Command{ Use: "status", @@ -35,15 +43,37 @@ var statusCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { api := libmachine.NewClient(constants.Minipath, constants.MakeMiniPath("certs")) defer api.Close() - s, err := cluster.GetHostStatus(api) + ms, err := cluster.GetHostStatus(api) if err != nil { glog.Errorln("Error getting machine status:", err) os.Exit(1) } - fmt.Fprintln(os.Stdout, s) + ls := "N/A" + if ms == state.Running.String() { + ls, err = cluster.GetLocalkubeStatus(api) + } + if err != nil { + glog.Errorln("Error getting machine status:", err) + os.Exit(1) + } + status := Status{ms, ls} + + tmpl, err := template.New("status").Parse(statusFormat) + if err != nil { + glog.Errorln("Error creating status template:", err) + os.Exit(1) + } + err = tmpl.Execute(os.Stdout, status) + if err != nil { + glog.Errorln("Error executing status template:", err) + os.Exit(1) + } }, } func init() { + statusCmd.Flags().StringVar(&statusFormat, "format", constants.DefaultStatusFormat, + `Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ +For the list accessible variables for the template, see the struct values here:https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status`) RootCmd.AddCommand(statusCmd) } diff --git a/docs/minikube_status.md b/docs/minikube_status.md index 74875860ce..34d81a8262 100644 --- a/docs/minikube_status.md +++ b/docs/minikube_status.md @@ -11,6 +11,13 @@ Gets the status of a local kubernetes cluster. minikube status ``` +### Options + +``` + --format="minikubeVM: {{.MinikubeStatus}}\nlocalkube: {{.LocalkubeStatus}}\n": Go template format string for the status output. The format for Go templates can be found here: https://golang.org/pkg/text/template/ +For the list accessible variables for the template, see the struct values here:https://godoc.org/k8s.io/minikube/cmd/minikube/cmd#Status +``` + ### Options inherited from parent commands ``` diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index 25a75afc81..e810f857a6 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -147,6 +147,26 @@ func GetHostStatus(api libmachine.API) (string, error) { return s.String(), err } +// GetLocalkubeStatus gets the status of localkube from the host VM. +func GetLocalkubeStatus(api libmachine.API) (string, error) { + host, err := checkIfApiExistsAndLoad(api) + if err != nil { + return "", err + } + s, err := host.RunSSHCommand(localkubeStatusCommand) + if err != nil { + return "", err + } + s = strings.TrimSpace(s) + if state.Running.String() == s { + return state.Running.String(), nil + } else if state.Stopped.String() == s { + return state.Stopped.String(), nil + } else { + return "", fmt.Errorf("Error: Unrecognize output from GetLocalkubeStatus: %s", s) + } +} + type sshAble interface { RunSSHCommand(string) (string, error) } @@ -600,7 +620,7 @@ func GetHostLogs(api libmachine.API) (string, error) { } s, err := host.RunSSHCommand(logsCommand) if err != nil { - return "", nil + return "", err } return s, err } diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index d5dc270ebf..7a78875b20 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -325,6 +325,46 @@ func TestGetHostStatus(t *testing.T) { checkState(state.Stopped.String()) } +func TestGetLocalkubeStatus(t *testing.T) { + api := tests.NewMockAPI() + + s, _ := tests.NewSSHServer() + port, err := s.Start() + if err != nil { + t.Fatalf("Error starting ssh server: %s", err) + } + + d := &tests.MockDriver{ + Port: port, + BaseDriver: drivers.BaseDriver{ + IPAddress: "127.0.0.1", + SSHKeyPath: "", + }, + } + api.Hosts[constants.MachineName] = &host.Host{Driver: d} + + s.CommandToOutput = map[string]string{ + localkubeStatusCommand: state.Running.String(), + } + if _, err := GetLocalkubeStatus(api); err != nil { + t.Fatalf("Error getting localkube status: %s", err) + } + + s.CommandToOutput = map[string]string{ + localkubeStatusCommand: state.Stopped.String(), + } + if _, err := GetLocalkubeStatus(api); err != nil { + t.Fatalf("Error getting localkube status: %s", err) + } + + s.CommandToOutput = map[string]string{ + localkubeStatusCommand: "Bad Output", + } + if _, err := GetLocalkubeStatus(api); err == nil { + t.Fatalf("Expected error in getting localkube status as ssh returned bad output") + } +} + func TestSetupCerts(t *testing.T) { s, _ := tests.NewSSHServer() port, err := s.Start() diff --git a/pkg/minikube/cluster/commands.go b/pkg/minikube/cluster/commands.go index fe093ef2d7..1e907a3ce7 100644 --- a/pkg/minikube/cluster/commands.go +++ b/pkg/minikube/cluster/commands.go @@ -29,7 +29,7 @@ var stopCommand = "sudo killall localkube || true" var startCommandFmtStr = ` # Run with nohup so it stays up. Redirect logs to useful places. -sudo sh -c 'PATH=/usr/local/sbin:$PATH nohup /usr/local/bin/localkube %s --generate-certs=false --logtostderr=true --node-ip=%s > %s 2> %s < /dev/null &' +sudo sh -c 'PATH=/usr/local/sbin:$PATH nohup /usr/local/bin/localkube %s --generate-certs=false --logtostderr=true --node-ip=%s > %s 2> %s < /dev/null & echo $! > %s &' ` var logsCommand = fmt.Sprintf("tail -n +1 %s %s", constants.RemoteLocalKubeErrPath, constants.RemoteLocalKubeOutPath) @@ -41,5 +41,13 @@ func GetStartCommand(kubernetesConfig KubernetesConfig) string { } } flags := strings.Join(flagVals, " ") - return fmt.Sprintf(startCommandFmtStr, flags, kubernetesConfig.NodeIP, constants.RemoteLocalKubeErrPath, constants.RemoteLocalKubeOutPath) + return fmt.Sprintf(startCommandFmtStr, flags, kubernetesConfig.NodeIP, constants.RemoteLocalKubeErrPath, constants.RemoteLocalKubeOutPath, constants.LocalkubePIDPath) } + +var localkubeStatusCommand = fmt.Sprintf(` +if ps $(cat %s) 2>&1 1>/dev/null; then + echo "Running" +else + echo "Stopped" +fi +`, constants.LocalkubePIDPath) diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 36579326d8..02137a7dbf 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -56,12 +56,14 @@ var LogFlags = [...]string{ } const ( - DefaultIsoUrl = "https://storage.googleapis.com/minikube/minikube-0.6.iso" - DefaultIsoShaUrl = "https://storage.googleapis.com/minikube/minikube-0.6.iso.sha256" - DefaultMemory = 1024 - DefaultCPUS = 1 - DefaultDiskSize = "20g" - DefaultVMDriver = "virtualbox" + DefaultIsoUrl = "https://storage.googleapis.com/minikube/minikube-0.6.iso" + DefaultIsoShaUrl = "https://storage.googleapis.com/minikube/minikube-0.6.iso.sha256" + DefaultMemory = 1024 + DefaultCPUS = 1 + DefaultDiskSize = "20g" + DefaultVMDriver = "virtualbox" + DefaultStatusFormat = "minikubeVM: {{.MinikubeStatus}}\n" + + "localkube: {{.LocalkubeStatus}}\n" ) var DefaultKubernetesVersion = version.Get().GitVersion @@ -69,6 +71,7 @@ var DefaultKubernetesVersion = version.Get().GitVersion const ( RemoteLocalKubeErrPath = "/var/lib/localkube/localkube.err" RemoteLocalKubeOutPath = "/var/lib/localkube/localkube.out" + LocalkubePIDPath = "/var/run/localkube.pid" ) var ConfigFilePath = MakeMiniPath("config") diff --git a/pkg/minikube/tests/ssh_mock.go b/pkg/minikube/tests/ssh_mock.go index fbad6a6225..36682336fe 100644 --- a/pkg/minikube/tests/ssh_mock.go +++ b/pkg/minikube/tests/ssh_mock.go @@ -36,6 +36,7 @@ type SSHServer struct { Connected bool Transfers *bytes.Buffer HadASessionRequested bool + CommandToOutput map[string]string } // NewSSHServer returns a NewSSHServer instance, ready for use. @@ -107,6 +108,11 @@ func (s *SSHServer) Start() (int, error) { return } s.Commands[cmd.Command] = 1 + + // Write specified command output as mocked ssh output + if val, ok := s.CommandToOutput[cmd.Command]; ok { + channel.Write([]byte(val)) + } channel.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) // Store anything that comes in over stdin. diff --git a/test/integration/util/util.go b/test/integration/util/util.go index 6840c65560..7a3c045d0a 100644 --- a/test/integration/util/util.go +++ b/test/integration/util/util.go @@ -100,8 +100,7 @@ func (m *MinikubeRunner) SetEnvFromEnvCmdOutput(dockerEnvVars string) error { } func (m *MinikubeRunner) GetStatus() string { - status := m.RunCommand("status", true) - return strings.Trim(status, "\n") + return m.RunCommand("status --format={{.MinikubeStatus}}", true) } func (m *MinikubeRunner) CheckStatus(desired string) {