github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/machine_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/names/v5" 11 "github.com/juju/naturalsort" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/core/instance" 17 "github.com/juju/juju/core/model" 18 "github.com/juju/juju/core/status" 19 "github.com/juju/juju/rpc/params" 20 "github.com/juju/juju/state" 21 ) 22 23 type machineSuite struct{} 24 25 var _ = gc.Suite(&machineSuite{}) 26 27 func (s *machineSuite) TestMachineJobFromParams(c *gc.C) { 28 var tests = []struct { 29 name model.MachineJob 30 want state.MachineJob 31 err string 32 }{{ 33 name: model.JobHostUnits, 34 want: state.JobHostUnits, 35 }, { 36 name: model.JobManageModel, 37 want: state.JobManageModel, 38 }, { 39 name: "invalid", 40 want: -1, 41 err: `invalid machine job "invalid"`, 42 }} 43 for _, test := range tests { 44 got, err := common.MachineJobFromParams(test.name) 45 if err != nil { 46 c.Check(err, gc.ErrorMatches, test.err) 47 } 48 c.Check(got, gc.Equals, test.want) 49 } 50 } 51 52 const ( 53 dontWait = time.Duration(0) 54 ) 55 56 func (s *machineSuite) TestDestroyMachines(c *gc.C) { 57 st := mockState{ 58 machines: map[string]*mockMachine{ 59 "1": {}, 60 "2": {destroyErr: errors.New("unit exists error")}, 61 "3": {life: state.Dying}, 62 }, 63 } 64 err := common.MockableDestroyMachines(&st, false, dontWait, "1", "2", "3", "4") 65 66 c.Assert(st.machines["1"].Life(), gc.Equals, state.Dying) 67 c.Assert(st.machines["1"].forceDestroyCalled, jc.IsFalse) 68 69 c.Assert(st.machines["2"].Life(), gc.Equals, state.Alive) 70 c.Assert(st.machines["2"].forceDestroyCalled, jc.IsFalse) 71 72 c.Assert(st.machines["3"].forceDestroyCalled, jc.IsFalse) 73 c.Assert(st.machines["3"].destroyCalled, jc.IsFalse) 74 75 c.Assert(err, gc.ErrorMatches, "some machines were not destroyed: unit exists error; machine 4 does not exist") 76 } 77 78 func (s *machineSuite) TestForceDestroyMachines(c *gc.C) { 79 st := mockState{ 80 machines: map[string]*mockMachine{ 81 "1": {}, 82 "2": {life: state.Dying}, 83 }, 84 } 85 err := common.MockableDestroyMachines(&st, true, dontWait, "1", "2") 86 87 c.Assert(st.machines["1"].Life(), gc.Equals, state.Dying) 88 c.Assert(st.machines["1"].forceDestroyCalled, jc.IsTrue) 89 c.Assert(st.machines["2"].forceDestroyCalled, jc.IsTrue) 90 91 c.Assert(err, jc.ErrorIsNil) 92 } 93 94 func (s *machineSuite) TestMachineHardwareInfo(c *gc.C) { 95 one := uint64(1) 96 amd64 := "amd64" 97 gig := uint64(1024) 98 st := mockState{ 99 machines: map[string]*mockMachine{ 100 "1": {id: "1", life: state.Alive, containerType: instance.NONE, 101 hw: &instance.HardwareCharacteristics{ 102 Arch: &amd64, 103 Mem: &gig, 104 CpuCores: &one, 105 CpuPower: &one, 106 }}, 107 "2": {id: "2", life: state.Alive, containerType: instance.LXD}, 108 "3": {life: state.Dying}, 109 }, 110 } 111 info, err := common.ModelMachineInfo(&st) 112 c.Assert(err, jc.ErrorIsNil) 113 c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{ 114 { 115 Id: "1", 116 DisplayName: "", 117 Hardware: ¶ms.MachineHardware{ 118 Arch: &amd64, 119 Mem: &gig, 120 Cores: &one, 121 CpuPower: &one, 122 }, 123 }, { 124 Id: "2", 125 DisplayName: "", 126 }, 127 }) 128 } 129 130 func (s *machineSuite) TestMachineInstanceInfo(c *gc.C) { 131 st := mockState{ 132 machines: map[string]*mockMachine{ 133 "1": { 134 id: "1", 135 instId: "123", 136 status: status.Down, 137 }, 138 "2": { 139 id: "2", 140 instId: "456", 141 displayName: "two", 142 status: status.Allocating, 143 }, 144 }, 145 controllerNodes: map[string]*mockControllerNode{ 146 "1": { 147 id: "1", 148 hasVote: true, 149 wantsVote: true, 150 }, 151 "2": { 152 id: "2", 153 hasVote: false, 154 wantsVote: true, 155 }, 156 }, 157 } 158 info, err := common.ModelMachineInfo(&st) 159 c.Assert(err, jc.ErrorIsNil) 160 c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{ 161 { 162 Id: "1", 163 InstanceId: "123", 164 Status: "down", 165 HasVote: true, 166 WantsVote: true, 167 }, 168 { 169 Id: "2", 170 InstanceId: "456", 171 DisplayName: "two", 172 Status: "allocating", 173 HasVote: false, 174 WantsVote: true, 175 }, 176 }) 177 } 178 179 func (s *machineSuite) TestMachineInstanceInfoWithEmptyDisplayName(c *gc.C) { 180 st := mockState{ 181 machines: map[string]*mockMachine{ 182 "1": { 183 id: "1", 184 instId: "123", 185 displayName: "", 186 status: status.Down, 187 }, 188 }, 189 controllerNodes: map[string]*mockControllerNode{ 190 "1": { 191 id: "1", 192 hasVote: true, 193 wantsVote: true, 194 }, 195 }, 196 } 197 info, err := common.ModelMachineInfo(&st) 198 c.Assert(err, jc.ErrorIsNil) 199 c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{ 200 { 201 Id: "1", 202 InstanceId: "123", 203 DisplayName: "", 204 Status: "down", 205 HasVote: true, 206 WantsVote: true, 207 }, 208 }) 209 } 210 211 func (s *machineSuite) TestMachineInstanceInfoWithSetDisplayName(c *gc.C) { 212 st := mockState{ 213 machines: map[string]*mockMachine{ 214 "1": { 215 id: "1", 216 instId: "123", 217 displayName: "snowflake", 218 status: status.Down, 219 }, 220 }, 221 controllerNodes: map[string]*mockControllerNode{ 222 "1": { 223 id: "1", 224 hasVote: true, 225 wantsVote: true, 226 }, 227 }, 228 } 229 info, err := common.ModelMachineInfo(&st) 230 c.Assert(err, jc.ErrorIsNil) 231 c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{ 232 { 233 Id: "1", 234 InstanceId: "123", 235 DisplayName: "snowflake", 236 Status: "down", 237 HasVote: true, 238 WantsVote: true, 239 }, 240 }) 241 } 242 243 func (s *machineSuite) TestMachineInstanceInfoWithHAPrimary(c *gc.C) { 244 st := mockState{ 245 machines: map[string]*mockMachine{ 246 "1": { 247 id: "1", 248 instId: "123", 249 displayName: "snowflake", 250 status: status.Down, 251 }, 252 }, 253 controllerNodes: map[string]*mockControllerNode{ 254 "1": { 255 id: "1", 256 hasVote: true, 257 wantsVote: true, 258 }, 259 "2": { 260 id: "1", 261 hasVote: true, 262 wantsVote: true, 263 }, 264 }, 265 haPrimaryMachineF: func() (names.MachineTag, error) { 266 return names.NewMachineTag("1"), nil 267 }, 268 } 269 info, err := common.ModelMachineInfo(&st) 270 c.Assert(err, jc.ErrorIsNil) 271 _true := true 272 c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{ 273 { 274 Id: "1", 275 InstanceId: "123", 276 DisplayName: "snowflake", 277 Status: "down", 278 HasVote: true, 279 WantsVote: true, 280 HAPrimary: &_true, 281 }, 282 }) 283 } 284 285 type mockState struct { 286 common.ModelManagerBackend 287 machines map[string]*mockMachine 288 controllerNodes map[string]*mockControllerNode 289 haPrimaryMachineF func() (names.MachineTag, error) 290 } 291 292 func (st *mockState) Machine(id string) (common.Machine, error) { 293 if m, ok := st.machines[id]; ok { 294 return m, nil 295 } 296 return nil, errors.Errorf("machine %s does not exist", id) 297 } 298 299 func (st *mockState) AllMachines() (machines []common.Machine, _ error) { 300 // Ensure we get machines in id order. 301 var ids []string 302 for id := range st.machines { 303 ids = append(ids, id) 304 } 305 naturalsort.Sort(ids) 306 for _, id := range ids { 307 machines = append(machines, st.machines[id]) 308 } 309 return machines, nil 310 } 311 312 func (st *mockState) ControllerNodes() ([]common.ControllerNode, error) { 313 var result []common.ControllerNode 314 for _, n := range st.controllerNodes { 315 result = append(result, n) 316 } 317 return result, nil 318 } 319 320 func (st *mockState) HAPrimaryMachine() (names.MachineTag, error) { 321 if st.haPrimaryMachineF == nil { 322 return names.MachineTag{}, nil 323 } 324 return st.haPrimaryMachineF() 325 } 326 327 type mockControllerNode struct { 328 id string 329 hasVote bool 330 wantsVote bool 331 } 332 333 func (m *mockControllerNode) Id() string { 334 return m.id 335 } 336 337 func (m *mockControllerNode) WantsVote() bool { 338 return m.wantsVote 339 } 340 341 func (m *mockControllerNode) HasVote() bool { 342 return m.hasVote 343 } 344 345 type mockMachine struct { 346 state.Machine 347 id string 348 life state.Life 349 containerType instance.ContainerType 350 hw *instance.HardwareCharacteristics 351 instId instance.Id 352 displayName string 353 status status.Status 354 statusErr error 355 destroyErr error 356 forceDestroyErr error 357 forceDestroyCalled bool 358 destroyCalled bool 359 } 360 361 func (m *mockMachine) Id() string { 362 return m.id 363 } 364 365 func (m *mockMachine) Life() state.Life { 366 return m.life 367 } 368 369 func (m *mockMachine) InstanceId() (instance.Id, error) { 370 return m.instId, nil 371 } 372 373 func (m *mockMachine) InstanceNames() (instance.Id, string, error) { 374 instId, err := m.InstanceId() 375 return instId, m.displayName, err 376 } 377 378 func (m *mockMachine) Status() (status.StatusInfo, error) { 379 return status.StatusInfo{ 380 Status: m.status, 381 }, m.statusErr 382 } 383 384 func (m *mockMachine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) { 385 return m.hw, nil 386 } 387 388 func (m *mockMachine) ForceDestroy(time.Duration) error { 389 m.forceDestroyCalled = true 390 if m.forceDestroyErr != nil { 391 return m.forceDestroyErr 392 } 393 m.life = state.Dying 394 return nil 395 } 396 397 func (m *mockMachine) Destroy() error { 398 m.destroyCalled = true 399 if m.destroyErr != nil { 400 return m.destroyErr 401 } 402 m.life = state.Dying 403 return nil 404 }