github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/core/instance"
    19  	"github.com/juju/juju/core/status"
    20  	"github.com/juju/juju/network"
    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  	results := params.LifeResults{
   146  		Results: []params.LifeResult{{Life: params.Dying}},
   147  	}
   148  	apiCaller := successAPICaller(c, "Life", entitiesArgs, results)
   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(apiCaller.CallCount, gc.Equals, 1)
   153  }
   154  
   155  func (s *MachineSuite) TestStatusSuccess(c *gc.C) {
   156  	now := time.Now()
   157  	expectStatus := params.StatusResult{
   158  		Status: "foo",
   159  		Info:   "bar",
   160  		Data: map[string]interface{}{
   161  			"int":    42,
   162  			"bool":   true,
   163  			"float":  3.14,
   164  			"slice":  []string{"a", "b"},
   165  			"map":    map[int]string{5: "five"},
   166  			"string": "argh",
   167  		},
   168  		Since: &now,
   169  	}
   170  	results := params.StatusResults{Results: []params.StatusResult{expectStatus}}
   171  	apiCaller := successAPICaller(c, "Status", entitiesArgs, results)
   172  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   173  	status, err := machine.Status()
   174  	c.Check(err, jc.ErrorIsNil)
   175  	c.Check(status, jc.DeepEquals, expectStatus)
   176  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   177  }
   178  
   179  func (s *MachineSuite) TestIsManualSuccess(c *gc.C) {
   180  	results := params.BoolResults{
   181  		Results: []params.BoolResult{{Result: true}},
   182  	}
   183  	apiCaller := successAPICaller(c, "AreManuallyProvisioned", entitiesArgs, results)
   184  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   185  	isManual, err := machine.IsManual()
   186  	c.Check(err, jc.ErrorIsNil)
   187  	c.Check(isManual, jc.IsTrue)
   188  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   189  }
   190  
   191  func (s *MachineSuite) TestInstanceIdSuccess(c *gc.C) {
   192  	results := params.StringResults{
   193  		Results: []params.StringResult{{Result: "i-foo"}},
   194  	}
   195  	apiCaller := successAPICaller(c, "InstanceId", entitiesArgs, results)
   196  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   197  	instId, err := machine.InstanceId()
   198  	c.Check(err, jc.ErrorIsNil)
   199  	c.Check(instId, gc.Equals, instance.Id("i-foo"))
   200  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   201  }
   202  
   203  func (s *MachineSuite) TestInstanceStatusSuccess(c *gc.C) {
   204  	results := params.StatusResults{
   205  		Results: []params.StatusResult{{
   206  			Status: status.Provisioning.String(),
   207  		}},
   208  	}
   209  	apiCaller := successAPICaller(c, "InstanceStatus", entitiesArgs, results)
   210  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   211  	statusResult, err := machine.InstanceStatus()
   212  	c.Check(err, jc.ErrorIsNil)
   213  	c.Check(statusResult.Status, gc.DeepEquals, status.Provisioning.String())
   214  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   215  }
   216  
   217  func (s *MachineSuite) TestSetInstanceStatusSuccess(c *gc.C) {
   218  	expectArgs := params.SetStatus{
   219  		Entities: []params.EntityStatusArgs{{
   220  			Tag:    "machine-42",
   221  			Status: "RUNNING",
   222  		}}}
   223  	results := params.ErrorResults{
   224  		Results: []params.ErrorResult{{Error: nil}},
   225  	}
   226  	apiCaller := successAPICaller(c, "SetInstanceStatus", expectArgs, results)
   227  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   228  	err := machine.SetInstanceStatus("RUNNING", "", nil)
   229  	c.Check(err, jc.ErrorIsNil)
   230  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   231  }
   232  
   233  func (s *MachineSuite) TestProviderAddressesSuccess(c *gc.C) {
   234  	addresses := network.NewAddresses("2001:db8::1", "0.1.2.3")
   235  	results := params.MachineAddressesResults{
   236  		Results: []params.MachineAddressesResult{{
   237  			Addresses: params.FromNetworkAddresses(addresses...),
   238  		}}}
   239  	apiCaller := successAPICaller(c, "ProviderAddresses", entitiesArgs, results)
   240  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   241  	addrs, err := machine.ProviderAddresses()
   242  	c.Check(err, jc.ErrorIsNil)
   243  	c.Check(addrs, jc.DeepEquals, addresses)
   244  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   245  }
   246  
   247  func (s *MachineSuite) TestSetProviderAddressesSuccess(c *gc.C) {
   248  	addresses := network.NewAddresses("2001:db8::1", "0.1.2.3")
   249  	expectArgs := params.SetMachinesAddresses{
   250  		MachineAddresses: []params.MachineAddresses{{
   251  			Tag:       "machine-42",
   252  			Addresses: params.FromNetworkAddresses(addresses...),
   253  		}}}
   254  	results := params.ErrorResults{
   255  		Results: []params.ErrorResult{{Error: nil}},
   256  	}
   257  	apiCaller := successAPICaller(c, "SetProviderAddresses", expectArgs, results)
   258  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   259  	err := machine.SetProviderAddresses(addresses...)
   260  	c.Check(err, jc.ErrorIsNil)
   261  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   262  }
   263  
   264  func (s *MachineSuite) CheckClientError(c *gc.C, wf methodWrapper) {
   265  	apiCaller := clientErrorAPICaller(c, "", nil)
   266  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   267  	c.Check(wf(machine), gc.ErrorMatches, "client error!")
   268  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   269  }
   270  
   271  func (s *MachineSuite) CheckServerError(c *gc.C, wf methodWrapper, expectErr string, serverResults interface{}) {
   272  	apiCaller := successAPICaller(c, "", nil, serverResults)
   273  	machine := instancepoller.NewMachine(apiCaller, s.tag, params.Alive)
   274  	c.Check(wf(machine), gc.ErrorMatches, expectErr)
   275  	c.Check(apiCaller.CallCount, gc.Equals, 1)
   276  }
   277  
   278  var entitiesArgs = params.Entities{
   279  	Entities: []params.Entity{{Tag: "machine-42"}},
   280  }
   281  
   282  // MakeResultsWithErrors constructs a new instance of the results type
   283  // (from apiserver/params), matching the given resultsRef, finds its
   284  // first field (expected to be a slice, usually "Results") and adds
   285  // howMany elements to it, setting the Error field of each element to
   286  // err.
   287  //
   288  // This helper makes a few assumptions:
   289  // - resultsRef's type is a struct and has a single field (commonly - "Results")
   290  // - that field is a slice of structs, which have an Error field
   291  // - the Error field is of type *params.Error
   292  //
   293  // Example:
   294  //   err := apiservertesting.ServerError("foo")
   295  //   r := MakeResultsWithErrors(params.LifeResults{}, err, 2)
   296  // is equvalent to:
   297  //   r := params.LifeResults{Results: []params.LifeResult{{Error: err}, {Error: err}}}
   298  //
   299  func MakeResultsWithErrors(resultsRef interface{}, err *params.Error, howMany int) interface{} {
   300  	// Make a new instance of the same type as resultsRef.
   301  	resultsType := reflect.TypeOf(resultsRef)
   302  	newResults := reflect.New(resultsType).Elem()
   303  
   304  	// Make a new empty slice for the results.
   305  	sliceField := newResults.Field(0)
   306  	newSlice := reflect.New(sliceField.Type()).Elem()
   307  
   308  	// Make a new result of the slice's element type and set it to err.
   309  	newResult := reflect.New(newSlice.Type().Elem()).Elem()
   310  	newResult.FieldByName("Error").Set(reflect.ValueOf(err))
   311  
   312  	// Append howMany copies of newResult to the slice.
   313  	for howMany > 0 {
   314  		sliceField.Set(reflect.Append(sliceField, newResult))
   315  		howMany--
   316  	}
   317  
   318  	return newResults.Interface()
   319  }
   320  
   321  // TODO(dimitern): Move this and MakeResultsWithErrors in params/testing ?
   322  func (MachineSuite) TestMakeResultsWithErrors(c *gc.C) {
   323  	err := apiservertesting.ServerError("foo")
   324  	r1 := MakeResultsWithErrors(params.LifeResults{}, err, 2)
   325  	r2 := params.LifeResults{Results: []params.LifeResult{{Error: err}, {Error: err}}}
   326  	c.Assert(r1, jc.DeepEquals, r2)
   327  }