diff --git a/cmd/minikube/cmd/ssh.go b/cmd/minikube/cmd/ssh.go new file mode 100644 index 0000000000..907d2f51b7 --- /dev/null +++ b/cmd/minikube/cmd/ssh.go @@ -0,0 +1,47 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "os" + + "github.com/docker/machine/libmachine" + "github.com/golang/glog" + "github.com/spf13/cobra" + "k8s.io/minikube/pkg/minikube/cluster" + "k8s.io/minikube/pkg/minikube/constants" +) + +// sshCmd represents the docker-ssh command +var sshCmd = &cobra.Command{ + Use: "ssh", + Short: "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'", + Long: "Log into or run a command on a machine with SSH; similar to 'docker-machine ssh'", + Run: func(cmd *cobra.Command, args []string) { + api := libmachine.NewClient(constants.Minipath, constants.MakeMiniPath("certs")) + defer api.Close() + err := cluster.CreateSSHShell(api, args) + if err != nil { + glog.Errorln("Error attempting to ssh into machine: ", err) + os.Exit(1) + } + }, +} + +func init() { + RootCmd.AddCommand(sshCmd) +} diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index a2f60febfb..81eab21588 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -293,3 +293,25 @@ func checkIfApiExistsAndLoad(api libmachine.API) (*host.Host, error) { } return host, nil } + +func CreateSSHShell(api libmachine.API, args []string) error { + host, err := checkIfApiExistsAndLoad(api) + if err != nil { + return err + } + + currentState, err := host.Driver.GetState() + if err != nil { + return err + } + + if currentState != state.Running { + return fmt.Errorf("Error: Cannot run ssh command: Host %q is not running", constants.MachineName) + } + + client, err := host.CreateSSHClient() + if err != nil { + return err + } + return client.Shell(strings.Join(args, " ")) +} diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index 18a1164e1d..478736c48d 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -388,3 +388,32 @@ func TestHostGetLogs(t *testing.T) { t.Fatalf("Expected command not run: %s", logsCommand) } } + +func TestCreateSSHShell(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, + CurrentState: state.Running, + BaseDriver: drivers.BaseDriver{ + IPAddress: "127.0.0.1", + SSHKeyPath: "", + }, + } + api.Hosts[constants.MachineName] = &host.Host{Driver: d} + + cliArgs := []string{"exit"} + if err := CreateSSHShell(api, cliArgs); err != nil { + t.Fatalf("Error running ssh command: %s", err) + } + + if s.HadASessionRequested != true { + t.Fatalf("Expected ssh session to be run") + } +} diff --git a/pkg/minikube/tests/ssh_mock.go b/pkg/minikube/tests/ssh_mock.go index 35a12d8c3c..58a6a4c36b 100644 --- a/pkg/minikube/tests/ssh_mock.go +++ b/pkg/minikube/tests/ssh_mock.go @@ -21,10 +21,10 @@ import ( "crypto/rand" "crypto/rsa" "io" - "log" "net" "strconv" + "github.com/golang/glog" "golang.org/x/crypto/ssh" ) @@ -32,9 +32,10 @@ import ( type SSHServer struct { Config *ssh.ServerConfig // Commands stores the raw commands executed against the server. - Commands map[string]int - Connected bool - Transfers *bytes.Buffer + Commands map[string]int + Connected bool + Transfers *bytes.Buffer + HadASessionRequested bool } // NewSSHServer returns a NewSSHServer instance, ready for use. @@ -86,6 +87,9 @@ func (s *SSHServer) Start() (int, error) { // Service the incoming Channel channel. for newChannel := range chans { + if newChannel.ChannelType() == "session" { + s.HadASessionRequested = true + } channel, requests, err := newChannel.Accept() s.Connected = true if err != nil { @@ -98,7 +102,7 @@ func (s *SSHServer) Start() (int, error) { //Note: string(req.Payload) adds additional characters to start of input, execRequest used to solve this issue var cmd execRequest if err := ssh.Unmarshal(req.Payload, &cmd); err != nil { - log.Println("Unmarshall encountered error: %s", err) + glog.Errorln("Unmarshall encountered error: %s", err) return } s.Commands[cmd.Command] = 1