github.com/jlmeeker/kismatic@v1.10.1-0.20180612190640-57f9005a1f1a/pkg/cli/ssh.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strings"
     7  
     8  	"github.com/apprenda/kismatic/pkg/ssh"
     9  
    10  	"github.com/apprenda/kismatic/pkg/install"
    11  	"github.com/apprenda/kismatic/pkg/util"
    12  	"github.com/spf13/cobra"
    13  )
    14  
    15  type sshOpts struct {
    16  	planFilename string
    17  	host         string
    18  	pty          bool
    19  	arguments    []string
    20  }
    21  
    22  // NewCmdSSH returns an ssh shell
    23  func NewCmdSSH(out io.Writer) *cobra.Command {
    24  	opts := &sshOpts{}
    25  
    26  	cmd := &cobra.Command{
    27  		Use:   "ssh HOST [commands]",
    28  		Short: "ssh into a node in the cluster",
    29  		Long: `ssh into a node in the cluster
    30  
    31  HOST must be one of the following:
    32  - A hostname defined in the plan filepath
    33  - An alias: master, etcd, worker, ingress or storage. This will ssh into the first defined node of that type.`,
    34  		RunE: func(cmd *cobra.Command, args []string) error {
    35  			if len(args) < 1 {
    36  				return cmd.Usage()
    37  			}
    38  			// get optional arguments
    39  			if len(args) > 1 {
    40  				opts.arguments = args[1:]
    41  			}
    42  
    43  			opts.host = args[0]
    44  
    45  			planner := &install.FilePlanner{File: opts.planFilename}
    46  			// Check if plan file exists
    47  			if !planner.PlanExists() {
    48  				return planFileNotFoundErr{filename: opts.planFilename}
    49  			}
    50  
    51  			err := doSSH(out, planner, opts)
    52  			// 130 = terminated by Control-C, so not an actual error
    53  			if err != nil && !strings.Contains(err.Error(), "130") {
    54  				return fmt.Errorf("SSH error %q: %v", opts.host, err)
    55  			}
    56  			return nil
    57  		},
    58  	}
    59  
    60  	cmd.Flags().StringVarP(&opts.planFilename, "plan-file", "f", "kismatic-cluster.yaml", "path to the installation plan file")
    61  	cmd.Flags().BoolVarP(&opts.pty, "pty", "t", false, "force PTY \"-t\" flag on the SSH connection")
    62  
    63  	return cmd
    64  }
    65  
    66  func doSSH(out io.Writer, planner install.Planner, opts *sshOpts) error {
    67  	plan, err := planner.Read()
    68  	if err != nil {
    69  		return fmt.Errorf("error reading plan file: %v", err)
    70  	}
    71  
    72  	// find node
    73  	con, err := plan.GetSSHConnection(opts.host)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	// validate SSH access to node
    79  	ok, errs := install.ValidateSSHConnection(con, "")
    80  	if !ok {
    81  		util.PrintValidationErrors(out, errs)
    82  		return fmt.Errorf("cannot validate SSH connection to node %q", opts.host)
    83  	}
    84  
    85  	client, err := ssh.NewClient(con.Node.IP, con.SSHConfig.Port, con.SSHConfig.User, con.SSHConfig.Key)
    86  	if err != nil {
    87  		return fmt.Errorf("error creating SSH client: %v", err)
    88  	}
    89  
    90  	if err = client.Shell(opts.pty, opts.arguments...); err != nil {
    91  		return fmt.Errorf("error running command: %v", err)
    92  	}
    93  
    94  	return err
    95  }