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  }