github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/maas/instance_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package maas
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	"github.com/juju/gomaasapi"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/core/instance"
    16  	"github.com/juju/juju/environs/context"
    17  	"github.com/juju/juju/network"
    18  )
    19  
    20  type instanceTest struct {
    21  	providerSuite
    22  }
    23  
    24  var _ = gc.Suite(&instanceTest{})
    25  
    26  func defaultSubnet() gomaasapi.CreateSubnet {
    27  	var s gomaasapi.CreateSubnet
    28  	s.DNSServers = []string{"192.168.1.2"}
    29  	s.Name = "maas-eth0"
    30  	s.Space = "space-0"
    31  	s.GatewayIP = "192.168.1.1"
    32  	s.CIDR = "192.168.1.0/24"
    33  	s.ID = 1
    34  	return s
    35  }
    36  
    37  func (s *instanceTest) newSubnet(cidr, space string, id uint) *bytes.Buffer {
    38  	var sub gomaasapi.CreateSubnet
    39  	sub.DNSServers = []string{"192.168.1.2"}
    40  	sub.Name = cidr
    41  	sub.Space = space
    42  	sub.GatewayIP = "192.168.1.1"
    43  	sub.CIDR = cidr
    44  	sub.ID = id
    45  	return s.subnetJSON(sub)
    46  }
    47  
    48  func (s *instanceTest) subnetJSON(subnet gomaasapi.CreateSubnet) *bytes.Buffer {
    49  	var out bytes.Buffer
    50  	err := json.NewEncoder(&out).Encode(subnet)
    51  	if err != nil {
    52  		panic(err)
    53  	}
    54  	return &out
    55  }
    56  
    57  func (s *instanceTest) SetUpTest(c *gc.C) {
    58  	s.providerSuite.SetUpTest(c)
    59  
    60  	// Create a subnet so that the spaces cache will be populated.
    61  	s.testMAASObject.TestServer.NewSubnet(s.subnetJSON(defaultSubnet()))
    62  }
    63  
    64  func (s *instanceTest) TestId(c *gc.C) {
    65  	jsonValue := `{"system_id": "system_id", "test": "test"}`
    66  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
    67  	resourceURI, _ := obj.GetField("resource_uri")
    68  	// TODO(perrito666) make a decent mock status getter
    69  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
    70  		return "unknown", "FAKE"
    71  	}
    72  	instance := maas1Instance{&obj, nil, statusGetter}
    73  
    74  	c.Check(string(instance.Id()), gc.Equals, resourceURI)
    75  }
    76  
    77  func (s *instanceTest) TestString(c *gc.C) {
    78  	jsonValue := `{"hostname": "thethingintheplace", "system_id": "system_id", "test": "test"}`
    79  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
    80  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
    81  		return "unknown", "FAKE"
    82  	}
    83  
    84  	instance := &maas1Instance{&obj, nil, statusGetter}
    85  	hostname, err := instance.hostname()
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	expected := hostname + ":" + string(instance.Id())
    88  	c.Assert(fmt.Sprint(instance), gc.Equals, expected)
    89  }
    90  
    91  func (s *instanceTest) TestStringWithoutHostname(c *gc.C) {
    92  	// For good measure, test what happens if we don't have a hostname.
    93  	jsonValue := `{"system_id": "system_id", "test": "test"}`
    94  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
    95  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
    96  		return "unknown", "FAKE"
    97  	}
    98  
    99  	instance := &maas1Instance{&obj, nil, statusGetter}
   100  	_, err := instance.hostname()
   101  	c.Assert(err, gc.NotNil)
   102  	expected := fmt.Sprintf("<DNSName failed: %q>", err) + ":" + string(instance.Id())
   103  	c.Assert(fmt.Sprint(instance), gc.Equals, expected)
   104  }
   105  
   106  func (s *instanceTest) TestAddressesViaInterfaces(c *gc.C) {
   107  	server := s.testMAASObject.TestServer
   108  	server.SetVersionJSON(`{"capabilities": ["network-deployment-ubuntu"]}`)
   109  	// We simulate an newer MAAS (1.9+) which returns both ip_addresses and
   110  	// interface_set for a node. To verify we use interfaces we deliberately put
   111  	// different items in ip_addresses
   112  	jsonValue := `{
   113      "hostname": "-testing.invalid",
   114      "system_id": "system_id",
   115      "interface_set" : [
   116  	{ "name": "eth0", "links": [
   117  	    { "subnet": { "space": "bar", "cidr": "8.7.6.0/24" }, "ip_address": "8.7.6.5" },
   118  	    { "subnet": { "space": "bar", "cidr": "8.7.6.0/24"  }, "ip_address": "8.7.6.6" }
   119  	] },
   120  	{ "name": "eth1", "links": [
   121  	    { "subnet": { "space": "storage", "cidr": "10.0.1.1/24" }, "ip_address": "10.0.1.1" }
   122  	] },
   123  	{ "name": "eth3", "links": [
   124  	    { "subnet": { "space": "db", "cidr": "fc00::/64" }, "ip_address": "fc00::123" }
   125  	] },
   126  	{ "name": "eth4" },
   127  	{ "name": "eth5", "links": [
   128  	    { "mode": "link-up" }
   129  	] }
   130      ],
   131      "ip_addresses": [ "anything", "foo", "0.1.2.3" ]
   132  }`
   133  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
   134  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
   135  		return "unknown", "FAKE"
   136  	}
   137  
   138  	barSpace := server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "bar"}))
   139  	storageSpace := server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "storage"}))
   140  	dbSpace := server.NewSpace(spaceJSON(gomaasapi.CreateSpace{Name: "db"}))
   141  	server.NewSubnet(s.newSubnet("8.7.6.0/24", "bar", 2))
   142  	server.NewSubnet(s.newSubnet("10.0.1.1/24", "storage", 3))
   143  	server.NewSubnet(s.newSubnet("fc00::/64", "db", 4))
   144  	inst := maas1Instance{&obj, s.makeEnviron(), statusGetter}
   145  
   146  	// Since gomaasapi treats "interface_set" specially and the only way to
   147  	// change it is via SetNodeNetworkLink(), which in turn does not allow you
   148  	// to specify ip_address, we need to patch the call which gets a fresh copy
   149  	// of the node details from the test server to avoid mangling the
   150  	// interface_set we used above.
   151  	s.PatchValue(&refreshMAASObject, func(mo *gomaasapi.MAASObject) (gomaasapi.MAASObject, error) {
   152  		return *mo, nil
   153  	})
   154  
   155  	idFromUint := func(u uint) network.Id {
   156  		return network.Id(fmt.Sprintf("%d", u))
   157  	}
   158  	expected := []network.Address{
   159  		newAddressOnSpaceWithId("bar", idFromUint(barSpace.ID), "8.7.6.5"),
   160  		newAddressOnSpaceWithId("bar", idFromUint(barSpace.ID), "8.7.6.6"),
   161  		newAddressOnSpaceWithId("storage", idFromUint(storageSpace.ID), "10.0.1.1"),
   162  		newAddressOnSpaceWithId("db", idFromUint(dbSpace.ID), "fc00::123"),
   163  	}
   164  
   165  	addr, err := inst.Addresses(s.callCtx)
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	c.Check(addr, jc.DeepEquals, expected)
   168  }
   169  
   170  func (s *instanceTest) TestAddressesInvalid(c *gc.C) {
   171  	jsonValue := `{
   172  		"hostname": "testing.invalid",
   173  		"system_id": "system_id",
   174  		"ip_addresses": "incompatible"
   175  		}`
   176  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
   177  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
   178  		return "unknown", "FAKE"
   179  	}
   180  
   181  	inst := maas1Instance{&obj, s.makeEnviron(), statusGetter}
   182  
   183  	_, err := inst.Addresses(s.callCtx)
   184  	c.Assert(err, gc.NotNil)
   185  }
   186  
   187  func (s *instanceTest) TestAddressesInvalidContents(c *gc.C) {
   188  	jsonValue := `{
   189  		"hostname": "testing.invalid",
   190  		"system_id": "system_id",
   191  		"ip_addresses": [42]
   192  		}`
   193  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
   194  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
   195  		return "unknown", "FAKE"
   196  	}
   197  
   198  	inst := maas1Instance{&obj, s.makeEnviron(), statusGetter}
   199  
   200  	_, err := inst.Addresses(s.callCtx)
   201  	c.Assert(err, gc.NotNil)
   202  }
   203  
   204  func (s *instanceTest) TestHardwareCharacteristics(c *gc.C) {
   205  	jsonValue := `{
   206  		"system_id": "system_id",
   207          "architecture": "amd64/generic",
   208          "cpu_count": 6,
   209          "zone": {"name": "tst"},
   210          "memory": 16384
   211  	}`
   212  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
   213  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
   214  		return "unknown", "FAKE"
   215  	}
   216  
   217  	inst := maas1Instance{&obj, nil, statusGetter}
   218  	hc, err := inst.hardwareCharacteristics()
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	c.Assert(hc, gc.NotNil)
   221  	c.Assert(hc.String(), gc.Equals, `arch=amd64 cores=6 mem=16384M availability-zone=tst`)
   222  }
   223  
   224  func (s *instanceTest) TestHardwareCharacteristicsWithTags(c *gc.C) {
   225  	jsonValue := `{
   226  		"system_id": "system_id",
   227          "architecture": "amd64/generic",
   228          "cpu_count": 6,
   229          "memory": 16384,
   230          "zone": {"name": "tst"},
   231          "tag_names": ["a", "b"]
   232  	}`
   233  	obj := s.testMAASObject.TestServer.NewNode(jsonValue)
   234  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
   235  		return "unknown", "FAKE"
   236  	}
   237  
   238  	inst := maas1Instance{&obj, nil, statusGetter}
   239  	hc, err := inst.hardwareCharacteristics()
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(hc, gc.NotNil)
   242  	c.Assert(hc.String(), gc.Equals, `arch=amd64 cores=6 mem=16384M tags=a,b availability-zone=tst`)
   243  }
   244  
   245  func (s *instanceTest) TestHardwareCharacteristicsMissing(c *gc.C) {
   246  	s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "cpu_count": 6, "memory": 16384}`,
   247  		`error determining architecture: Requested string, got <nil>.`)
   248  	s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "amd64", "memory": 16384}`,
   249  		`error determining cpu count: Requested float64, got <nil>.`)
   250  	s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6}`,
   251  		`error determining available memory: Requested float64, got <nil>.`)
   252  	s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1}`,
   253  		`error determining availability zone: zone property not set on maas`)
   254  	s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1, "zone": ""}`,
   255  		`error determining availability zone: zone property is not an expected type`)
   256  	s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1, "zone": {}}`,
   257  		`error determining availability zone: zone property is not set correctly: name is missing`)
   258  	s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1, "zone": {"name": "tst"}, "tag_names": "wot"}`,
   259  		`error determining tag names: Requested array, got string.`)
   260  }
   261  
   262  func (s *instanceTest) testHardwareCharacteristicsMissing(c *gc.C, json, expect string) {
   263  	obj := s.testMAASObject.TestServer.NewNode(json)
   264  	statusGetter := func(context.ProviderCallContext, instance.Id) (string, string) {
   265  		return "unknown", "FAKE"
   266  	}
   267  
   268  	inst := maas1Instance{&obj, nil, statusGetter}
   269  	_, err := inst.hardwareCharacteristics()
   270  	c.Assert(err, gc.ErrorMatches, expect)
   271  }