github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/controller/machineundertaker/undertaker_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package machineundertaker_test 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/testing" 9 jc "github.com/juju/testing/checkers" 10 gc "gopkg.in/check.v1" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/apiserver/facades/controller/machineundertaker" 14 "github.com/juju/juju/apiserver/params" 15 apiservertesting "github.com/juju/juju/apiserver/testing" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/state" 18 ) 19 20 type undertakerSuite struct { 21 testing.IsolationSuite 22 } 23 24 var _ = gc.Suite(&undertakerSuite{}) 25 26 const ( 27 uuid1 = "12345678-1234-1234-1234-123456789abc" 28 tag1 = "model-12345678-1234-1234-1234-123456789abc" 29 uuid2 = "12345678-1234-1234-1234-123456789abd" 30 tag2 = "model-12345678-1234-1234-1234-123456789abd" 31 ) 32 33 func (*undertakerSuite) TestRequiresModelManager(c *gc.C) { 34 backend := &mockBackend{} 35 _, err := machineundertaker.NewAPI( 36 backend, 37 nil, 38 apiservertesting.FakeAuthorizer{Controller: false}, 39 ) 40 c.Assert(err, gc.ErrorMatches, "permission denied") 41 _, err = machineundertaker.NewAPI( 42 backend, 43 nil, 44 apiservertesting.FakeAuthorizer{Controller: true}, 45 ) 46 c.Assert(err, jc.ErrorIsNil) 47 } 48 49 func (*undertakerSuite) TestAllMachineRemovalsNoResults(c *gc.C) { 50 _, _, api := makeAPI(c, uuid1) 51 result := api.AllMachineRemovals(makeEntities(tag1)) 52 c.Assert(result, gc.DeepEquals, params.EntitiesResults{ 53 Results: []params.EntitiesResult{{}}, // So, one empty set of entities. 54 }) 55 } 56 57 func (*undertakerSuite) TestAllMachineRemovalsError(c *gc.C) { 58 backend, _, api := makeAPI(c, uuid1) 59 backend.SetErrors(errors.New("I don't want to set the world on fire")) 60 result := api.AllMachineRemovals(makeEntities(tag1)) 61 c.Assert(result.Results, gc.HasLen, 1) 62 c.Assert(result.Results[0].Error, gc.ErrorMatches, "I don't want to set the world on fire") 63 c.Assert(result.Results[0].Entities, jc.DeepEquals, []params.Entity{}) 64 } 65 66 func (*undertakerSuite) TestAllMachineRemovalsRequiresModelTags(c *gc.C) { 67 _, _, api := makeAPI(c, uuid1) 68 results := api.AllMachineRemovals(makeEntities(tag1, "machine-0")) 69 c.Assert(results.Results, gc.HasLen, 2) 70 c.Assert(results.Results[0].Error, gc.IsNil) 71 c.Assert(results.Results[0].Entities, jc.DeepEquals, []params.Entity{}) 72 c.Assert(results.Results[1].Error, gc.ErrorMatches, `"machine-0" is not a valid model tag`) 73 c.Assert(results.Results[1].Entities, jc.DeepEquals, []params.Entity{}) 74 } 75 76 func (*undertakerSuite) TestAllMachineRemovalsChecksModelTag(c *gc.C) { 77 _, _, api := makeAPI(c, uuid1) 78 results := api.AllMachineRemovals(makeEntities(tag2)) 79 c.Assert(results.Results, gc.HasLen, 1) 80 c.Assert(results.Results[0].Error, gc.ErrorMatches, "permission denied") 81 c.Assert(results.Results[0].Entities, gc.IsNil) 82 } 83 84 func (*undertakerSuite) TestAllMachineRemovals(c *gc.C) { 85 backend, _, api := makeAPI(c, uuid1) 86 backend.removals = []string{"0", "2"} 87 88 result := api.AllMachineRemovals(makeEntities(tag1)) 89 c.Assert(result, gc.DeepEquals, makeEntitiesResults("machine-0", "machine-2")) 90 } 91 92 func (*undertakerSuite) TestGetMachineProviderInterfaceInfo(c *gc.C) { 93 backend, _, api := makeAPI(c, "") 94 backend.machines = map[string]*mockMachine{ 95 "0": { 96 Stub: &testing.Stub{}, 97 interfaceInfos: []network.ProviderInterfaceInfo{{ 98 InterfaceName: "billy", 99 MACAddress: "hexadecimal!", 100 ProviderId: "a number", 101 }, { 102 InterfaceName: "lily", 103 MACAddress: "octal?", 104 ProviderId: "different number", 105 }}}, 106 "2": { 107 Stub: &testing.Stub{}, 108 interfaceInfos: []network.ProviderInterfaceInfo{{ 109 InterfaceName: "gilly", 110 MACAddress: "sexagesimal?!", 111 ProviderId: "some number", 112 }}, 113 }, 114 } 115 backend.SetErrors(nil, errors.NotFoundf("no machine 100 fool!")) 116 117 args := makeEntities("machine-2", "machine-100", "machine-0", "machine-inv") 118 result := api.GetMachineProviderInterfaceInfo(args) 119 120 c.Assert(result, gc.DeepEquals, params.ProviderInterfaceInfoResults{ 121 Results: []params.ProviderInterfaceInfoResult{{ 122 MachineTag: "machine-2", 123 Interfaces: []params.ProviderInterfaceInfo{{ 124 InterfaceName: "gilly", 125 MACAddress: "sexagesimal?!", 126 ProviderId: "some number", 127 }}, 128 }, { 129 MachineTag: "machine-100", 130 Error: common.ServerError( 131 errors.NotFoundf("no machine 100 fool!"), 132 ), 133 }, { 134 MachineTag: "machine-0", 135 Interfaces: []params.ProviderInterfaceInfo{{ 136 InterfaceName: "billy", 137 MACAddress: "hexadecimal!", 138 ProviderId: "a number", 139 }, { 140 InterfaceName: "lily", 141 MACAddress: "octal?", 142 ProviderId: "different number", 143 }}, 144 }, { 145 MachineTag: "machine-inv", 146 Error: common.ServerError( 147 errors.New(`"machine-inv" is not a valid machine tag`), 148 ), 149 }}, 150 }) 151 } 152 153 func (*undertakerSuite) TestGetMachineProviderInterfaceInfoHandlesError(c *gc.C) { 154 backend, _, api := makeAPI(c, "") 155 backend.machines = map[string]*mockMachine{ 156 "0": {Stub: backend.Stub}, 157 } 158 backend.SetErrors(nil, errors.New("oops - problem getting interface infos")) 159 result := api.GetMachineProviderInterfaceInfo(makeEntities("machine-0")) 160 161 c.Assert(result.Results, gc.DeepEquals, []params.ProviderInterfaceInfoResult{{ 162 MachineTag: "machine-0", 163 Error: common.ServerError(errors.New("oops - problem getting interface infos")), 164 }}) 165 } 166 167 func (*undertakerSuite) TestCompleteMachineRemovalsWithNonMachineTags(c *gc.C) { 168 _, _, api := makeAPI(c, "") 169 err := api.CompleteMachineRemovals(makeEntities("machine-2", "application-a1")) 170 c.Assert(err, gc.ErrorMatches, `"application-a1" is not a valid machine tag`) 171 } 172 173 func (*undertakerSuite) TestCompleteMachineRemovalsWithOtherError(c *gc.C) { 174 backend, _, api := makeAPI(c, "") 175 backend.SetErrors(errors.New("boom")) 176 err := api.CompleteMachineRemovals(makeEntities("machine-2")) 177 c.Assert(err, gc.ErrorMatches, "boom") 178 } 179 180 func (*undertakerSuite) TestCompleteMachineRemovals(c *gc.C) { 181 backend, _, api := makeAPI(c, "") 182 err := api.CompleteMachineRemovals(makeEntities("machine-2", "machine-52")) 183 c.Assert(err, jc.ErrorIsNil) 184 backend.CheckCallNames(c, "CompleteMachineRemovals") 185 callArgs := backend.Calls()[0].Args 186 c.Assert(len(callArgs), gc.Equals, 1) 187 values, ok := callArgs[0].([]string) 188 c.Assert(ok, jc.IsTrue) 189 c.Assert(values, gc.DeepEquals, []string{"2", "52"}) 190 } 191 192 func (*undertakerSuite) TestWatchMachineRemovals(c *gc.C) { 193 backend, res, api := makeAPI(c, uuid1) 194 195 result := api.WatchMachineRemovals(makeEntities(tag1)) 196 c.Assert(result.Results, gc.HasLen, 1) 197 c.Assert(res.Get(result.Results[0].NotifyWatcherId), gc.NotNil) 198 c.Assert(result.Results[0].Error, gc.IsNil) 199 backend.CheckCallNames(c, "WatchMachineRemovals") 200 } 201 202 func (*undertakerSuite) TestWatchMachineRemovalsPermissionError(c *gc.C) { 203 _, _, api := makeAPI(c, uuid1) 204 result := api.WatchMachineRemovals(makeEntities(tag2)) 205 c.Assert(result.Results, gc.HasLen, 1) 206 c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied") 207 } 208 209 func (*undertakerSuite) TestWatchMachineRemovalsError(c *gc.C) { 210 backend, _, api := makeAPI(c, uuid1) 211 backend.watcherBlowsUp = true 212 backend.SetErrors(errors.New("oh no!")) 213 214 result := api.WatchMachineRemovals(makeEntities(tag1)) 215 c.Assert(result.Results, gc.HasLen, 1) 216 c.Assert(result.Results[0].Error, gc.ErrorMatches, "oh no!") 217 c.Assert(result.Results[0].NotifyWatcherId, gc.Equals, "") 218 backend.CheckCallNames(c, "WatchMachineRemovals") 219 } 220 221 func makeAPI(c *gc.C, modelUUID string) (*mockBackend, *common.Resources, *machineundertaker.API) { 222 backend := &mockBackend{Stub: &testing.Stub{}} 223 res := common.NewResources() 224 api, err := machineundertaker.NewAPI( 225 backend, 226 res, 227 apiservertesting.FakeAuthorizer{ 228 Controller: true, 229 ModelUUID: modelUUID, 230 }, 231 ) 232 c.Assert(err, jc.ErrorIsNil) 233 return backend, res, api 234 } 235 236 func makeEntities(tags ...string) params.Entities { 237 return params.Entities{Entities: makeEntitySlice(tags...)} 238 } 239 240 func makeEntitySlice(tags ...string) []params.Entity { 241 entities := make([]params.Entity, len(tags)) 242 for i := range tags { 243 entities[i] = params.Entity{Tag: tags[i]} 244 } 245 return entities 246 } 247 248 func makeEntitiesResults(tags ...string) params.EntitiesResults { 249 return params.EntitiesResults{ 250 Results: []params.EntitiesResult{{ 251 Entities: makeEntitySlice(tags...), 252 }}, 253 } 254 } 255 256 type mockBackend struct { 257 *testing.Stub 258 259 removals []string 260 machines map[string]*mockMachine 261 watcherBlowsUp bool 262 } 263 264 func (b *mockBackend) AllMachineRemovals() ([]string, error) { 265 b.AddCall("AllMachineRemovals") 266 return b.removals, b.NextErr() 267 } 268 269 func (b *mockBackend) CompleteMachineRemovals(machineIDs ...string) error { 270 b.AddCall("CompleteMachineRemovals", machineIDs) 271 return b.NextErr() 272 } 273 274 func (b *mockBackend) WatchMachineRemovals() state.NotifyWatcher { 275 b.AddCall("WatchMachineRemovals") 276 watcher := &mockWatcher{backend: b, out: make(chan struct{}, 1)} 277 if b.watcherBlowsUp { 278 close(watcher.out) 279 } else { 280 watcher.out <- struct{}{} 281 } 282 return watcher 283 } 284 285 func (b *mockBackend) Machine(id string) (machineundertaker.Machine, error) { 286 b.AddCall("Machine", id) 287 return b.machines[id], b.NextErr() 288 } 289 290 type mockMachine struct { 291 *testing.Stub 292 interfaceInfos []network.ProviderInterfaceInfo 293 } 294 295 func (m *mockMachine) AllProviderInterfaceInfos() ([]network.ProviderInterfaceInfo, error) { 296 m.AddCall("AllProviderInterfaceInfos") 297 err := m.NextErr() 298 if err != nil { 299 return nil, err 300 } 301 return m.interfaceInfos, err 302 } 303 304 type mockWatcher struct { 305 state.NotifyWatcher 306 307 backend *mockBackend 308 out chan struct{} 309 } 310 311 func (w *mockWatcher) Changes() <-chan struct{} { 312 return w.out 313 } 314 315 func (w *mockWatcher) Err() error { 316 return w.backend.NextErr() 317 }