github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/environs/manual/sshprovisioner/fakessh_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Copyright 2016 Cloudbase Solutions SRL
     3  // Licensed under the AGPLv3, see LICENCE file for details.
     4  
     5  package sshprovisioner_test
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/environs/manual/sshprovisioner"
    18  	"github.com/juju/juju/service"
    19  )
    20  
    21  // sshscript should only print the result on the first execution,
    22  // to handle the case where it's called multiple times. On
    23  // subsequent executions, it should find the next 'ssh' in $PATH
    24  // and exec that.
    25  var sshscript = `#!/bin/bash --norc
    26  if [ ! -e "$0.run" ]; then
    27      touch "$0.run"
    28      if [ -e "$0.expected-input" ]; then
    29          diff "$0.expected-input" -
    30          exitcode=$?
    31          if [ $exitcode -ne 0 ]; then
    32              echo "ERROR: did not match expected input" >&2
    33              exit $exitcode
    34          fi
    35          else
    36              head >/dev/null
    37          fi
    38      # stdout
    39      %s
    40      # stderr
    41      %s
    42      exit %d
    43  else
    44      export PATH=${PATH#*:}
    45      exec ssh $*
    46  fi`
    47  
    48  // installFakeSSH creates a fake "ssh" command in a new $PATH,
    49  // updates $PATH, and returns a function to reset $PATH to its
    50  // original value when called.
    51  //
    52  // input may be:
    53  //    - nil (ignore input)
    54  //    - a string (match input exactly)
    55  // output may be:
    56  //    - nil (no output)
    57  //    - a string (stdout)
    58  //    - a slice of strings, of length two (stdout, stderr)
    59  func installFakeSSH(c *gc.C, input, output interface{}, rc int) testing.Restorer {
    60  	fakebin := c.MkDir()
    61  	ssh := filepath.Join(fakebin, "ssh")
    62  	switch input := input.(type) {
    63  	case nil:
    64  	case string:
    65  		sshexpectedinput := ssh + ".expected-input"
    66  		err := ioutil.WriteFile(sshexpectedinput, []byte(input), 0644)
    67  		c.Assert(err, jc.ErrorIsNil)
    68  	default:
    69  		c.Errorf("input has invalid type: %T", input)
    70  	}
    71  	var stdout, stderr string
    72  	switch output := output.(type) {
    73  	case nil:
    74  	case string:
    75  		stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output)
    76  	case []string:
    77  		c.Assert(output, gc.HasLen, 2)
    78  		stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output[0])
    79  		stderr = fmt.Sprintf("cat>&2<<EOF\n%s\nEOF", output[1])
    80  	}
    81  	script := fmt.Sprintf(sshscript, stdout, stderr, rc)
    82  	err := ioutil.WriteFile(ssh, []byte(script), 0777)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	return testing.PatchEnvPathPrepend(fakebin)
    85  }
    86  
    87  // installDetectionFakeSSH installs a fake SSH command, which will respond
    88  // to the series/hardware detection script with the specified
    89  // series/arch.
    90  func installDetectionFakeSSH(c *gc.C, series, arch string) testing.Restorer {
    91  	if series == "" {
    92  		series = "precise"
    93  	}
    94  	if arch == "" {
    95  		arch = "amd64"
    96  	}
    97  	detectionoutput := strings.Join([]string{
    98  		series,
    99  		arch,
   100  		"MemTotal: 4096 kB",
   101  		"processor: 0",
   102  	}, "\n")
   103  	return installFakeSSH(c, sshprovisioner.DetectionScript, detectionoutput, 0)
   104  }
   105  
   106  // fakeSSH wraps the invocation of InstallFakeSSH based on the parameters.
   107  type fakeSSH struct {
   108  	Series string
   109  	Arch   string
   110  
   111  	// Provisioned should be set to true if the fakeSSH script
   112  	// should respond to checkProvisioned with a non-empty result.
   113  	Provisioned bool
   114  
   115  	// exit code for the checkProvisioned script.
   116  	CheckProvisionedExitCode int
   117  
   118  	// exit code for the machine agent provisioning script.
   119  	ProvisionAgentExitCode int
   120  
   121  	// InitUbuntuUser should be set to true if the fakeSSH script
   122  	// should respond to an attempt to initialise the ubuntu user.
   123  	InitUbuntuUser bool
   124  
   125  	// there are conditions other than error in the above
   126  	// that might cause provisioning to not go ahead, such
   127  	// as tools being missing.
   128  	SkipProvisionAgent bool
   129  
   130  	// detection will be skipped if the series/hardware were
   131  	// detected ahead of time. This should always be set to
   132  	// true when testing Bootstrap.
   133  	SkipDetection bool
   134  }
   135  
   136  // install installs fake SSH commands, which will respond to
   137  // manual provisioning/bootstrapping commands with the specified
   138  // output and exit codes.
   139  func (r fakeSSH) install(c *gc.C) testing.Restorer {
   140  	var restore testing.Restorer
   141  	add := func(input, output interface{}, rc int) {
   142  		restore = restore.Add(installFakeSSH(c, input, output, rc))
   143  	}
   144  	if !r.SkipProvisionAgent {
   145  		add(nil, nil, r.ProvisionAgentExitCode)
   146  	}
   147  	if !r.SkipDetection {
   148  		restore.Add(installDetectionFakeSSH(c, r.Series, r.Arch))
   149  	}
   150  	var checkProvisionedOutput interface{}
   151  	if r.Provisioned {
   152  		checkProvisionedOutput = "/etc/init/jujud-machine-0.conf"
   153  	}
   154  	listCmd := service.ListServicesScript()
   155  	add(listCmd, checkProvisionedOutput, r.CheckProvisionedExitCode)
   156  	if r.InitUbuntuUser {
   157  		add("", nil, 0)
   158  	}
   159  	return restore
   160  }