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 }