github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/commands/ssh.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"fmt"
     8  	"strconv"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/gnuflag"
    13  	"github.com/juju/utils/ssh"
    14  
    15  	jujucmd "github.com/juju/juju/cmd"
    16  	"github.com/juju/juju/cmd/modelcmd"
    17  	jujussh "github.com/juju/juju/network/ssh"
    18  )
    19  
    20  var usageSSHSummary = `
    21  Initiates an SSH session or executes a command on a Juju machine.`[1:]
    22  
    23  var usageSSHDetails = `
    24  The machine is identified by the <target> argument which is either a 'unit
    25  name' or a 'machine id'. Both are obtained in the output to "juju status". If
    26  'user' is specified then the connection is made to that user account;
    27  otherwise, the default 'ubuntu' account, created by Juju, is used.
    28  
    29  The optional command is executed on the remote machine, and any output is sent
    30  back to the user. If no command is specified, then an interactive shell session
    31  will be initiated.
    32  
    33  When "juju ssh" is executed without a terminal attached, e.g. when piping the
    34  output of another command into it, then the default behavior is to not allocate
    35  a pseudo-terminal (pty) for the ssh session; otherwise a pty is allocated. This
    36  behavior can be overridden by explicitly specifying the behavior with
    37  "--pty=true" or "--pty=false".
    38  
    39  The SSH host keys of the target are verified. The --no-host-key-checks option
    40  can be used to disable these checks. Use of this option is not recommended as
    41  it opens up the possibility of a man-in-the-middle attack.
    42  
    43  The default identity known to Juju and used by this command is ~/.ssh/id_rsa
    44  
    45  Options can be passed to the local OpenSSH client (ssh) on platforms 
    46  where it is available. This is done by inserting them between the target and 
    47  a possible remote command. Refer to the ssh man page for an explanation 
    48  of those options.
    49  
    50  Examples:
    51  Connect to machine 0:
    52  
    53      juju ssh 0
    54  
    55  Connect to machine 1 and run command 'uname -a':
    56  
    57      juju ssh 1 uname -a
    58  
    59  Connect to a mysql unit:
    60  
    61      juju ssh mysql/0
    62  
    63  Connect to a jenkins unit as user jenkins:
    64  
    65      juju ssh jenkins@jenkins/0
    66  
    67  Connect to a mysql unit with an identity not known to juju (ssh option -i):
    68  
    69      juju ssh mysql/0 -i ~/.ssh/my_private_key echo hello
    70  
    71  See also: 
    72      scp`
    73  
    74  func newSSHCommand(
    75  	hostChecker jujussh.ReachableChecker,
    76  	isTerminal func(interface{}) bool,
    77  ) cmd.Command {
    78  	c := new(sshCommand)
    79  	c.setHostChecker(hostChecker)
    80  	c.isTerminal = isTerminal
    81  	return modelcmd.Wrap(c)
    82  }
    83  
    84  // sshCommand is responsible for launching a ssh shell on a given unit or machine.
    85  type sshCommand struct {
    86  	SSHCommon
    87  	isTerminal func(interface{}) bool
    88  	pty        autoBoolValue
    89  }
    90  
    91  func (c *sshCommand) SetFlags(f *gnuflag.FlagSet) {
    92  	c.SSHCommon.SetFlags(f)
    93  	f.Var(&c.pty, "pty", "Enable pseudo-tty allocation")
    94  }
    95  
    96  func (c *sshCommand) Info() *cmd.Info {
    97  	return jujucmd.Info(&cmd.Info{
    98  		Name:    "ssh",
    99  		Args:    "<[user@]target> [openssh options] [command]",
   100  		Purpose: usageSSHSummary,
   101  		Doc:     usageSSHDetails,
   102  	})
   103  }
   104  
   105  func (c *sshCommand) Init(args []string) error {
   106  	if len(args) == 0 {
   107  		return errors.Errorf("no target name specified")
   108  	}
   109  	c.Target, c.Args = args[0], args[1:]
   110  	return nil
   111  }
   112  
   113  // Run resolves c.Target to a machine, to the address of a i
   114  // machine or unit forks ssh passing any arguments provided.
   115  func (c *sshCommand) Run(ctx *cmd.Context) error {
   116  	err := c.initRun()
   117  	if err != nil {
   118  		return errors.Trace(err)
   119  	}
   120  	defer c.cleanupRun()
   121  
   122  	target, err := c.resolveTarget(c.Target)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	var pty bool
   128  	if c.pty.b != nil {
   129  		pty = *c.pty.b
   130  	} else {
   131  		// Flag was not specified: create a pty
   132  		// on the remote side iff this process
   133  		// has a terminal.
   134  		isTerminal := isTerminal
   135  		if c.isTerminal != nil {
   136  			isTerminal = c.isTerminal
   137  		}
   138  		pty = isTerminal(ctx.Stdin)
   139  	}
   140  
   141  	options, err := c.getSSHOptions(pty, target)
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	cmd := ssh.Command(target.userHost(), c.Args, options)
   147  	cmd.Stdin = ctx.Stdin
   148  	cmd.Stdout = ctx.Stdout
   149  	cmd.Stderr = ctx.Stderr
   150  	return cmd.Run()
   151  }
   152  
   153  // autoBoolValue is like gnuflag.boolValue, but remembers
   154  // whether or not a value has been set, so its behaviour
   155  // can be determined dynamically, during command execution.
   156  type autoBoolValue struct {
   157  	b *bool
   158  }
   159  
   160  func (b *autoBoolValue) Set(s string) error {
   161  	v, err := strconv.ParseBool(s)
   162  	if err != nil {
   163  		return err
   164  	}
   165  	b.b = &v
   166  	return nil
   167  }
   168  
   169  func (b *autoBoolValue) Get() interface{} {
   170  	if b.b != nil {
   171  		return *b.b
   172  	}
   173  	return b.b // nil
   174  }
   175  
   176  func (b *autoBoolValue) String() string {
   177  	if b.b != nil {
   178  		return fmt.Sprint(*b.b)
   179  	}
   180  	return "<auto>"
   181  }
   182  
   183  func (b *autoBoolValue) IsBoolFlag() bool { return true }