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