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