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 }