github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/helper/vagrant/ssh.go (about)

     1  package vagrant
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"os/exec"
     9  	"strings"
    10  )
    11  
    12  // SSHCache is a helper to cache the SSH connection info from Vagrant
    13  // and use that for executing to avoid the overhead of loading Vagrant.
    14  type SSHCache struct {
    15  	// Path is the path to where the SSH cache file should go
    16  	Path string
    17  
    18  	// Vagrant is the Vagrant instance we'll use to execute Vagrant commands.
    19  	Vagrant *Vagrant
    20  }
    21  
    22  // Exec executes SSH and opens a console.
    23  //
    24  // This will use the cached SSH info if it exists, or will otherwise
    25  // drop into `vagrant ssh`. If cacheOkay is false, then it'll always go
    26  // straight to `vagrant ssh`.
    27  func (c *SSHCache) Exec(cacheOkay bool) error {
    28  	// If we have the cache file, use that
    29  	if _, err := os.Stat(c.Path); err == nil {
    30  		log.Printf("[DEBUG] ssh command: ssh -F " + c.Path + " default")
    31  		cmd := exec.Command("ssh", "-t", "-t", "-F", c.Path, "default")
    32  		cmd.Stdin = os.Stdin
    33  		cmd.Stdout = os.Stdout
    34  		cmd.Stderr = os.Stderr
    35  		if err := cmd.Start(); err != nil {
    36  			return err
    37  		}
    38  		if err := cmd.Wait(); err != nil {
    39  			return err
    40  		}
    41  		return nil
    42  	}
    43  
    44  	// Otherwise raw SSH
    45  	return c.Vagrant.Execute("ssh")
    46  }
    47  
    48  // Cache will execute "ssh-config" and cache the SSH info.
    49  func (c *SSHCache) Cache() error {
    50  	// Callback that records the output
    51  	var result string
    52  	callback := func(o *Output) {
    53  		result = o.Data[0]
    54  	}
    55  
    56  	// We just copy the Vagrant instance so we can modify it without
    57  	// worrying about restoring stuff. We set the UI to nil so nothing
    58  	// goes to the UI, and we set a callback to read the SSH config from
    59  	// the machine-readable output.
    60  	vagrant := *c.Vagrant
    61  	vagrant.Ui = nil
    62  	vagrant.Callbacks = map[string]OutputCallback{
    63  		"ssh-config": callback,
    64  	}
    65  	if err := vagrant.Execute("ssh-config"); err != nil {
    66  		return err
    67  	}
    68  
    69  	// If we have no output, it is an error
    70  	if result == "" {
    71  		return fmt.Errorf(
    72  			"No SSH info found in the output of Vagrant. This is a bug somewhere.\n" +
    73  				"Please re-run the command with OTTO_LOG=1 and report this as a bug.")
    74  	}
    75  
    76  	// Write the output to the cache
    77  	f, err := os.Create(c.Path)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	defer f.Close()
    82  	if _, err := io.Copy(f, strings.NewReader(result)); err != nil {
    83  		return err
    84  	}
    85  
    86  	return nil
    87  }
    88  
    89  // Delete clears the cache.
    90  func (c *SSHCache) Delete() error {
    91  	// We ignore the return value here because it'll happen if the
    92  	// file doesn't exist and we just don't care.
    93  	os.Remove(c.Path)
    94  	return nil
    95  }