github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/runner/factory_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runner_test 5 6 import ( 7 "os" 8 "strings" 9 "time" 10 11 "github.com/juju/clock/testclock" 12 "github.com/juju/errors" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/charm.v6/hooks" 17 "gopkg.in/juju/names.v2" 18 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/worker/common/charmrunner" 21 "github.com/juju/juju/worker/uniter/hook" 22 "github.com/juju/juju/worker/uniter/runner" 23 "github.com/juju/juju/worker/uniter/runner/context" 24 runnertesting "github.com/juju/juju/worker/uniter/runner/testing" 25 ) 26 27 type FactorySuite struct { 28 ContextSuite 29 } 30 31 var _ = gc.Suite(&FactorySuite{}) 32 33 func (s *FactorySuite) AssertPaths(c *gc.C, rnr runner.Runner) { 34 c.Assert(runner.RunnerPaths(rnr), gc.DeepEquals, s.paths) 35 } 36 37 func (s *FactorySuite) TestNewCommandRunnerNoRelation(c *gc.C) { 38 rnr, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: -1}) 39 c.Assert(err, jc.ErrorIsNil) 40 s.AssertPaths(c, rnr) 41 } 42 43 func (s *FactorySuite) TestNewCommandRunnerRelationIdDoesNotExist(c *gc.C) { 44 for _, value := range []bool{true, false} { 45 _, err := s.factory.NewCommandRunner(context.CommandInfo{ 46 RelationId: 12, ForceRemoteUnit: value, 47 }) 48 c.Check(err, gc.ErrorMatches, `unknown relation id: 12`) 49 } 50 } 51 52 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInvalid(c *gc.C) { 53 for _, value := range []bool{true, false} { 54 _, err := s.factory.NewCommandRunner(context.CommandInfo{ 55 RelationId: 0, RemoteUnitName: "blah", ForceRemoteUnit: value, 56 }) 57 c.Check(err, gc.ErrorMatches, `invalid remote unit: blah`) 58 } 59 } 60 61 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInappropriate(c *gc.C) { 62 for _, value := range []bool{true, false} { 63 _, err := s.factory.NewCommandRunner(context.CommandInfo{ 64 RelationId: -1, RemoteUnitName: "blah/123", ForceRemoteUnit: value, 65 }) 66 c.Check(err, gc.ErrorMatches, `remote unit provided without a relation: blah/123`) 67 } 68 } 69 70 func (s *FactorySuite) TestNewCommandRunnerEmptyRelation(c *gc.C) { 71 _, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: 1}) 72 c.Check(err, gc.ErrorMatches, `cannot infer remote unit in empty relation 1`) 73 } 74 75 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitAmbiguous(c *gc.C) { 76 s.membership[1] = []string{"foo/0", "foo/1"} 77 _, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: 1}) 78 c.Check(err, gc.ErrorMatches, `ambiguous remote unit; possibilities are \[foo/0 foo/1\]`) 79 } 80 81 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitMissing(c *gc.C) { 82 s.membership[0] = []string{"foo/0", "foo/1"} 83 _, err := s.factory.NewCommandRunner(context.CommandInfo{ 84 RelationId: 0, RemoteUnitName: "blah/123", 85 }) 86 c.Check(err, gc.ErrorMatches, `unknown remote unit blah/123; possibilities are \[foo/0 foo/1\]`) 87 } 88 89 func (s *FactorySuite) TestNewCommandRunnerForceNoRemoteUnit(c *gc.C) { 90 rnr, err := s.factory.NewCommandRunner(context.CommandInfo{ 91 RelationId: 0, ForceRemoteUnit: true, 92 }) 93 c.Assert(err, jc.ErrorIsNil) 94 s.AssertPaths(c, rnr) 95 } 96 97 func (s *FactorySuite) TestNewCommandRunnerForceRemoteUnitMissing(c *gc.C) { 98 _, err := s.factory.NewCommandRunner(context.CommandInfo{ 99 RelationId: 0, RemoteUnitName: "blah/123", ForceRemoteUnit: true, 100 }) 101 c.Assert(err, gc.IsNil) 102 } 103 104 func (s *FactorySuite) TestNewCommandRunnerInferRemoteUnit(c *gc.C) { 105 s.membership[0] = []string{"foo/2"} 106 rnr, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: 0}) 107 c.Assert(err, jc.ErrorIsNil) 108 s.AssertPaths(c, rnr) 109 } 110 111 func (s *FactorySuite) TestNewHookRunner(c *gc.C) { 112 rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.ConfigChanged}) 113 c.Assert(err, jc.ErrorIsNil) 114 s.AssertPaths(c, rnr) 115 } 116 117 func (s *FactorySuite) TestNewHookRunnerWithBadHook(c *gc.C) { 118 rnr, err := s.factory.NewHookRunner(hook.Info{}) 119 c.Assert(rnr, gc.IsNil) 120 c.Assert(err, gc.ErrorMatches, `unknown hook kind ""`) 121 } 122 123 func (s *FactorySuite) TestNewHookRunnerWithStorage(c *gc.C) { 124 // We need to set up a unit that has storage metadata defined. 125 ch := s.AddTestingCharm(c, "storage-block") 126 sCons := map[string]state.StorageConstraints{ 127 "data": {Pool: "", Size: 1024, Count: 1}, 128 } 129 application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, sCons) 130 s.machine = nil // allocate a new machine 131 unit := s.AddUnit(c, application) 132 133 sb, err := state.NewStorageBackend(s.State) 134 c.Assert(err, jc.ErrorIsNil) 135 storageAttachments, err := sb.UnitStorageAttachments(unit.UnitTag()) 136 c.Assert(err, jc.ErrorIsNil) 137 c.Assert(storageAttachments, gc.HasLen, 1) 138 storageTag := storageAttachments[0].StorageInstance() 139 140 volume, err := sb.StorageInstanceVolume(storageTag) 141 c.Assert(err, jc.ErrorIsNil) 142 volumeTag := volume.VolumeTag() 143 machineTag := s.machine.MachineTag() 144 145 err = sb.SetVolumeInfo( 146 volumeTag, state.VolumeInfo{ 147 VolumeId: "vol-123", 148 Size: 456, 149 }, 150 ) 151 c.Assert(err, jc.ErrorIsNil) 152 err = sb.SetVolumeAttachmentInfo( 153 machineTag, volumeTag, state.VolumeAttachmentInfo{ 154 DeviceName: "sdb", 155 }, 156 ) 157 c.Assert(err, jc.ErrorIsNil) 158 159 password, err := utils.RandomPassword() 160 err = unit.SetPassword(password) 161 c.Assert(err, jc.ErrorIsNil) 162 st := s.OpenAPIAs(c, unit.Tag(), password) 163 uniter, err := st.Uniter() 164 c.Assert(err, jc.ErrorIsNil) 165 166 contextFactory, err := context.NewContextFactory(context.FactoryConfig{ 167 State: uniter, 168 UnitTag: unit.Tag().(names.UnitTag), 169 Tracker: runnertesting.FakeTracker{}, 170 GetRelationInfos: s.getRelationInfos, 171 Storage: s.storage, 172 Paths: s.paths, 173 Clock: testclock.NewClock(time.Time{}), 174 }) 175 c.Assert(err, jc.ErrorIsNil) 176 factory, err := runner.NewFactory( 177 uniter, 178 s.paths, 179 contextFactory, 180 ) 181 c.Assert(err, jc.ErrorIsNil) 182 183 rnr, err := factory.NewHookRunner(hook.Info{ 184 Kind: hooks.StorageAttached, 185 StorageId: "data/0", 186 }) 187 c.Assert(err, jc.ErrorIsNil) 188 s.AssertPaths(c, rnr) 189 ctx := rnr.Context() 190 c.Assert(ctx.UnitName(), gc.Equals, "storage-block/0") 191 } 192 193 func (s *FactorySuite) TestNewHookRunnerWithRelation(c *gc.C) { 194 rnr, err := s.factory.NewHookRunner(hook.Info{ 195 Kind: hooks.RelationBroken, 196 RelationId: 1, 197 }) 198 c.Assert(err, jc.ErrorIsNil) 199 s.AssertPaths(c, rnr) 200 } 201 202 func (s *FactorySuite) TestNewHookRunnerWithBadRelation(c *gc.C) { 203 rnr, err := s.factory.NewHookRunner(hook.Info{ 204 Kind: hooks.RelationBroken, 205 RelationId: 12345, 206 }) 207 c.Assert(rnr, gc.IsNil) 208 c.Assert(err, gc.ErrorMatches, `unknown relation id: 12345`) 209 } 210 211 func (s *FactorySuite) TestNewActionRunnerGood(c *gc.C) { 212 s.SetCharm(c, "dummy") 213 for i, test := range []struct { 214 actionName string 215 payload map[string]interface{} 216 }{ 217 { 218 actionName: "snapshot", 219 payload: map[string]interface{}{ 220 "outfile": "/some/file.bz2", 221 }, 222 }, 223 { 224 // juju-run should work as a predefined action even if 225 // it's not part of the charm 226 actionName: "juju-run", 227 payload: map[string]interface{}{ 228 "command": "foo", 229 "timeout": 0.0, 230 }, 231 }, 232 } { 233 c.Logf("test %d", i) 234 action, err := s.model.EnqueueAction(s.unit.Tag(), test.actionName, test.payload) 235 c.Assert(err, jc.ErrorIsNil) 236 rnr, err := s.factory.NewActionRunner(action.Id()) 237 c.Assert(err, jc.ErrorIsNil) 238 s.AssertPaths(c, rnr) 239 ctx := rnr.Context() 240 data, err := ctx.ActionData() 241 c.Assert(err, jc.ErrorIsNil) 242 c.Assert(data, jc.DeepEquals, &context.ActionData{ 243 Name: test.actionName, 244 Tag: action.ActionTag(), 245 Params: test.payload, 246 ResultsMap: map[string]interface{}{}, 247 }) 248 vars, err := ctx.HookVars(s.paths) 249 c.Assert(err, jc.ErrorIsNil) 250 c.Assert(len(vars) > 0, jc.IsTrue, gc.Commentf("expected HookVars but found none")) 251 combined := strings.Join(vars, "|") 252 c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_NAME=`+test.actionName+`(\|.*|$)`) 253 c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_UUID=`+action.Id()+`(\|.*|$)`) 254 c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_TAG=`+action.Tag().String()+`(\|.*|$)`) 255 } 256 } 257 258 func (s *FactorySuite) TestNewActionRunnerBadCharm(c *gc.C) { 259 rnr, err := s.factory.NewActionRunner("irrelevant") 260 c.Assert(rnr, gc.IsNil) 261 c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist) 262 c.Assert(err, gc.Not(jc.Satisfies), charmrunner.IsBadActionError) 263 } 264 265 func (s *FactorySuite) TestNewActionRunnerBadName(c *gc.C) { 266 s.SetCharm(c, "dummy") 267 action, err := s.model.EnqueueAction(s.unit.Tag(), "no-such-action", nil) 268 c.Assert(err, jc.ErrorIsNil) // this will fail when using AddAction on unit 269 rnr, err := s.factory.NewActionRunner(action.Id()) 270 c.Check(rnr, gc.IsNil) 271 c.Check(err, gc.ErrorMatches, "cannot run \"no-such-action\" action: not defined") 272 c.Check(err, jc.Satisfies, charmrunner.IsBadActionError) 273 } 274 275 func (s *FactorySuite) TestNewActionRunnerBadParams(c *gc.C) { 276 s.SetCharm(c, "dummy") 277 action, err := s.model.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{ 278 "outfile": 123, 279 }) 280 c.Assert(err, jc.ErrorIsNil) // this will fail when state is done right 281 rnr, err := s.factory.NewActionRunner(action.Id()) 282 c.Check(rnr, gc.IsNil) 283 c.Check(err, gc.ErrorMatches, "cannot run \"snapshot\" action: .*") 284 c.Check(err, jc.Satisfies, charmrunner.IsBadActionError) 285 } 286 287 func (s *FactorySuite) TestNewActionRunnerMissingAction(c *gc.C) { 288 s.SetCharm(c, "dummy") 289 action, err := s.model.EnqueueAction(s.unit.Tag(), "snapshot", nil) 290 c.Assert(err, jc.ErrorIsNil) 291 _, err = s.unit.CancelAction(action) 292 c.Assert(err, jc.ErrorIsNil) 293 rnr, err := s.factory.NewActionRunner(action.Id()) 294 c.Check(rnr, gc.IsNil) 295 c.Check(err, gc.ErrorMatches, "action no longer available") 296 c.Check(err, gc.Equals, charmrunner.ErrActionNotAvailable) 297 } 298 299 func (s *FactorySuite) TestNewActionRunnerUnauthAction(c *gc.C) { 300 s.SetCharm(c, "dummy") 301 otherUnit, err := s.application.AddUnit(state.AddUnitParams{}) 302 c.Assert(err, jc.ErrorIsNil) 303 action, err := s.model.EnqueueAction(otherUnit.Tag(), "snapshot", nil) 304 c.Assert(err, jc.ErrorIsNil) 305 rnr, err := s.factory.NewActionRunner(action.Id()) 306 c.Check(rnr, gc.IsNil) 307 c.Check(err, gc.ErrorMatches, "action no longer available") 308 c.Check(err, gc.Equals, charmrunner.ErrActionNotAvailable) 309 }