github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/api/controller/instancepoller/machine_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package instancepoller_test 5 6 import ( 7 "reflect" 8 "time" 9 10 "github.com/juju/names/v5" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 apitesting "github.com/juju/juju/api/base/testing" 15 "github.com/juju/juju/api/controller/instancepoller" 16 apiservertesting "github.com/juju/juju/apiserver/testing" 17 "github.com/juju/juju/core/instance" 18 "github.com/juju/juju/core/life" 19 "github.com/juju/juju/core/network" 20 "github.com/juju/juju/core/status" 21 "github.com/juju/juju/rpc/params" 22 coretesting "github.com/juju/juju/testing" 23 ) 24 25 type MachineSuite struct { 26 coretesting.BaseSuite 27 28 tag names.MachineTag 29 } 30 31 var _ = gc.Suite(&MachineSuite{}) 32 33 func (s *MachineSuite) SetUpTest(c *gc.C) { 34 s.BaseSuite.SetUpTest(c) 35 s.tag = names.NewMachineTag("42") 36 } 37 38 func (s *MachineSuite) TestNonFacadeMethods(c *gc.C) { 39 nopCaller := apitesting.APICallerFunc( 40 func(_ string, _ int, _, _ string, _, _ interface{}) error { 41 c.Fatalf("facade call was not expected") 42 return nil 43 }, 44 ) 45 machine := instancepoller.NewMachine(nopCaller, s.tag, life.Dying) 46 47 c.Assert(machine.Id(), gc.Equals, "42") 48 c.Assert(machine.Tag(), jc.DeepEquals, s.tag) 49 c.Assert(machine.String(), gc.Equals, "42") 50 c.Assert(machine.Life(), gc.Equals, life.Dying) 51 } 52 53 // methodWrapper wraps a Machine method call and returns the error, 54 // ignoring the result (if any). 55 type methodWrapper func(*instancepoller.Machine) error 56 57 // machineErrorTests contains all the necessary information to test 58 // how each Machine method handles client- and server-side API errors, 59 // as well as the case when the server-side API returns more results 60 // than expected. 61 var machineErrorTests = []struct { 62 method string // only for logging 63 wrapper methodWrapper 64 resultsRef interface{} // an instance of the server-side method's result type 65 }{{ 66 method: "Refresh", 67 wrapper: (*instancepoller.Machine).Refresh, 68 resultsRef: params.LifeResults{}, 69 }, { 70 method: "IsManual", 71 wrapper: func(m *instancepoller.Machine) error { 72 _, err := m.IsManual() 73 return err 74 }, 75 resultsRef: params.BoolResults{}, 76 }, { 77 method: "InstanceId", 78 wrapper: func(m *instancepoller.Machine) error { 79 _, err := m.InstanceId() 80 return err 81 }, 82 resultsRef: params.StringResults{}, 83 }, { 84 method: "Status", 85 wrapper: func(m *instancepoller.Machine) error { 86 _, err := m.Status() 87 return err 88 }, 89 resultsRef: params.StatusResults{}, 90 }, { 91 method: "InstanceStatus", 92 wrapper: func(m *instancepoller.Machine) error { 93 _, err := m.InstanceStatus() 94 return err 95 }, 96 resultsRef: params.StatusResults{}, 97 }, { 98 method: "SetInstanceStatus", 99 wrapper: func(m *instancepoller.Machine) error { 100 return m.SetInstanceStatus("", "", nil) 101 }, 102 resultsRef: params.ErrorResults{}, 103 }, { 104 method: "SetProviderNetworkConfig", 105 wrapper: func(m *instancepoller.Machine) error { 106 _, _, err := m.SetProviderNetworkConfig(nil) 107 return err 108 }, 109 resultsRef: params.SetProviderNetworkConfigResults{}, 110 }} 111 112 func (s *MachineSuite) TestClientError(c *gc.C) { 113 for i, test := range machineErrorTests { 114 c.Logf("test #%d: %s", i, test.method) 115 s.CheckClientError(c, test.wrapper) 116 } 117 } 118 119 func (s *MachineSuite) TestServerError(c *gc.C) { 120 err := apiservertesting.ServerError("server error!") 121 expected := err.Error() 122 for i, test := range machineErrorTests { 123 c.Logf("test #%d: %s", i, test.method) 124 results := MakeResultsWithErrors(test.resultsRef, err, 1) 125 s.CheckServerError(c, test.wrapper, expected, results) 126 } 127 } 128 129 func (s *MachineSuite) TestTooManyResultsServerError(c *gc.C) { 130 err := apiservertesting.ServerError("some error") 131 expected := "expected 1 result, got 2" 132 for i, test := range machineErrorTests { 133 c.Logf("test #%d: %s", i, test.method) 134 results := MakeResultsWithErrors(test.resultsRef, err, 2) 135 s.CheckServerError(c, test.wrapper, expected, results) 136 } 137 } 138 139 func (s *MachineSuite) TestRefreshSuccess(c *gc.C) { 140 results := params.LifeResults{ 141 Results: []params.LifeResult{{Life: life.Dying}}, 142 } 143 apiCaller := successAPICaller(c, "Life", entitiesArgs, results) 144 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 145 c.Check(machine.Refresh(), jc.ErrorIsNil) 146 c.Check(machine.Life(), gc.Equals, life.Dying) 147 c.Check(apiCaller.CallCount, gc.Equals, 1) 148 } 149 150 func (s *MachineSuite) TestStatusSuccess(c *gc.C) { 151 now := time.Now() 152 expectStatus := params.StatusResult{ 153 Status: "foo", 154 Info: "bar", 155 Data: map[string]interface{}{ 156 "int": 42, 157 "bool": true, 158 "float": 3.14, 159 "slice": []string{"a", "b"}, 160 "map": map[int]string{5: "five"}, 161 "string": "argh", 162 }, 163 Since: &now, 164 } 165 results := params.StatusResults{Results: []params.StatusResult{expectStatus}} 166 apiCaller := successAPICaller(c, "Status", entitiesArgs, results) 167 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 168 status, err := machine.Status() 169 c.Check(err, jc.ErrorIsNil) 170 c.Check(status, jc.DeepEquals, expectStatus) 171 c.Check(apiCaller.CallCount, gc.Equals, 1) 172 } 173 174 func (s *MachineSuite) TestIsManualSuccess(c *gc.C) { 175 results := params.BoolResults{ 176 Results: []params.BoolResult{{Result: true}}, 177 } 178 apiCaller := successAPICaller(c, "AreManuallyProvisioned", entitiesArgs, results) 179 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 180 isManual, err := machine.IsManual() 181 c.Check(err, jc.ErrorIsNil) 182 c.Check(isManual, jc.IsTrue) 183 c.Check(apiCaller.CallCount, gc.Equals, 1) 184 } 185 186 func (s *MachineSuite) TestInstanceIdSuccess(c *gc.C) { 187 results := params.StringResults{ 188 Results: []params.StringResult{{Result: "i-foo"}}, 189 } 190 apiCaller := successAPICaller(c, "InstanceId", entitiesArgs, results) 191 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 192 instId, err := machine.InstanceId() 193 c.Check(err, jc.ErrorIsNil) 194 c.Check(instId, gc.Equals, instance.Id("i-foo")) 195 c.Check(apiCaller.CallCount, gc.Equals, 1) 196 } 197 198 func (s *MachineSuite) TestInstanceStatusSuccess(c *gc.C) { 199 results := params.StatusResults{ 200 Results: []params.StatusResult{{ 201 Status: status.Provisioning.String(), 202 }}, 203 } 204 apiCaller := successAPICaller(c, "InstanceStatus", entitiesArgs, results) 205 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 206 statusResult, err := machine.InstanceStatus() 207 c.Check(err, jc.ErrorIsNil) 208 c.Check(statusResult.Status, gc.DeepEquals, status.Provisioning.String()) 209 c.Check(apiCaller.CallCount, gc.Equals, 1) 210 } 211 212 func (s *MachineSuite) TestSetInstanceStatusSuccess(c *gc.C) { 213 expectArgs := params.SetStatus{ 214 Entities: []params.EntityStatusArgs{{ 215 Tag: "machine-42", 216 Status: "RUNNING", 217 }}} 218 results := params.ErrorResults{ 219 Results: []params.ErrorResult{{Error: nil}}, 220 } 221 apiCaller := successAPICaller(c, "SetInstanceStatus", expectArgs, results) 222 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 223 err := machine.SetInstanceStatus("RUNNING", "", nil) 224 c.Check(err, jc.ErrorIsNil) 225 c.Check(apiCaller.CallCount, gc.Equals, 1) 226 } 227 228 func (s *MachineSuite) TestSetProviderNetworkConfigSuccess(c *gc.C) { 229 cfg := network.InterfaceInfos{{ 230 DeviceIndex: 0, 231 Addresses: []network.ProviderAddress{ 232 network.NewMachineAddress("10.0.0.42", network.WithCIDR("10.0.0.0/24")).AsProviderAddress(), 233 }, 234 }} 235 expectArgs := params.SetProviderNetworkConfig{ 236 Args: []params.ProviderNetworkConfig{{ 237 Tag: "machine-42", 238 Configs: params.NetworkConfigFromInterfaceInfo(cfg), 239 }}, 240 } 241 results := params.SetProviderNetworkConfigResults{ 242 Results: []params.SetProviderNetworkConfigResult{{Error: nil}}, 243 } 244 apiCaller := successAPICaller(c, "SetProviderNetworkConfig", expectArgs, results) 245 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 246 _, _, err := machine.SetProviderNetworkConfig(cfg) 247 c.Check(err, jc.ErrorIsNil) 248 c.Check(apiCaller.CallCount, gc.Equals, 1) 249 } 250 251 func (s *MachineSuite) CheckClientError(c *gc.C, wf methodWrapper) { 252 apiCaller := clientErrorAPICaller(c, "", nil) 253 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 254 c.Check(wf(machine), gc.ErrorMatches, "client error!") 255 c.Check(apiCaller.CallCount, gc.Equals, 1) 256 } 257 258 func (s *MachineSuite) CheckServerError(c *gc.C, wf methodWrapper, expectErr string, serverResults interface{}) { 259 apiCaller := successAPICaller(c, "", nil, serverResults) 260 machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive) 261 c.Check(wf(machine), gc.ErrorMatches, expectErr) 262 c.Check(apiCaller.CallCount, gc.Equals, 1) 263 } 264 265 var entitiesArgs = params.Entities{ 266 Entities: []params.Entity{{Tag: "machine-42"}}, 267 } 268 269 // MakeResultsWithErrors constructs a new instance of the results type 270 // (from apiserver/params), matching the given resultsRef, finds its 271 // first field (expected to be a slice, usually "Results") and adds 272 // howMany elements to it, setting the Error field of each element to 273 // err. 274 // 275 // This helper makes a few assumptions: 276 // - resultsRef's type is a struct and has a single field (commonly - "Results") 277 // - that field is a slice of structs, which have an Error field 278 // - the Error field is of type *params.Error 279 // 280 // Example: 281 // 282 // err := apiservertesting.ServerError("foo") 283 // r := MakeResultsWithErrors(params.LifeResults{}, err, 2) 284 // 285 // is equvalent to: 286 // 287 // r := params.LifeResults{Results: []params.LifeResult{{Error: err}, {Error: err}}} 288 func MakeResultsWithErrors(resultsRef interface{}, err *params.Error, howMany int) interface{} { 289 // Make a new instance of the same type as resultsRef. 290 resultsType := reflect.TypeOf(resultsRef) 291 newResults := reflect.New(resultsType).Elem() 292 293 // Make a new empty slice for the results. 294 sliceField := newResults.Field(0) 295 newSlice := reflect.New(sliceField.Type()).Elem() 296 297 // Make a new result of the slice's element type and set it to err. 298 newResult := reflect.New(newSlice.Type().Elem()).Elem() 299 newResult.FieldByName("Error").Set(reflect.ValueOf(err)) 300 301 // Append howMany copies of newResult to the slice. 302 for howMany > 0 { 303 sliceField.Set(reflect.Append(sliceField, newResult)) 304 howMany-- 305 } 306 307 return newResults.Interface() 308 } 309 310 // TODO(dimitern): Move this and MakeResultsWithErrors in params/testing ? 311 func (MachineSuite) TestMakeResultsWithErrors(c *gc.C) { 312 err := apiservertesting.ServerError("foo") 313 r1 := MakeResultsWithErrors(params.LifeResults{}, err, 2) 314 r2 := params.LifeResults{Results: []params.LifeResult{{Error: err}, {Error: err}}} 315 c.Assert(r1, jc.DeepEquals, r2) 316 }