github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/manual/environ_test.go (about) 1 // Copyright 2012 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package manual 5 6 import ( 7 "os" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils/arch" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/core/constraints" 16 "github.com/juju/juju/core/instance" 17 "github.com/juju/juju/environs" 18 "github.com/juju/juju/environs/context" 19 "github.com/juju/juju/environs/manual/sshprovisioner" 20 coretesting "github.com/juju/juju/testing" 21 ) 22 23 type baseEnvironSuite struct { 24 coretesting.FakeJujuXDGDataHomeSuite 25 env *manualEnviron 26 27 callCtx context.ProviderCallContext 28 } 29 30 func (s *baseEnvironSuite) SetUpTest(c *gc.C) { 31 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 32 env, err := ManualProvider{}.Open(environs.OpenParams{ 33 Cloud: CloudSpec(), 34 Config: MinimalConfig(c), 35 }) 36 c.Assert(err, jc.ErrorIsNil) 37 s.env = env.(*manualEnviron) 38 s.callCtx = context.NewCloudCallContext() 39 } 40 41 type environSuite struct { 42 baseEnvironSuite 43 } 44 45 var _ = gc.Suite(&environSuite{}) 46 47 func (s *environSuite) TestInstances(c *gc.C) { 48 var ids []instance.Id 49 50 instances, err := s.env.Instances(s.callCtx, ids) 51 c.Assert(err, gc.Equals, environs.ErrNoInstances) 52 c.Assert(instances, gc.HasLen, 0) 53 54 ids = append(ids, BootstrapInstanceId) 55 instances, err = s.env.Instances(s.callCtx, ids) 56 c.Assert(err, jc.ErrorIsNil) 57 c.Assert(instances, gc.HasLen, 1) 58 c.Assert(instances[0], gc.NotNil) 59 60 ids = append(ids, BootstrapInstanceId) 61 instances, err = s.env.Instances(s.callCtx, ids) 62 c.Assert(err, jc.ErrorIsNil) 63 c.Assert(instances, gc.HasLen, 2) 64 c.Assert(instances[0], gc.NotNil) 65 c.Assert(instances[1], gc.NotNil) 66 67 ids = append(ids, instance.Id("invalid")) 68 instances, err = s.env.Instances(s.callCtx, ids) 69 c.Assert(err, gc.Equals, environs.ErrPartialInstances) 70 c.Assert(instances, gc.HasLen, 3) 71 c.Assert(instances[0], gc.NotNil) 72 c.Assert(instances[1], gc.NotNil) 73 c.Assert(instances[2], gc.IsNil) 74 75 ids = []instance.Id{instance.Id("invalid")} 76 instances, err = s.env.Instances(s.callCtx, ids) 77 c.Assert(err, gc.Equals, environs.ErrNoInstances) 78 c.Assert(instances, gc.HasLen, 1) 79 c.Assert(instances[0], gc.IsNil) 80 } 81 82 func (s *environSuite) TestDestroyController(c *gc.C) { 83 var resultStdout string 84 var resultErr error 85 runSSHCommandTesting := func(host string, command []string, stdin string) (string, string, error) { 86 c.Assert(host, gc.Equals, "ubuntu@hostname") 87 c.Assert(command, gc.DeepEquals, []string{"sudo", "/bin/bash"}) 88 c.Assert(stdin, gc.Equals, ` 89 # Signal the jujud process to stop, then check it has done so before cleaning-up 90 # after it. 91 set -x 92 touch '/var/lib/juju/uninstall-agent' 93 94 stopped=0 95 function wait_for_jujud { 96 for i in {1..30}; do 97 if pgrep jujud > /dev/null ; then 98 sleep 1 99 else 100 echo jujud stopped 101 stopped=1 102 logger --id jujud stopped on attempt $i 103 break 104 fi 105 done 106 } 107 108 # There might be no jujud at all (for example, after a failed deployment) so 109 # don't require pkill to succeed before looking for a jujud process. 110 # SIGABRT not SIGTERM, as abort lets the worker know it should uninstall itself, 111 # rather than terminate normally. 112 pkill -SIGABRT jujud 113 wait_for_jujud 114 115 [[ $stopped -ne 1 ]] && { 116 # If jujud didn't stop nicely, we kill it hard here. 117 pkill -SIGKILL jujud && wait_for_jujud 118 } 119 [[ $stopped -ne 1 ]] && { 120 echo jujud removal failed 121 logger --id $(ps -o pid,cmd,state -p $(pgrep jujud) | awk 'NR != 1 {printf("Process %d (%s) has state %s\n", $1, $2, $3)}') 122 exit 1 123 } 124 service juju-db stop && logger --id stopped juju-db 125 apt-get -y purge juju-mongo* 126 apt-get -y autoremove 127 rm -f /etc/init/juju* 128 rm -f /etc/systemd/system{,/multi-user.target.wants}/juju* 129 rm -fr '/var/lib/juju' '/var/log/juju' 130 exit 0 131 `) 132 return resultStdout, "", resultErr 133 } 134 s.PatchValue(&runSSHCommand, runSSHCommandTesting) 135 type test struct { 136 stdout string 137 err error 138 match string 139 } 140 tests := []test{ 141 {"", nil, ""}, 142 {"abc", nil, ""}, 143 {"", errors.New("oh noes"), "oh noes"}, 144 } 145 for i, t := range tests { 146 c.Logf("test %d: %v", i, t) 147 resultStdout, resultErr = t.stdout, t.err 148 err := s.env.DestroyController(s.callCtx, "controller-uuid") 149 if t.match == "" { 150 c.Assert(err, jc.ErrorIsNil) 151 } else { 152 c.Assert(err, gc.ErrorMatches, t.match) 153 } 154 } 155 } 156 157 func (s *environSuite) TestSupportsNetworking(c *gc.C) { 158 _, ok := environs.SupportsNetworking(s.env) 159 c.Assert(ok, jc.IsFalse) 160 } 161 162 func (s *environSuite) TestConstraintsValidator(c *gc.C) { 163 s.PatchValue(&sshprovisioner.DetectSeriesAndHardwareCharacteristics, 164 func(string) (instance.HardwareCharacteristics, string, error) { 165 amd64 := "amd64" 166 return instance.HardwareCharacteristics{ 167 Arch: &amd64, 168 }, "", nil 169 }, 170 ) 171 172 validator, err := s.env.ConstraintsValidator(s.callCtx) 173 c.Assert(err, jc.ErrorIsNil) 174 cons := constraints.MustParse("arch=amd64 instance-type=foo tags=bar cpu-power=10 cores=2 mem=1G virt-type=kvm") 175 unsupported, err := validator.Validate(cons) 176 c.Assert(err, jc.ErrorIsNil) 177 c.Assert(unsupported, jc.SameContents, []string{"cpu-power", "instance-type", "tags", "virt-type"}) 178 } 179 180 func (s *environSuite) TestConstraintsValidatorInsideController(c *gc.C) { 181 // Patch os.Args so it appears that we're running in "jujud", and then 182 // patch the host arch so it looks like we're running arm64. 183 s.PatchValue(&os.Args, []string{"/some/where/containing/jujud", "whatever"}) 184 s.PatchValue(&arch.HostArch, func() string { return arch.ARM64 }) 185 186 validator, err := s.env.ConstraintsValidator(s.callCtx) 187 c.Assert(err, jc.ErrorIsNil) 188 cons := constraints.MustParse("arch=arm64") 189 _, err = validator.Validate(cons) 190 c.Assert(err, jc.ErrorIsNil) 191 } 192 193 type controllerInstancesSuite struct { 194 baseEnvironSuite 195 } 196 197 var _ = gc.Suite(&controllerInstancesSuite{}) 198 199 func (s *controllerInstancesSuite) TestControllerInstances(c *gc.C) { 200 var outputResult string 201 var errResult error 202 runSSHCommandTesting := func(host string, command []string, stdin string) (string, string, error) { 203 return outputResult, "", errResult 204 } 205 s.PatchValue(&runSSHCommand, runSSHCommandTesting) 206 207 type test struct { 208 output string 209 err error 210 expectedErr string 211 } 212 tests := []test{{ 213 output: "", 214 }, { 215 output: "no-agent-dir", 216 expectedErr: "model is not bootstrapped", 217 }, { 218 output: "woo", 219 expectedErr: `unexpected output: "woo"`, 220 }, { 221 err: errors.New("an error"), 222 expectedErr: "an error", 223 }} 224 225 for i, test := range tests { 226 c.Logf("test %d", i) 227 outputResult = test.output 228 errResult = test.err 229 instances, err := s.env.ControllerInstances(s.callCtx, "not-used") 230 if test.expectedErr == "" { 231 c.Assert(err, jc.ErrorIsNil) 232 c.Assert(instances, gc.DeepEquals, []instance.Id{BootstrapInstanceId}) 233 } else { 234 c.Assert(err, gc.ErrorMatches, test.expectedErr) 235 c.Assert(instances, gc.HasLen, 0) 236 } 237 } 238 } 239 240 func (s *controllerInstancesSuite) TestControllerInstancesStderr(c *gc.C) { 241 // Stderr should not affect the behaviour of ControllerInstances. 242 testing.PatchExecutable(c, s, "ssh", "#!/bin/sh\nhead -n1 > /dev/null; echo abc >&2; exit 0") 243 _, err := s.env.ControllerInstances(s.callCtx, "not-used") 244 c.Assert(err, jc.ErrorIsNil) 245 } 246 247 func (s *controllerInstancesSuite) TestControllerInstancesError(c *gc.C) { 248 // If the ssh execution fails, its stderr will be captured in the error message. 249 testing.PatchExecutable(c, s, "ssh", "#!/bin/sh\nhead -n1 > /dev/null; echo abc >&2; exit 1") 250 _, err := s.env.ControllerInstances(s.callCtx, "not-used") 251 c.Assert(err, gc.ErrorMatches, "abc: .*") 252 } 253 254 func (s *controllerInstancesSuite) TestControllerInstancesInternal(c *gc.C) { 255 // Patch os.Args so it appears that we're running in "jujud". 256 s.PatchValue(&os.Args, []string{"/some/where/containing/jujud", "whatever"}) 257 // Patch the ssh executable so that it would cause an error if we 258 // were to call it. 259 testing.PatchExecutable(c, s, "ssh", "#!/bin/sh\nhead -n1 > /dev/null; echo abc >&2; exit 1") 260 instances, err := s.env.ControllerInstances(s.callCtx, "not-used") 261 c.Assert(err, jc.ErrorIsNil) 262 c.Assert(instances, gc.DeepEquals, []instance.Id{BootstrapInstanceId}) 263 }