github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/tools/ssh.go (about)

     1  /*
     2  Copyright 2018 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tools
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"os/exec"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/renstrom/dedent"
    29  	"github.com/spf13/cobra"
    30  )
    31  
    32  // sshCommand can be used to ssh into a VM pod.
    33  type sshCommand struct {
    34  	client        KubeClient
    35  	user          string
    36  	podName       string
    37  	args          []string
    38  	out           io.Writer
    39  	sshExecutable string
    40  }
    41  
    42  // NewSSHCmd returns a cobra.Command that performs ssh into a VM pod.
    43  func NewSSHCmd(client KubeClient, out io.Writer, sshExecutable string) *cobra.Command {
    44  	ssh := &sshCommand{client: client, out: out, sshExecutable: sshExecutable}
    45  	return &cobra.Command{
    46  		Use:   "ssh [flags] user@pod -- [ssh args...]",
    47  		Short: "Connect to a VM pod using ssh",
    48  		Long: dedent.Dedent(`
    49                          This command runs ssh and makes it connect to a VM pod.
    50                  `),
    51  		RunE: func(cmd *cobra.Command, args []string) error {
    52  			if len(args) < 1 {
    53  				return errors.New("user name and pod name not specified")
    54  			}
    55  			parts := strings.Split(args[0], "@")
    56  			if len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) == 0 {
    57  				return errors.New("malformed user@host")
    58  			}
    59  			ssh.user = parts[0]
    60  			ssh.podName = parts[1]
    61  			ssh.args = args[1:]
    62  			if sshExecutable == "" {
    63  				ssh.sshExecutable = "ssh"
    64  			} else {
    65  				ssh.sshExecutable = sshExecutable
    66  			}
    67  			return ssh.Run()
    68  		},
    69  	}
    70  }
    71  
    72  // Run executes the command.
    73  func (s *sshCommand) Run() error {
    74  	// make sure we're dealing with a VM pod
    75  	_, err := s.client.GetVMPodInfo(s.podName)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	pf := &ForwardedPort{
    81  		RemotePort: 22,
    82  	}
    83  	stopCh, err := s.client.ForwardPorts(s.podName, "", []*ForwardedPort{pf})
    84  	if err != nil {
    85  		return fmt.Errorf("error forwarding the ssh port: %v", err)
    86  	}
    87  	defer close(stopCh)
    88  	sshArgs := append([]string{
    89  		"-q",
    90  		"-o",
    91  		"StrictHostKeyChecking=no",
    92  		"-o",
    93  		"UserKnownHostsFile=/dev/null",
    94  		"-p",
    95  		strconv.Itoa(int(pf.LocalPort)),
    96  		fmt.Sprintf("%s@127.0.0.1", s.user),
    97  	}, s.args...)
    98  	cmd := exec.Command(s.sshExecutable, sshArgs...)
    99  	cmd.Stdin = os.Stdin
   100  	cmd.Stdout = s.out
   101  	cmd.Stderr = os.Stderr
   102  	if err := cmd.Run(); err != nil {
   103  		return fmt.Errorf("error executing ssh: %v", err)
   104  	}
   105  	return nil
   106  }