github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/maas/maas2_environ_whitebox_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package maas
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/gomaasapi"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils/arch"
    15  	"github.com/juju/utils/set"
    16  	"github.com/juju/version"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/names.v2"
    19  	goyaml "gopkg.in/yaml.v2"
    20  
    21  	"github.com/juju/juju/cloud"
    22  	"github.com/juju/juju/cloudconfig/cloudinit"
    23  	"github.com/juju/juju/constraints"
    24  	"github.com/juju/juju/environs"
    25  	"github.com/juju/juju/environs/bootstrap"
    26  	"github.com/juju/juju/environs/config"
    27  	"github.com/juju/juju/environs/tags"
    28  	envjujutesting "github.com/juju/juju/environs/testing"
    29  	envtools "github.com/juju/juju/environs/tools"
    30  	"github.com/juju/juju/instance"
    31  	jujutesting "github.com/juju/juju/juju/testing"
    32  	"github.com/juju/juju/network"
    33  	coretesting "github.com/juju/juju/testing"
    34  )
    35  
    36  type maas2EnvironSuite struct {
    37  	maas2Suite
    38  }
    39  
    40  var _ = gc.Suite(&maas2EnvironSuite{})
    41  
    42  func (suite *maas2EnvironSuite) getEnvWithServer(c *gc.C) (*maasEnviron, error) {
    43  	testServer := gomaasapi.NewSimpleServer()
    44  	testServer.AddGetResponse("/api/2.0/version/", http.StatusOK, maas2VersionResponse)
    45  	testServer.AddGetResponse("/api/2.0/users/?op=whoami", http.StatusOK, "{}")
    46  	// Weirdly, rather than returning a 404 when the version is
    47  	// unknown, MAAS2 returns some HTML (the login page).
    48  	testServer.AddGetResponse("/api/1.0/version/", http.StatusOK, "<html></html>")
    49  	testServer.Start()
    50  	suite.AddCleanup(func(*gc.C) { testServer.Close() })
    51  	cred := cloud.NewCredential(cloud.OAuth1AuthType, map[string]string{
    52  		"maas-oauth": "a:b:c",
    53  	})
    54  	cloud := environs.CloudSpec{
    55  		Type:       "maas",
    56  		Name:       "maas",
    57  		Endpoint:   testServer.Server.URL,
    58  		Credential: &cred,
    59  	}
    60  	attrs := coretesting.FakeConfig().Merge(maasEnvAttrs)
    61  	cfg, err := config.New(config.NoDefaults, attrs)
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	return NewEnviron(cloud, cfg)
    64  }
    65  
    66  func (suite *maas2EnvironSuite) TestNewEnvironWithController(c *gc.C) {
    67  	env, err := suite.getEnvWithServer(c)
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	c.Assert(env, gc.NotNil)
    70  }
    71  
    72  func (suite *maas2EnvironSuite) injectControllerWithSpacesAndCheck(c *gc.C, spaces []gomaasapi.Space, expected gomaasapi.AllocateMachineArgs) (*maasEnviron, *fakeController) {
    73  	var env *maasEnviron
    74  	check := func(args gomaasapi.AllocateMachineArgs) {
    75  		expected.AgentName = env.Config().UUID()
    76  		c.Assert(args, gc.DeepEquals, expected)
    77  	}
    78  	controller := &fakeController{
    79  		allocateMachineArgsCheck: check,
    80  		allocateMachine:          newFakeMachine("Bruce Sterling", arch.HostArch(), ""),
    81  		allocateMachineMatches: gomaasapi.ConstraintMatches{
    82  			Storage: map[string][]gomaasapi.BlockDevice{},
    83  		},
    84  		spaces: spaces,
    85  	}
    86  	suite.injectController(controller)
    87  	suite.setupFakeTools(c)
    88  	env = suite.makeEnviron(c, nil)
    89  	return env, controller
    90  }
    91  
    92  func (suite *maas2EnvironSuite) makeEnvironWithMachines(c *gc.C, expectedSystemIDs []string, returnSystemIDs []string) *maasEnviron {
    93  	var env *maasEnviron
    94  	checkArgs := func(args gomaasapi.MachinesArgs) {
    95  		c.Check(args.SystemIDs, gc.DeepEquals, expectedSystemIDs)
    96  		c.Check(args.AgentName, gc.Equals, env.Config().UUID())
    97  	}
    98  	machines := make([]gomaasapi.Machine, len(returnSystemIDs))
    99  	for index, id := range returnSystemIDs {
   100  		machines[index] = &fakeMachine{systemID: id}
   101  	}
   102  	controller := &fakeController{
   103  		machines:          machines,
   104  		machinesArgsCheck: checkArgs,
   105  	}
   106  	env = suite.makeEnviron(c, controller)
   107  	return env
   108  }
   109  
   110  func (suite *maas2EnvironSuite) TestAllInstances(c *gc.C) {
   111  	env := suite.makeEnvironWithMachines(
   112  		c, []string{}, []string{"tuco", "tio", "gus"},
   113  	)
   114  	result, err := env.AllInstances()
   115  	c.Assert(err, jc.ErrorIsNil)
   116  	expectedMachines := set.NewStrings("tuco", "tio", "gus")
   117  	actualMachines := set.NewStrings()
   118  	for _, instance := range result {
   119  		actualMachines.Add(string(instance.Id()))
   120  	}
   121  	c.Assert(actualMachines, gc.DeepEquals, expectedMachines)
   122  }
   123  
   124  func (suite *maas2EnvironSuite) TestAllInstancesError(c *gc.C) {
   125  	controller := &fakeController{machinesError: errors.New("Something terrible!")}
   126  	env := suite.makeEnviron(c, controller)
   127  	_, err := env.AllInstances()
   128  	c.Assert(err, gc.ErrorMatches, "Something terrible!")
   129  }
   130  
   131  func (suite *maas2EnvironSuite) TestInstances(c *gc.C) {
   132  	env := suite.makeEnvironWithMachines(
   133  		c, []string{"jake", "bonnibel"}, []string{"jake", "bonnibel"},
   134  	)
   135  	result, err := env.Instances([]instance.Id{"jake", "bonnibel"})
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	expectedMachines := set.NewStrings("jake", "bonnibel")
   138  	actualMachines := set.NewStrings()
   139  	for _, machine := range result {
   140  		actualMachines.Add(string(machine.Id()))
   141  	}
   142  	c.Assert(actualMachines, gc.DeepEquals, expectedMachines)
   143  }
   144  
   145  func (suite *maas2EnvironSuite) TestInstancesPartialResult(c *gc.C) {
   146  	env := suite.makeEnvironWithMachines(
   147  		c, []string{"jake", "bonnibel"}, []string{"tuco", "bonnibel"},
   148  	)
   149  	result, err := env.Instances([]instance.Id{"jake", "bonnibel"})
   150  	c.Check(err, gc.Equals, environs.ErrPartialInstances)
   151  	c.Assert(result, gc.HasLen, 2)
   152  	c.Assert(result[0], gc.IsNil)
   153  	c.Assert(result[1].Id(), gc.Equals, instance.Id("bonnibel"))
   154  }
   155  
   156  func (suite *maas2EnvironSuite) TestAvailabilityZones(c *gc.C) {
   157  	controller := &fakeController{
   158  		zones: []gomaasapi.Zone{
   159  			&fakeZone{name: "mossack"},
   160  			&fakeZone{name: "fonseca"},
   161  		},
   162  	}
   163  	env := suite.makeEnviron(c, controller)
   164  	result, err := env.AvailabilityZones()
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	expectedZones := set.NewStrings("mossack", "fonseca")
   167  	actualZones := set.NewStrings()
   168  	for _, zone := range result {
   169  		actualZones.Add(zone.Name())
   170  	}
   171  	c.Assert(actualZones, gc.DeepEquals, expectedZones)
   172  }
   173  
   174  func (suite *maas2EnvironSuite) TestAvailabilityZonesError(c *gc.C) {
   175  	controller := &fakeController{
   176  		zonesError: errors.New("a bad thing"),
   177  	}
   178  	env := suite.makeEnviron(c, controller)
   179  	_, err := env.AvailabilityZones()
   180  	c.Assert(err, gc.ErrorMatches, "a bad thing")
   181  }
   182  
   183  func (suite *maas2EnvironSuite) TestSpaces(c *gc.C) {
   184  	controller := &fakeController{
   185  		spaces: []gomaasapi.Space{
   186  			fakeSpace{
   187  				name: "pepper",
   188  				id:   1234,
   189  			},
   190  			fakeSpace{
   191  				name: "freckles",
   192  				id:   4567,
   193  				subnets: []gomaasapi.Subnet{
   194  					fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"},
   195  					fakeSubnet{id: 98, vlan: fakeVLAN{vid: 67}, cidr: "192.168.11.0/24"},
   196  				},
   197  			},
   198  		},
   199  	}
   200  	env := suite.makeEnviron(c, controller)
   201  	result, err := env.Spaces()
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	c.Assert(result, gc.HasLen, 1)
   204  	c.Assert(result[0].Name, gc.Equals, "freckles")
   205  	c.Assert(result[0].ProviderId, gc.Equals, network.Id("4567"))
   206  	subnets := result[0].Subnets
   207  	c.Assert(subnets, gc.HasLen, 2)
   208  	c.Assert(subnets[0].ProviderId, gc.Equals, network.Id("99"))
   209  	c.Assert(subnets[0].VLANTag, gc.Equals, 66)
   210  	c.Assert(subnets[0].CIDR, gc.Equals, "192.168.10.0/24")
   211  	c.Assert(subnets[0].SpaceProviderId, gc.Equals, network.Id("4567"))
   212  	c.Assert(subnets[1].ProviderId, gc.Equals, network.Id("98"))
   213  	c.Assert(subnets[1].VLANTag, gc.Equals, 67)
   214  	c.Assert(subnets[1].CIDR, gc.Equals, "192.168.11.0/24")
   215  	c.Assert(subnets[1].SpaceProviderId, gc.Equals, network.Id("4567"))
   216  }
   217  
   218  func (suite *maas2EnvironSuite) TestSpacesError(c *gc.C) {
   219  	controller := &fakeController{
   220  		spacesError: errors.New("Joe Manginiello"),
   221  	}
   222  	env := suite.makeEnviron(c, controller)
   223  	_, err := env.Spaces()
   224  	c.Assert(err, gc.ErrorMatches, "Joe Manginiello")
   225  }
   226  
   227  func collectReleaseArgs(controller *fakeController) []gomaasapi.ReleaseMachinesArgs {
   228  	args := []gomaasapi.ReleaseMachinesArgs{}
   229  	for _, call := range controller.Stub.Calls() {
   230  		if call.FuncName == "ReleaseMachines" {
   231  			args = append(args, call.Args[0].(gomaasapi.ReleaseMachinesArgs))
   232  		}
   233  	}
   234  	return args
   235  }
   236  
   237  func (suite *maas2EnvironSuite) TestStopInstancesReturnsIfParameterEmpty(c *gc.C) {
   238  	controller := newFakeController()
   239  	err := suite.makeEnviron(c, controller).StopInstances()
   240  	c.Check(err, jc.ErrorIsNil)
   241  	c.Assert(collectReleaseArgs(controller), gc.HasLen, 0)
   242  }
   243  
   244  func (suite *maas2EnvironSuite) TestStopInstancesStopsAndReleasesInstances(c *gc.C) {
   245  	// Return a cannot complete indicating that test1 is in the wrong state.
   246  	// The release operation will still release the others and succeed.
   247  	controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"})
   248  	err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3")
   249  	c.Check(err, jc.ErrorIsNil)
   250  	args := collectReleaseArgs(controller)
   251  	c.Assert(args, gc.HasLen, 1)
   252  	c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"})
   253  }
   254  
   255  func (suite *maas2EnvironSuite) TestStopInstancesIgnoresConflict(c *gc.C) {
   256  	// Return a cannot complete indicating that test1 is in the wrong state.
   257  	// The release operation will still release the others and succeed.
   258  	controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"})
   259  	controller.SetErrors(gomaasapi.NewCannotCompleteError("test1 not allocated"))
   260  	err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3")
   261  	c.Check(err, jc.ErrorIsNil)
   262  
   263  	args := collectReleaseArgs(controller)
   264  	c.Assert(args, gc.HasLen, 1)
   265  	c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"})
   266  }
   267  
   268  func (suite *maas2EnvironSuite) TestStopInstancesIgnoresMissingNodeAndRecurses(c *gc.C) {
   269  	controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"})
   270  	controller.SetErrors(
   271  		gomaasapi.NewBadRequestError("no such machine: test1"),
   272  		gomaasapi.NewBadRequestError("no such machine: test1"),
   273  	)
   274  	err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3")
   275  	c.Check(err, jc.ErrorIsNil)
   276  	args := collectReleaseArgs(controller)
   277  	c.Assert(args, gc.HasLen, 4)
   278  	c.Assert(args[0].SystemIDs, gc.DeepEquals, []string{"test1", "test2", "test3"})
   279  	c.Assert(args[1].SystemIDs, gc.DeepEquals, []string{"test1"})
   280  	c.Assert(args[2].SystemIDs, gc.DeepEquals, []string{"test2"})
   281  	c.Assert(args[3].SystemIDs, gc.DeepEquals, []string{"test3"})
   282  }
   283  
   284  func (suite *maas2EnvironSuite) checkStopInstancesFails(c *gc.C, withError error) {
   285  	controller := newFakeControllerWithFiles(&fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"})
   286  	controller.SetErrors(withError)
   287  	err := suite.makeEnviron(c, controller).StopInstances("test1", "test2", "test3")
   288  	c.Check(err, gc.ErrorMatches, fmt.Sprintf("cannot release nodes: %s", withError))
   289  	// Only tries once.
   290  	c.Assert(collectReleaseArgs(controller), gc.HasLen, 1)
   291  }
   292  
   293  func (suite *maas2EnvironSuite) TestStopInstancesReturnsUnexpectedMAASError(c *gc.C) {
   294  	suite.checkStopInstancesFails(c, gomaasapi.NewNoMatchError("Something else bad!"))
   295  }
   296  
   297  func (suite *maas2EnvironSuite) TestStopInstancesReturnsUnexpectedError(c *gc.C) {
   298  	suite.checkStopInstancesFails(c, errors.New("Something completely unexpected!"))
   299  }
   300  
   301  func (suite *maas2EnvironSuite) TestStartInstanceError(c *gc.C) {
   302  	suite.injectController(&fakeController{
   303  		allocateMachineError: errors.New("Charles Babbage"),
   304  	})
   305  	env := suite.makeEnviron(c, nil)
   306  	_, err := env.StartInstance(environs.StartInstanceParams{})
   307  	c.Assert(err, gc.ErrorMatches, ".* cannot run instance: Charles Babbage")
   308  }
   309  
   310  func (suite *maas2EnvironSuite) TestStartInstance(c *gc.C) {
   311  	env, _ := suite.injectControllerWithSpacesAndCheck(c, nil, gomaasapi.AllocateMachineArgs{})
   312  
   313  	params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID}
   314  	result, err := jujutesting.StartInstanceWithParams(env, "1", params)
   315  	c.Assert(err, jc.ErrorIsNil)
   316  	c.Assert(result.Instance.Id(), gc.Equals, instance.Id("Bruce Sterling"))
   317  }
   318  
   319  func (suite *maas2EnvironSuite) TestStartInstanceAppliesResourceTags(c *gc.C) {
   320  	env, controller := suite.injectControllerWithSpacesAndCheck(c, nil, gomaasapi.AllocateMachineArgs{})
   321  	config := env.Config()
   322  	_, ok := config.ResourceTags()
   323  	c.Assert(ok, jc.IsTrue)
   324  	params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID}
   325  	_, err := jujutesting.StartInstanceWithParams(env, "1", params)
   326  	c.Assert(err, jc.ErrorIsNil)
   327  
   328  	machine := controller.allocateMachine.(*fakeMachine)
   329  	machine.CheckCallNames(c, "Start", "SetOwnerData")
   330  	c.Assert(machine.Calls()[1].Args[0], gc.DeepEquals, map[string]string{
   331  		"claude":            "rains",
   332  		tags.JujuController: suite.controllerUUID,
   333  		tags.JujuModel:      config.UUID(),
   334  	})
   335  }
   336  
   337  func (suite *maas2EnvironSuite) TestStartInstanceParams(c *gc.C) {
   338  	var env *maasEnviron
   339  	suite.injectController(&fakeController{
   340  		allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) {
   341  			c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{
   342  				AgentName: env.Config().UUID(),
   343  				Zone:      "foo",
   344  				MinMemory: 8192,
   345  			})
   346  		},
   347  		allocateMachine: newFakeMachine("Bruce Sterling", arch.HostArch(), ""),
   348  		allocateMachineMatches: gomaasapi.ConstraintMatches{
   349  			Storage: map[string][]gomaasapi.BlockDevice{},
   350  		},
   351  		zones: []gomaasapi.Zone{&fakeZone{name: "foo"}},
   352  	})
   353  	suite.setupFakeTools(c)
   354  	env = suite.makeEnviron(c, nil)
   355  	params := environs.StartInstanceParams{
   356  		ControllerUUID: suite.controllerUUID,
   357  		Placement:      "zone=foo",
   358  		Constraints:    constraints.MustParse("mem=8G"),
   359  	}
   360  	result, err := jujutesting.StartInstanceWithParams(env, "1", params)
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	c.Assert(result.Instance.Id(), gc.Equals, instance.Id("Bruce Sterling"))
   363  }
   364  
   365  func (suite *maas2EnvironSuite) TestAcquireNodePassedAgentName(c *gc.C) {
   366  	var env *maasEnviron
   367  	suite.injectController(&fakeController{
   368  		allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) {
   369  			c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{
   370  				AgentName: env.Config().UUID()})
   371  		},
   372  		allocateMachine: &fakeMachine{
   373  			systemID:     "Bruce Sterling",
   374  			architecture: arch.HostArch(),
   375  		},
   376  	})
   377  	suite.setupFakeTools(c)
   378  	env = suite.makeEnviron(c, nil)
   379  
   380  	_, err := env.acquireNode2("", "", constraints.Value{}, nil, nil)
   381  
   382  	c.Check(err, jc.ErrorIsNil)
   383  }
   384  
   385  func (suite *maas2EnvironSuite) TestAcquireNodePassesPositiveAndNegativeTags(c *gc.C) {
   386  	var env *maasEnviron
   387  	expected := gomaasapi.AllocateMachineArgs{
   388  		Tags:    []string{"tag1", "tag3"},
   389  		NotTags: []string{"tag2", "tag4"},
   390  	}
   391  	env, _ = suite.injectControllerWithSpacesAndCheck(c, nil, expected)
   392  	_, err := env.acquireNode2(
   393  		"", "",
   394  		constraints.Value{Tags: stringslicep("tag1", "^tag2", "tag3", "^tag4")},
   395  		nil, nil,
   396  	)
   397  	c.Check(err, jc.ErrorIsNil)
   398  }
   399  
   400  func getFourSpaces() []gomaasapi.Space {
   401  	return []gomaasapi.Space{
   402  		fakeSpace{
   403  			name:    "space-1",
   404  			subnets: []gomaasapi.Subnet{fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}},
   405  			id:      5,
   406  		},
   407  		fakeSpace{
   408  			name:    "space-2",
   409  			subnets: []gomaasapi.Subnet{fakeSubnet{id: 100, vlan: fakeVLAN{vid: 66}, cidr: "192.168.11.0/24"}},
   410  			id:      6,
   411  		},
   412  		fakeSpace{
   413  			name:    "space-3",
   414  			subnets: []gomaasapi.Subnet{fakeSubnet{id: 101, vlan: fakeVLAN{vid: 66}, cidr: "192.168.12.0/24"}},
   415  			id:      7,
   416  		},
   417  		fakeSpace{
   418  			name:    "space-4",
   419  			subnets: []gomaasapi.Subnet{fakeSubnet{id: 102, vlan: fakeVLAN{vid: 66}, cidr: "192.168.13.0/24"}},
   420  			id:      8,
   421  		},
   422  	}
   423  }
   424  
   425  func (suite *maas2EnvironSuite) TestAcquireNodePassesPositiveAndNegativeSpaces(c *gc.C) {
   426  	expected := gomaasapi.AllocateMachineArgs{
   427  		NotSpace: []string{"6", "8"},
   428  		Interfaces: []gomaasapi.InterfaceSpec{
   429  			{Label: "0", Space: "5"},
   430  			{Label: "1", Space: "7"},
   431  		},
   432  	}
   433  	env, _ := suite.injectControllerWithSpacesAndCheck(c, getFourSpaces(), expected)
   434  
   435  	_, err := env.acquireNode2(
   436  		"", "",
   437  		constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")},
   438  		nil, nil,
   439  	)
   440  	c.Check(err, jc.ErrorIsNil)
   441  }
   442  
   443  func (suite *maas2EnvironSuite) TestAcquireNodeDisambiguatesNamedLabelsFromIndexedUpToALimit(c *gc.C) {
   444  	env, _ := suite.injectControllerWithSpacesAndCheck(c, getFourSpaces(), gomaasapi.AllocateMachineArgs{})
   445  	var shortLimit uint = 0
   446  	suite.PatchValue(&numericLabelLimit, shortLimit)
   447  
   448  	_, err := env.acquireNode2(
   449  		"", "",
   450  		constraints.Value{Spaces: stringslicep("space-1", "^space-2", "space-3", "^space-4")},
   451  		[]interfaceBinding{{"0", "first-clash"}, {"1", "final-clash"}},
   452  		nil,
   453  	)
   454  	c.Assert(err, gc.ErrorMatches, `too many conflicting numeric labels, giving up.`)
   455  }
   456  
   457  func (suite *maas2EnvironSuite) TestAcquireNodeStorage(c *gc.C) {
   458  	var env *maasEnviron
   459  	var getStorage func() []gomaasapi.StorageSpec
   460  	suite.injectController(&fakeController{
   461  		allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) {
   462  			c.Assert(args, jc.DeepEquals, gomaasapi.AllocateMachineArgs{
   463  				AgentName: env.Config().UUID(),
   464  				Storage:   getStorage(),
   465  			})
   466  		},
   467  		allocateMachine: &fakeMachine{
   468  			systemID:     "Bruce Sterling",
   469  			architecture: arch.HostArch(),
   470  		},
   471  	})
   472  	suite.setupFakeTools(c)
   473  	for i, test := range []struct {
   474  		volumes  []volumeInfo
   475  		expected []gomaasapi.StorageSpec
   476  	}{{
   477  		volumes:  nil,
   478  		expected: []gomaasapi.StorageSpec{},
   479  	}, {
   480  		volumes:  []volumeInfo{{"volume-1", 1234, nil}},
   481  		expected: []gomaasapi.StorageSpec{{"volume-1", 1234, nil}},
   482  	}, {
   483  		volumes:  []volumeInfo{{"", 1234, []string{"tag1", "tag2"}}},
   484  		expected: []gomaasapi.StorageSpec{{"", 1234, []string{"tag1", "tag2"}}},
   485  	}, {
   486  		volumes:  []volumeInfo{{"volume-1", 1234, []string{"tag1", "tag2"}}},
   487  		expected: []gomaasapi.StorageSpec{{"volume-1", 1234, []string{"tag1", "tag2"}}},
   488  	}, {
   489  		volumes: []volumeInfo{
   490  			{"volume-1", 1234, []string{"tag1", "tag2"}},
   491  			{"volume-2", 4567, []string{"tag1", "tag3"}},
   492  		},
   493  		expected: []gomaasapi.StorageSpec{
   494  			{"volume-1", 1234, []string{"tag1", "tag2"}},
   495  			{"volume-2", 4567, []string{"tag1", "tag3"}},
   496  		},
   497  	}} {
   498  		c.Logf("test #%d: volumes=%v", i, test.volumes)
   499  		getStorage = func() []gomaasapi.StorageSpec {
   500  			return test.expected
   501  		}
   502  		env = suite.makeEnviron(c, nil)
   503  		_, err := env.acquireNode2("", "", constraints.Value{}, nil, test.volumes)
   504  		c.Check(err, jc.ErrorIsNil)
   505  	}
   506  }
   507  
   508  func (suite *maas2EnvironSuite) TestAcquireNodeInterfaces(c *gc.C) {
   509  	var env *maasEnviron
   510  	var getNegatives func() []string
   511  	var getPositives func() []gomaasapi.InterfaceSpec
   512  	suite.injectController(&fakeController{
   513  		allocateMachineArgsCheck: func(args gomaasapi.AllocateMachineArgs) {
   514  			c.Assert(args, gc.DeepEquals, gomaasapi.AllocateMachineArgs{
   515  				AgentName:  env.Config().UUID(),
   516  				Interfaces: getPositives(),
   517  				NotSpace:   getNegatives(),
   518  			})
   519  		},
   520  		allocateMachine: &fakeMachine{
   521  			systemID:     "Bruce Sterling",
   522  			architecture: arch.HostArch(),
   523  		},
   524  		spaces: getTwoSpaces(),
   525  	})
   526  	suite.setupFakeTools(c)
   527  	// Add some constraints, including spaces to verify specified bindings
   528  	// always override any spaces constraints.
   529  	cons := constraints.Value{
   530  		Spaces: stringslicep("foo", "^bar"),
   531  	}
   532  	// In the tests below "space:5" means foo, "space:6" means bar.
   533  	for i, test := range []struct {
   534  		interfaces        []interfaceBinding
   535  		expectedPositives []gomaasapi.InterfaceSpec
   536  		expectedNegatives []string
   537  		expectedError     string
   538  	}{{ // without specified bindings, spaces constraints are used instead.
   539  		interfaces:        nil,
   540  		expectedPositives: []gomaasapi.InterfaceSpec{{"0", "2"}},
   541  		expectedNegatives: []string{"3"},
   542  		expectedError:     "",
   543  	}, {
   544  		interfaces:        []interfaceBinding{{"name-1", "space-1"}},
   545  		expectedPositives: []gomaasapi.InterfaceSpec{{"name-1", "space-1"}, {"0", "2"}},
   546  		expectedNegatives: []string{"3"},
   547  	}, {
   548  		interfaces: []interfaceBinding{
   549  			{"name-1", "7"},
   550  			{"name-2", "8"},
   551  			{"name-3", "9"},
   552  		},
   553  		expectedPositives: []gomaasapi.InterfaceSpec{{"name-1", "7"}, {"name-2", "8"}, {"name-3", "9"}, {"0", "2"}},
   554  		expectedNegatives: []string{"3"},
   555  	}, {
   556  		interfaces:    []interfaceBinding{{"", "anything"}},
   557  		expectedError: "interface bindings cannot have empty names",
   558  	}, {
   559  		interfaces:    []interfaceBinding{{"shared-db", "3"}},
   560  		expectedError: `negative space "bar" from constraints clashes with interface bindings`,
   561  	}, {
   562  		interfaces: []interfaceBinding{
   563  			{"shared-db", "1"},
   564  			{"db", "1"},
   565  		},
   566  		expectedPositives: []gomaasapi.InterfaceSpec{{"shared-db", "1"}, {"db", "1"}, {"0", "2"}},
   567  		expectedNegatives: []string{"3"},
   568  	}, {
   569  		interfaces:    []interfaceBinding{{"", ""}},
   570  		expectedError: "interface bindings cannot have empty names",
   571  	}, {
   572  		interfaces: []interfaceBinding{
   573  			{"valid", "ok"},
   574  			{"", "valid-but-ignored-space"},
   575  			{"valid-name-empty-space", ""},
   576  			{"", ""},
   577  		},
   578  		expectedError: "interface bindings cannot have empty names",
   579  	}, {
   580  		interfaces:    []interfaceBinding{{"foo", ""}},
   581  		expectedError: `invalid interface binding "foo": space provider ID is required`,
   582  	}, {
   583  		interfaces: []interfaceBinding{
   584  			{"bar", ""},
   585  			{"valid", "ok"},
   586  			{"", "valid-but-ignored-space"},
   587  			{"", ""},
   588  		},
   589  		expectedError: `invalid interface binding "bar": space provider ID is required`,
   590  	}, {
   591  		interfaces: []interfaceBinding{
   592  			{"dup-name", "1"},
   593  			{"dup-name", "2"},
   594  		},
   595  		expectedError: `duplicated interface binding "dup-name"`,
   596  	}, {
   597  		interfaces: []interfaceBinding{
   598  			{"valid-1", "0"},
   599  			{"dup-name", "1"},
   600  			{"dup-name", "2"},
   601  			{"valid-2", "3"},
   602  		},
   603  		expectedError: `duplicated interface binding "dup-name"`,
   604  	}} {
   605  		c.Logf("test #%d: interfaces=%v", i, test.interfaces)
   606  		env = suite.makeEnviron(c, nil)
   607  		getNegatives = func() []string {
   608  			return test.expectedNegatives
   609  		}
   610  		getPositives = func() []gomaasapi.InterfaceSpec {
   611  			return test.expectedPositives
   612  		}
   613  		_, err := env.acquireNode2("", "", cons, test.interfaces, nil)
   614  		if test.expectedError != "" {
   615  			c.Check(err, gc.ErrorMatches, test.expectedError)
   616  			c.Check(err, jc.Satisfies, errors.IsNotValid)
   617  			continue
   618  		}
   619  		c.Check(err, jc.ErrorIsNil)
   620  	}
   621  }
   622  
   623  func getTwoSpaces() []gomaasapi.Space {
   624  	return []gomaasapi.Space{
   625  		fakeSpace{
   626  			name:    "foo",
   627  			subnets: []gomaasapi.Subnet{fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24"}},
   628  			id:      2,
   629  		},
   630  		fakeSpace{
   631  			name:    "bar",
   632  			subnets: []gomaasapi.Subnet{fakeSubnet{id: 100, vlan: fakeVLAN{vid: 66}, cidr: "192.168.11.0/24"}},
   633  			id:      3,
   634  		},
   635  	}
   636  }
   637  
   638  func (suite *maas2EnvironSuite) TestAcquireNodeConvertsSpaceNames(c *gc.C) {
   639  	expected := gomaasapi.AllocateMachineArgs{
   640  		NotSpace:   []string{"3"},
   641  		Interfaces: []gomaasapi.InterfaceSpec{{Label: "0", Space: "2"}},
   642  	}
   643  	env, _ := suite.injectControllerWithSpacesAndCheck(c, getTwoSpaces(), expected)
   644  	cons := constraints.Value{
   645  		Spaces: stringslicep("foo", "^bar"),
   646  	}
   647  	_, err := env.acquireNode2("", "", cons, nil, nil)
   648  	c.Assert(err, jc.ErrorIsNil)
   649  }
   650  
   651  func (suite *maas2EnvironSuite) TestAcquireNodeTranslatesSpaceNames(c *gc.C) {
   652  	expected := gomaasapi.AllocateMachineArgs{
   653  		NotSpace:   []string{"3"},
   654  		Interfaces: []gomaasapi.InterfaceSpec{{Label: "0", Space: "2"}},
   655  	}
   656  	env, _ := suite.injectControllerWithSpacesAndCheck(c, getTwoSpaces(), expected)
   657  	cons := constraints.Value{
   658  		Spaces: stringslicep("foo-1", "^bar-3"),
   659  	}
   660  	_, err := env.acquireNode2("", "", cons, nil, nil)
   661  	c.Assert(err, jc.ErrorIsNil)
   662  }
   663  
   664  func (suite *maas2EnvironSuite) TestAcquireNodeUnrecognisedSpace(c *gc.C) {
   665  	suite.injectController(&fakeController{})
   666  	env := suite.makeEnviron(c, nil)
   667  	cons := constraints.Value{
   668  		Spaces: stringslicep("baz"),
   669  	}
   670  	_, err := env.acquireNode2("", "", cons, nil, nil)
   671  	c.Assert(err, gc.ErrorMatches, `unrecognised space in constraint "baz"`)
   672  }
   673  
   674  func (suite *maas2EnvironSuite) TestWaitForNodeDeploymentError(c *gc.C) {
   675  	machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "")
   676  	controller := newFakeController()
   677  	controller.allocateMachine = machine
   678  	controller.allocateMachineMatches = gomaasapi.ConstraintMatches{
   679  		Storage: map[string][]gomaasapi.BlockDevice{},
   680  	}
   681  	controller.machines = []gomaasapi.Machine{machine}
   682  	suite.injectController(controller)
   683  	suite.setupFakeTools(c)
   684  	env := suite.makeEnviron(c, nil)
   685  	err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{
   686  		ControllerConfig: coretesting.FakeControllerConfig(),
   687  		AdminSecret:      jujutesting.AdminSecret,
   688  		CAPrivateKey:     coretesting.CAKey,
   689  	})
   690  	c.Assert(err, gc.ErrorMatches, "bootstrap instance started but did not change to Deployed state.*")
   691  }
   692  
   693  func (suite *maas2EnvironSuite) TestWaitForNodeDeploymentSucceeds(c *gc.C) {
   694  	machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "Deployed")
   695  	controller := newFakeController()
   696  	controller.allocateMachine = machine
   697  	controller.allocateMachineMatches = gomaasapi.ConstraintMatches{
   698  		Storage: map[string][]gomaasapi.BlockDevice{},
   699  	}
   700  	controller.machines = []gomaasapi.Machine{machine}
   701  	suite.injectController(controller)
   702  	suite.setupFakeTools(c)
   703  	env := suite.makeEnviron(c, nil)
   704  	err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{
   705  		ControllerConfig: coretesting.FakeControllerConfig(),
   706  		AdminSecret:      jujutesting.AdminSecret,
   707  		CAPrivateKey:     coretesting.CAKey,
   708  	})
   709  	c.Assert(err, jc.ErrorIsNil)
   710  }
   711  
   712  func (suite *maas2EnvironSuite) TestSubnetsNoFilters(c *gc.C) {
   713  	suite.injectController(&fakeController{
   714  		spaces: getFourSpaces(),
   715  	})
   716  	env := suite.makeEnviron(c, nil)
   717  	subnets, err := env.Subnets("", nil)
   718  	c.Assert(err, jc.ErrorIsNil)
   719  	expected := []network.SubnetInfo{
   720  		{CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"},
   721  		{CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 66, SpaceProviderId: "6"},
   722  		{CIDR: "192.168.12.0/24", ProviderId: "101", VLANTag: 66, SpaceProviderId: "7"},
   723  		{CIDR: "192.168.13.0/24", ProviderId: "102", VLANTag: 66, SpaceProviderId: "8"},
   724  	}
   725  	c.Assert(subnets, jc.DeepEquals, expected)
   726  }
   727  
   728  func (suite *maas2EnvironSuite) TestSubnetsNoFiltersError(c *gc.C) {
   729  	suite.injectController(&fakeController{
   730  		spacesError: errors.New("bang"),
   731  	})
   732  	env := suite.makeEnviron(c, nil)
   733  	_, err := env.Subnets("", nil)
   734  	c.Assert(err, gc.ErrorMatches, "bang")
   735  }
   736  
   737  func (suite *maas2EnvironSuite) TestSubnetsSubnetIds(c *gc.C) {
   738  	suite.injectController(&fakeController{
   739  		spaces: getFourSpaces(),
   740  	})
   741  	env := suite.makeEnviron(c, nil)
   742  	subnets, err := env.Subnets("", []network.Id{"99", "100"})
   743  	c.Assert(err, jc.ErrorIsNil)
   744  	expected := []network.SubnetInfo{
   745  		{CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"},
   746  		{CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 66, SpaceProviderId: "6"},
   747  	}
   748  	c.Assert(subnets, jc.DeepEquals, expected)
   749  }
   750  
   751  func (suite *maas2EnvironSuite) TestSubnetsSubnetIdsMissing(c *gc.C) {
   752  	suite.injectController(&fakeController{
   753  		spaces: getFourSpaces(),
   754  	})
   755  	env := suite.makeEnviron(c, nil)
   756  	_, err := env.Subnets("", []network.Id{"99", "missing"})
   757  	msg := "failed to find the following subnets: missing"
   758  	c.Assert(err, gc.ErrorMatches, msg)
   759  }
   760  
   761  func (suite *maas2EnvironSuite) TestSubnetsInstIdNotFound(c *gc.C) {
   762  	suite.injectController(&fakeController{})
   763  	env := suite.makeEnviron(c, nil)
   764  	_, err := env.Subnets("foo", nil)
   765  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   766  }
   767  
   768  func (suite *maas2EnvironSuite) TestSubnetsInstId(c *gc.C) {
   769  	interfaces := []gomaasapi.Interface{
   770  		&fakeInterface{
   771  			links: []gomaasapi.Link{
   772  				&fakeLink{subnet: fakeSubnet{id: 99, vlan: fakeVLAN{vid: 66}, cidr: "192.168.10.0/24", space: "space-1"}},
   773  				&fakeLink{subnet: fakeSubnet{id: 100, vlan: fakeVLAN{vid: 0}, cidr: "192.168.11.0/24", space: "space-2"}},
   774  			},
   775  		},
   776  		&fakeInterface{
   777  			links: []gomaasapi.Link{
   778  				&fakeLink{subnet: fakeSubnet{id: 101, vlan: fakeVLAN{vid: 2}, cidr: "192.168.12.0/24", space: "space-3"}},
   779  			},
   780  		},
   781  	}
   782  	machine := &fakeMachine{
   783  		systemID:     "William Gibson",
   784  		interfaceSet: interfaces,
   785  	}
   786  	machine2 := &fakeMachine{systemID: "Bruce Sterling"}
   787  	suite.injectController(&fakeController{
   788  		machines: []gomaasapi.Machine{machine, machine2},
   789  		spaces:   getFourSpaces(),
   790  	})
   791  	env := suite.makeEnviron(c, nil)
   792  	subnets, err := env.Subnets("William Gibson", nil)
   793  	c.Assert(err, jc.ErrorIsNil)
   794  	expected := []network.SubnetInfo{
   795  		{CIDR: "192.168.10.0/24", ProviderId: "99", VLANTag: 66, SpaceProviderId: "5"},
   796  		{CIDR: "192.168.11.0/24", ProviderId: "100", VLANTag: 0, SpaceProviderId: "6"},
   797  		{CIDR: "192.168.12.0/24", ProviderId: "101", VLANTag: 2, SpaceProviderId: "7"},
   798  	}
   799  	c.Assert(subnets, jc.DeepEquals, expected)
   800  }
   801  
   802  func (suite *maas2EnvironSuite) TestStartInstanceNetworkInterfaces(c *gc.C) {
   803  	vlan0 := fakeVLAN{
   804  		id:  5001,
   805  		vid: 0,
   806  		mtu: 1500,
   807  	}
   808  
   809  	vlan50 := fakeVLAN{
   810  		id:  5004,
   811  		vid: 50,
   812  		mtu: 1500,
   813  	}
   814  
   815  	subnetPXE := fakeSubnet{
   816  		id:         3,
   817  		space:      "default",
   818  		vlan:       vlan0,
   819  		gateway:    "10.20.19.2",
   820  		cidr:       "10.20.19.0/24",
   821  		dnsServers: []string{"10.20.19.2", "10.20.19.3"},
   822  	}
   823  
   824  	exampleInterfaces := []gomaasapi.Interface{
   825  		&fakeInterface{
   826  			id:         91,
   827  			name:       "eth0",
   828  			type_:      "physical",
   829  			enabled:    true,
   830  			macAddress: "52:54:00:70:9b:fe",
   831  			vlan:       vlan0,
   832  			links: []gomaasapi.Link{
   833  				&fakeLink{
   834  					id:        436,
   835  					subnet:    &subnetPXE,
   836  					ipAddress: "10.20.19.103",
   837  					mode:      "static",
   838  				},
   839  				&fakeLink{
   840  					id:        437,
   841  					subnet:    &subnetPXE,
   842  					ipAddress: "10.20.19.104",
   843  					mode:      "static",
   844  				},
   845  			},
   846  			parents:  []string{},
   847  			children: []string{"eth0.100", "eth0.250", "eth0.50"},
   848  		},
   849  		&fakeInterface{
   850  			id:         150,
   851  			name:       "eth0.50",
   852  			type_:      "vlan",
   853  			enabled:    true,
   854  			macAddress: "52:54:00:70:9b:fe",
   855  			vlan:       vlan50,
   856  			links: []gomaasapi.Link{
   857  				&fakeLink{
   858  					id: 517,
   859  					subnet: &fakeSubnet{
   860  						id:         5,
   861  						space:      "admin",
   862  						vlan:       vlan50,
   863  						gateway:    "10.50.19.2",
   864  						cidr:       "10.50.19.0/24",
   865  						dnsServers: []string{},
   866  					},
   867  					ipAddress: "10.50.19.103",
   868  					mode:      "static",
   869  				},
   870  			},
   871  			parents:  []string{"eth0"},
   872  			children: []string{},
   873  		},
   874  	}
   875  	var env *maasEnviron
   876  	machine := newFakeMachine("Bruce Sterling", arch.HostArch(), "")
   877  	machine.interfaceSet = exampleInterfaces
   878  	controller := &fakeController{
   879  		allocateMachine: machine,
   880  		allocateMachineMatches: gomaasapi.ConstraintMatches{
   881  			Storage: map[string][]gomaasapi.BlockDevice{},
   882  		},
   883  	}
   884  	suite.injectController(controller)
   885  	suite.setupFakeTools(c)
   886  	env = suite.makeEnviron(c, nil)
   887  
   888  	params := environs.StartInstanceParams{ControllerUUID: suite.controllerUUID}
   889  	result, err := jujutesting.StartInstanceWithParams(env, "1", params)
   890  	c.Assert(err, jc.ErrorIsNil)
   891  	expected := []network.InterfaceInfo{{
   892  		DeviceIndex:       0,
   893  		MACAddress:        "52:54:00:70:9b:fe",
   894  		CIDR:              "10.20.19.0/24",
   895  		ProviderId:        "91",
   896  		ProviderSubnetId:  "3",
   897  		AvailabilityZones: nil,
   898  		VLANTag:           0,
   899  		ProviderVLANId:    "5001",
   900  		ProviderAddressId: "436",
   901  		InterfaceName:     "eth0",
   902  		InterfaceType:     "ethernet",
   903  		Disabled:          false,
   904  		NoAutoStart:       false,
   905  		ConfigType:        "static",
   906  		Address:           network.NewAddressOnSpace("default", "10.20.19.103"),
   907  		DNSServers:        network.NewAddressesOnSpace("default", "10.20.19.2", "10.20.19.3"),
   908  		DNSSearchDomains:  nil,
   909  		MTU:               1500,
   910  		GatewayAddress:    network.NewAddressOnSpace("default", "10.20.19.2"),
   911  	}, {
   912  		DeviceIndex:       0,
   913  		MACAddress:        "52:54:00:70:9b:fe",
   914  		CIDR:              "10.20.19.0/24",
   915  		ProviderId:        "91",
   916  		ProviderSubnetId:  "3",
   917  		AvailabilityZones: nil,
   918  		VLANTag:           0,
   919  		ProviderVLANId:    "5001",
   920  		ProviderAddressId: "437",
   921  		InterfaceName:     "eth0",
   922  		InterfaceType:     "ethernet",
   923  		Disabled:          false,
   924  		NoAutoStart:       false,
   925  		ConfigType:        "static",
   926  		Address:           network.NewAddressOnSpace("default", "10.20.19.104"),
   927  		DNSServers:        network.NewAddressesOnSpace("default", "10.20.19.2", "10.20.19.3"),
   928  		DNSSearchDomains:  nil,
   929  		MTU:               1500,
   930  		GatewayAddress:    network.NewAddressOnSpace("default", "10.20.19.2"),
   931  	}, {
   932  		DeviceIndex:         1,
   933  		MACAddress:          "52:54:00:70:9b:fe",
   934  		CIDR:                "10.50.19.0/24",
   935  		ProviderId:          "150",
   936  		ProviderSubnetId:    "5",
   937  		AvailabilityZones:   nil,
   938  		VLANTag:             50,
   939  		ProviderVLANId:      "5004",
   940  		ProviderAddressId:   "517",
   941  		InterfaceName:       "eth0.50",
   942  		ParentInterfaceName: "eth0",
   943  		InterfaceType:       "802.1q",
   944  		Disabled:            false,
   945  		NoAutoStart:         false,
   946  		ConfigType:          "static",
   947  		Address:             network.NewAddressOnSpace("admin", "10.50.19.103"),
   948  		DNSServers:          nil,
   949  		DNSSearchDomains:    nil,
   950  		MTU:                 1500,
   951  		GatewayAddress:      network.NewAddressOnSpace("admin", "10.50.19.2"),
   952  	},
   953  	}
   954  	c.Assert(result.NetworkInfo, jc.DeepEquals, expected)
   955  }
   956  
   957  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSingleNic(c *gc.C) {
   958  	vlan1 := fakeVLAN{
   959  		id:  5001,
   960  		mtu: 1500,
   961  	}
   962  	vlan2 := fakeVLAN{
   963  		id:  5002,
   964  		mtu: 1500,
   965  	}
   966  	subnet1 := fakeSubnet{
   967  		id:         3,
   968  		space:      "default",
   969  		vlan:       vlan1,
   970  		gateway:    "10.20.19.2",
   971  		cidr:       "10.20.19.0/24",
   972  		dnsServers: []string{"10.20.19.2", "10.20.19.3"},
   973  	}
   974  	subnet2 := fakeSubnet{
   975  		id:         4,
   976  		space:      "freckles",
   977  		vlan:       vlan2,
   978  		gateway:    "192.168.1.1",
   979  		cidr:       "192.168.1.0/24",
   980  		dnsServers: []string{"10.20.19.2", "10.20.19.3"},
   981  	}
   982  
   983  	interfaces := []gomaasapi.Interface{
   984  		&fakeInterface{
   985  			id:         91,
   986  			name:       "eth0",
   987  			type_:      "physical",
   988  			enabled:    true,
   989  			macAddress: "52:54:00:70:9b:fe",
   990  			vlan:       vlan1,
   991  			links: []gomaasapi.Link{
   992  				&fakeLink{
   993  					id:        436,
   994  					subnet:    &subnet1,
   995  					ipAddress: "10.20.19.103",
   996  					mode:      "static",
   997  				},
   998  			},
   999  			parents:  []string{},
  1000  			children: []string{"eth0.100", "eth0.250", "eth0.50"},
  1001  		},
  1002  	}
  1003  	deviceInterfaces := []gomaasapi.Interface{
  1004  		&fakeInterface{
  1005  			id:         93,
  1006  			name:       "eth1",
  1007  			type_:      "physical",
  1008  			enabled:    true,
  1009  			macAddress: "53:54:00:70:9b:ff",
  1010  			vlan:       vlan2,
  1011  			links: []gomaasapi.Link{
  1012  				&fakeLink{
  1013  					id:        480,
  1014  					subnet:    &subnet2,
  1015  					ipAddress: "192.168.1.127",
  1016  					mode:      "static",
  1017  				},
  1018  			},
  1019  			parents:  []string{},
  1020  			children: []string{"eth0.100", "eth0.250", "eth0.50"},
  1021  		},
  1022  	}
  1023  	var env *maasEnviron
  1024  	device := &fakeDevice{
  1025  		interfaceSet: deviceInterfaces,
  1026  		systemID:     "foo",
  1027  	}
  1028  	controller := &fakeController{
  1029  		Stub: &testing.Stub{},
  1030  		machines: []gomaasapi.Machine{&fakeMachine{
  1031  			Stub:         &testing.Stub{},
  1032  			systemID:     "1",
  1033  			architecture: arch.HostArch(),
  1034  			interfaceSet: interfaces,
  1035  			createDevice: device,
  1036  		}},
  1037  		spaces: []gomaasapi.Space{
  1038  			fakeSpace{
  1039  				name:    "freckles",
  1040  				id:      4567,
  1041  				subnets: []gomaasapi.Subnet{subnet1, subnet2},
  1042  			},
  1043  		},
  1044  		devices: []gomaasapi.Device{device},
  1045  	}
  1046  	suite.injectController(controller)
  1047  	suite.setupFakeTools(c)
  1048  	env = suite.makeEnviron(c, nil)
  1049  
  1050  	prepared := []network.InterfaceInfo{{
  1051  		MACAddress:    "52:54:00:70:9b:fe",
  1052  		CIDR:          "10.20.19.0/24",
  1053  		InterfaceName: "eth0",
  1054  	}}
  1055  	ignored := names.NewMachineTag("1/lxd/0")
  1056  	result, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1057  	c.Assert(err, jc.ErrorIsNil)
  1058  	expected := []network.InterfaceInfo{{
  1059  		DeviceIndex:       0,
  1060  		MACAddress:        "53:54:00:70:9b:ff",
  1061  		CIDR:              "192.168.1.0/24",
  1062  		ProviderId:        "93",
  1063  		ProviderSubnetId:  "4",
  1064  		VLANTag:           0,
  1065  		ProviderVLANId:    "5002",
  1066  		ProviderAddressId: "480",
  1067  		InterfaceName:     "eth1",
  1068  		InterfaceType:     "ethernet",
  1069  		ConfigType:        "static",
  1070  		Address:           network.NewAddressOnSpace("freckles", "192.168.1.127"),
  1071  		DNSServers:        network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"),
  1072  		MTU:               1500,
  1073  		GatewayAddress:    network.NewAddressOnSpace("freckles", "192.168.1.1"),
  1074  	}}
  1075  	c.Assert(result, jc.DeepEquals, expected)
  1076  }
  1077  
  1078  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesDualNic(c *gc.C) {
  1079  	vlan1 := fakeVLAN{
  1080  		id:  5001,
  1081  		mtu: 1500,
  1082  	}
  1083  	vlan2 := fakeVLAN{
  1084  		id:  5002,
  1085  		mtu: 1500,
  1086  	}
  1087  	subnet1 := fakeSubnet{
  1088  		id:         3,
  1089  		space:      "freckles",
  1090  		vlan:       vlan1,
  1091  		gateway:    "10.20.19.2",
  1092  		cidr:       "10.20.19.0/24",
  1093  		dnsServers: []string{"10.20.19.2", "10.20.19.3"},
  1094  	}
  1095  	subnet2 := fakeSubnet{
  1096  		id:         4,
  1097  		space:      "freckles",
  1098  		vlan:       vlan2,
  1099  		gateway:    "192.168.1.1",
  1100  		cidr:       "192.168.1.0/24",
  1101  		dnsServers: []string{"10.20.19.2", "10.20.19.3"},
  1102  	}
  1103  
  1104  	interfaces := []gomaasapi.Interface{
  1105  		&fakeInterface{
  1106  			id:         91,
  1107  			name:       "eth0",
  1108  			type_:      "physical",
  1109  			enabled:    true,
  1110  			macAddress: "52:54:00:70:9b:fe",
  1111  			vlan:       vlan1,
  1112  			links: []gomaasapi.Link{
  1113  				&fakeLink{
  1114  					id:        436,
  1115  					subnet:    &subnet1,
  1116  					ipAddress: "10.20.19.103",
  1117  					mode:      "static",
  1118  				},
  1119  			},
  1120  			parents:  []string{},
  1121  			children: []string{"eth0.100", "eth0.250", "eth0.50"},
  1122  		},
  1123  		&fakeInterface{
  1124  			id:         92,
  1125  			name:       "eth1",
  1126  			type_:      "physical",
  1127  			enabled:    true,
  1128  			macAddress: "52:54:00:70:9b:ff",
  1129  			vlan:       vlan2,
  1130  			links: []gomaasapi.Link{
  1131  				&fakeLink{
  1132  					id:        437,
  1133  					subnet:    &subnet2,
  1134  					ipAddress: "192.168.1.4",
  1135  					mode:      "static",
  1136  				},
  1137  			},
  1138  		},
  1139  	}
  1140  	deviceInterfaces := []gomaasapi.Interface{
  1141  		&fakeInterface{
  1142  			id:         93,
  1143  			name:       "eth0",
  1144  			type_:      "physical",
  1145  			enabled:    true,
  1146  			macAddress: "53:54:00:70:9b:ff",
  1147  			vlan:       vlan1,
  1148  			links: []gomaasapi.Link{
  1149  				&fakeLink{
  1150  					id:        480,
  1151  					subnet:    &subnet1,
  1152  					ipAddress: "10.20.19.127",
  1153  					mode:      "static",
  1154  				},
  1155  			},
  1156  			parents:  []string{},
  1157  			children: []string{"eth0.100", "eth0.250", "eth0.50"},
  1158  			Stub:     &testing.Stub{},
  1159  		},
  1160  	}
  1161  	newInterface := &fakeInterface{
  1162  		id:         94,
  1163  		name:       "eth1",
  1164  		type_:      "physical",
  1165  		enabled:    true,
  1166  		macAddress: "52:54:00:70:9b:f4",
  1167  		vlan:       vlan2,
  1168  		links: []gomaasapi.Link{
  1169  			&fakeLink{
  1170  				id:        481,
  1171  				subnet:    &subnet2,
  1172  				ipAddress: "192.168.1.127",
  1173  				mode:      "static",
  1174  			},
  1175  		},
  1176  		Stub: &testing.Stub{},
  1177  	}
  1178  	device := &fakeDevice{
  1179  		interfaceSet: deviceInterfaces,
  1180  		systemID:     "foo",
  1181  		interface_:   newInterface,
  1182  		Stub:         &testing.Stub{},
  1183  	}
  1184  	controller := &fakeController{
  1185  		Stub: &testing.Stub{},
  1186  		machines: []gomaasapi.Machine{&fakeMachine{
  1187  			Stub:         &testing.Stub{},
  1188  			systemID:     "1",
  1189  			architecture: arch.HostArch(),
  1190  			interfaceSet: interfaces,
  1191  			createDevice: device,
  1192  		}},
  1193  		spaces: []gomaasapi.Space{
  1194  			fakeSpace{
  1195  				name:    "freckles",
  1196  				id:      4567,
  1197  				subnets: []gomaasapi.Subnet{subnet1, subnet2},
  1198  			},
  1199  		},
  1200  		devices: []gomaasapi.Device{device},
  1201  	}
  1202  	suite.injectController(controller)
  1203  	env := suite.makeEnviron(c, nil)
  1204  
  1205  	prepared := []network.InterfaceInfo{{
  1206  		MACAddress:    "53:54:00:70:9b:ff",
  1207  		CIDR:          "10.20.19.0/24",
  1208  		InterfaceName: "eth0",
  1209  	}, {
  1210  		MACAddress:    "52:54:00:70:9b:f4",
  1211  		CIDR:          "192.168.1.0/24",
  1212  		InterfaceName: "eth1",
  1213  	}}
  1214  	expected := []network.InterfaceInfo{{
  1215  		DeviceIndex:       0,
  1216  		MACAddress:        "53:54:00:70:9b:ff",
  1217  		CIDR:              "10.20.19.0/24",
  1218  		ProviderId:        "93",
  1219  		ProviderSubnetId:  "3",
  1220  		ProviderVLANId:    "5001",
  1221  		ProviderAddressId: "480",
  1222  		InterfaceName:     "eth0",
  1223  		InterfaceType:     "ethernet",
  1224  		ConfigType:        "static",
  1225  		Address:           network.NewAddressOnSpace("freckles", "10.20.19.127"),
  1226  		DNSServers:        network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"),
  1227  		MTU:               1500,
  1228  		GatewayAddress:    network.NewAddressOnSpace("freckles", "10.20.19.2"),
  1229  	}, {
  1230  		DeviceIndex:       0,
  1231  		MACAddress:        "52:54:00:70:9b:f4",
  1232  		CIDR:              "192.168.1.0/24",
  1233  		ProviderId:        "94",
  1234  		ProviderSubnetId:  "4",
  1235  		ProviderVLANId:    "5002",
  1236  		ProviderAddressId: "481",
  1237  		InterfaceName:     "eth1",
  1238  		InterfaceType:     "ethernet",
  1239  		ConfigType:        "static",
  1240  		Address:           network.NewAddressOnSpace("freckles", "192.168.1.127"),
  1241  		DNSServers:        network.NewAddressesOnSpace("freckles", "10.20.19.2", "10.20.19.3"),
  1242  		MTU:               1500,
  1243  		GatewayAddress:    network.NewAddressOnSpace("freckles", "192.168.1.1"),
  1244  	}}
  1245  	ignored := names.NewMachineTag("1/lxd/0")
  1246  	result, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1247  	c.Assert(err, jc.ErrorIsNil)
  1248  	c.Assert(result, jc.DeepEquals, expected)
  1249  }
  1250  
  1251  func (suite *maas2EnvironSuite) assertAllocateContainerAddressesFails(c *gc.C, controller *fakeController, prepared []network.InterfaceInfo, errorMatches string) {
  1252  	if prepared == nil {
  1253  		prepared = []network.InterfaceInfo{{}}
  1254  	}
  1255  	suite.injectController(controller)
  1256  	env := suite.makeEnviron(c, nil)
  1257  	ignored := names.NewMachineTag("1/lxd/0")
  1258  	_, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1259  	c.Assert(err, gc.ErrorMatches, errorMatches)
  1260  }
  1261  
  1262  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSpacesError(c *gc.C) {
  1263  	controller := &fakeController{spacesError: errors.New("boom")}
  1264  	suite.assertAllocateContainerAddressesFails(c, controller, nil, "boom")
  1265  }
  1266  
  1267  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesPrimaryInterfaceMissing(c *gc.C) {
  1268  	controller := &fakeController{}
  1269  	suite.assertAllocateContainerAddressesFails(c, controller, nil, "cannot find primary interface for container")
  1270  }
  1271  
  1272  func makeFakeSubnet(id int) fakeSubnet {
  1273  	return fakeSubnet{
  1274  		id:      id,
  1275  		space:   "freckles",
  1276  		gateway: fmt.Sprintf("10.20.%d.2", 16+id),
  1277  		cidr:    fmt.Sprintf("10.20.%d.0/24", 16+id),
  1278  	}
  1279  }
  1280  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesMachinesError(c *gc.C) {
  1281  	var env *maasEnviron
  1282  	subnet := makeFakeSubnet(3)
  1283  	checkMachinesArgs := func(args gomaasapi.MachinesArgs) {
  1284  		expected := gomaasapi.MachinesArgs{
  1285  			AgentName: env.Config().UUID(),
  1286  			SystemIDs: []string{"1"},
  1287  		}
  1288  		c.Assert(args, jc.DeepEquals, expected)
  1289  	}
  1290  	controller := &fakeController{
  1291  		machinesError:     errors.New("boom"),
  1292  		machinesArgsCheck: checkMachinesArgs,
  1293  		spaces: []gomaasapi.Space{
  1294  			fakeSpace{
  1295  				name:    "freckles",
  1296  				id:      4567,
  1297  				subnets: []gomaasapi.Subnet{subnet},
  1298  			},
  1299  		},
  1300  	}
  1301  	suite.injectController(controller)
  1302  	env = suite.makeEnviron(c, nil)
  1303  	prepared := []network.InterfaceInfo{
  1304  		{InterfaceName: "eth0", CIDR: "10.20.19.0/24"},
  1305  	}
  1306  	ignored := names.NewMachineTag("1/lxd/0")
  1307  	_, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1308  	c.Assert(err, gc.ErrorMatches, "boom")
  1309  }
  1310  
  1311  func getArgs(c *gc.C, calls []testing.StubCall) interface{} {
  1312  	c.Assert(calls, gc.HasLen, 1)
  1313  	args := calls[0].Args
  1314  	c.Assert(args, gc.HasLen, 1)
  1315  	return args[0]
  1316  }
  1317  
  1318  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateDevicerror(c *gc.C) {
  1319  	subnet := makeFakeSubnet(3)
  1320  	var env *maasEnviron
  1321  	machine := &fakeMachine{
  1322  		Stub:     &testing.Stub{},
  1323  		systemID: "1",
  1324  	}
  1325  	machine.SetErrors(errors.New("boom"))
  1326  	controller := &fakeController{
  1327  		machines: []gomaasapi.Machine{machine},
  1328  		spaces: []gomaasapi.Space{
  1329  			fakeSpace{
  1330  				name:    "freckles",
  1331  				id:      4567,
  1332  				subnets: []gomaasapi.Subnet{subnet},
  1333  			},
  1334  		},
  1335  	}
  1336  	suite.injectController(controller)
  1337  	env = suite.makeEnviron(c, nil)
  1338  	prepared := []network.InterfaceInfo{
  1339  		{InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"},
  1340  	}
  1341  	ignored := names.NewMachineTag("1/lxd/0")
  1342  	_, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1343  	c.Assert(err, gc.ErrorMatches, "boom")
  1344  	args := getArgs(c, machine.Calls())
  1345  	maasArgs, ok := args.(gomaasapi.CreateMachineDeviceArgs)
  1346  	c.Assert(ok, jc.IsTrue)
  1347  	expected := gomaasapi.CreateMachineDeviceArgs{
  1348  		Hostname:      "juju-06f00d-1-lxd-0",
  1349  		Subnet:        subnet,
  1350  		MACAddress:    "DEADBEEF",
  1351  		InterfaceName: "eth0",
  1352  	}
  1353  	c.Assert(maasArgs, jc.DeepEquals, expected)
  1354  }
  1355  
  1356  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesSubnetMissing(c *gc.C) {
  1357  	subnet := makeFakeSubnet(3)
  1358  	var env *maasEnviron
  1359  	device := &fakeDevice{
  1360  		Stub: &testing.Stub{},
  1361  		interfaceSet: []gomaasapi.Interface{
  1362  			&fakeInterface{
  1363  				id:         93,
  1364  				name:       "eth0",
  1365  				type_:      "physical",
  1366  				enabled:    true,
  1367  				macAddress: "53:54:00:70:9b:ff",
  1368  				vlan:       &fakeVLAN{vid: 0},
  1369  				links: []gomaasapi.Link{
  1370  					&fakeLink{
  1371  						id:   480,
  1372  						mode: "link_up",
  1373  					},
  1374  				},
  1375  				parents:  []string{},
  1376  				children: []string{},
  1377  				Stub:     &testing.Stub{},
  1378  			},
  1379  		},
  1380  		interface_: &fakeInterface{
  1381  			id:         94,
  1382  			name:       "eth1",
  1383  			type_:      "physical",
  1384  			enabled:    true,
  1385  			macAddress: "53:54:00:70:9b:f1",
  1386  			vlan:       &fakeVLAN{vid: 0},
  1387  			links: []gomaasapi.Link{
  1388  				&fakeLink{
  1389  					id:   481,
  1390  					mode: "link_up",
  1391  				},
  1392  			},
  1393  			parents:  []string{},
  1394  			children: []string{},
  1395  			Stub:     &testing.Stub{},
  1396  		},
  1397  		systemID: "foo",
  1398  	}
  1399  	machine := &fakeMachine{
  1400  		Stub:         &testing.Stub{},
  1401  		systemID:     "1",
  1402  		createDevice: device,
  1403  	}
  1404  	controller := &fakeController{
  1405  		Stub:     &testing.Stub{},
  1406  		machines: []gomaasapi.Machine{machine},
  1407  		spaces: []gomaasapi.Space{
  1408  			fakeSpace{
  1409  				name:    "freckles",
  1410  				id:      4567,
  1411  				subnets: []gomaasapi.Subnet{subnet},
  1412  			},
  1413  		},
  1414  		devices: []gomaasapi.Device{device},
  1415  	}
  1416  	suite.injectController(controller)
  1417  	env = suite.makeEnviron(c, nil)
  1418  	prepared := []network.InterfaceInfo{
  1419  		{InterfaceName: "eth0", CIDR: "", MACAddress: "DEADBEEF"},
  1420  		{InterfaceName: "eth1", CIDR: "", MACAddress: "DEADBEEE"},
  1421  	}
  1422  	ignored := names.NewMachineTag("1/lxd/0")
  1423  	allocated, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1424  	c.Assert(err, jc.ErrorIsNil)
  1425  	c.Assert(allocated, jc.DeepEquals, []network.InterfaceInfo{{
  1426  		MACAddress:     "53:54:00:70:9b:ff",
  1427  		ProviderId:     "93",
  1428  		ProviderVLANId: "0",
  1429  		VLANTag:        0,
  1430  		InterfaceName:  "eth0",
  1431  		InterfaceType:  "ethernet",
  1432  		Disabled:       false,
  1433  		NoAutoStart:    false,
  1434  		ConfigType:     "manual",
  1435  		MTU:            1500,
  1436  	}, {
  1437  		MACAddress:     "53:54:00:70:9b:f1",
  1438  		ProviderId:     "94",
  1439  		ProviderVLANId: "0",
  1440  		VLANTag:        0,
  1441  		InterfaceName:  "eth1",
  1442  		InterfaceType:  "ethernet",
  1443  		Disabled:       false,
  1444  		NoAutoStart:    false,
  1445  		ConfigType:     "manual",
  1446  		MTU:            1500,
  1447  	}})
  1448  }
  1449  
  1450  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesCreateInterfaceError(c *gc.C) {
  1451  	subnet := makeFakeSubnet(3)
  1452  	subnet2 := makeFakeSubnet(4)
  1453  	subnet2.vlan = fakeVLAN{vid: 66}
  1454  	var env *maasEnviron
  1455  	device := &fakeDevice{
  1456  		Stub:         &testing.Stub{},
  1457  		interfaceSet: []gomaasapi.Interface{&fakeInterface{}},
  1458  		systemID:     "foo",
  1459  	}
  1460  	device.SetErrors(errors.New("boom"))
  1461  	machine := &fakeMachine{
  1462  		Stub:         &testing.Stub{},
  1463  		systemID:     "1",
  1464  		createDevice: device,
  1465  	}
  1466  	controller := &fakeController{
  1467  		machines: []gomaasapi.Machine{machine},
  1468  		spaces: []gomaasapi.Space{
  1469  			fakeSpace{
  1470  				name:    "freckles",
  1471  				id:      4567,
  1472  				subnets: []gomaasapi.Subnet{subnet, subnet2},
  1473  			},
  1474  		},
  1475  	}
  1476  	suite.injectController(controller)
  1477  	env = suite.makeEnviron(c, nil)
  1478  	prepared := []network.InterfaceInfo{
  1479  		{InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"},
  1480  		{InterfaceName: "eth1", CIDR: "10.20.20.0/24", MACAddress: "DEADBEEE"},
  1481  	}
  1482  	ignored := names.NewMachineTag("1/lxd/0")
  1483  	_, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1484  	c.Assert(err, gc.ErrorMatches, "creating device interface: boom")
  1485  	args := getArgs(c, device.Calls())
  1486  	maasArgs, ok := args.(gomaasapi.CreateInterfaceArgs)
  1487  	c.Assert(ok, jc.IsTrue)
  1488  	expected := gomaasapi.CreateInterfaceArgs{
  1489  		MACAddress: "DEADBEEE",
  1490  		Name:       "eth1",
  1491  		VLAN:       subnet2.VLAN(),
  1492  	}
  1493  	c.Assert(maasArgs, jc.DeepEquals, expected)
  1494  }
  1495  
  1496  func (suite *maas2EnvironSuite) TestAllocateContainerAddressesLinkSubnetError(c *gc.C) {
  1497  	subnet := makeFakeSubnet(3)
  1498  	subnet2 := makeFakeSubnet(4)
  1499  	subnet2.vlan = fakeVLAN{vid: 66}
  1500  	var env *maasEnviron
  1501  	interface_ := &fakeInterface{Stub: &testing.Stub{}}
  1502  	interface_.SetErrors(errors.New("boom"))
  1503  	device := &fakeDevice{
  1504  		Stub:         &testing.Stub{},
  1505  		interfaceSet: []gomaasapi.Interface{&fakeInterface{}},
  1506  		interface_:   interface_,
  1507  		systemID:     "foo",
  1508  	}
  1509  	machine := &fakeMachine{
  1510  		Stub:         &testing.Stub{},
  1511  		systemID:     "1",
  1512  		createDevice: device,
  1513  	}
  1514  	controller := &fakeController{
  1515  		Stub:     &testing.Stub{},
  1516  		machines: []gomaasapi.Machine{machine},
  1517  		spaces: []gomaasapi.Space{
  1518  			fakeSpace{
  1519  				name:    "freckles",
  1520  				id:      4567,
  1521  				subnets: []gomaasapi.Subnet{subnet, subnet2},
  1522  			},
  1523  		},
  1524  		devices: []gomaasapi.Device{device},
  1525  	}
  1526  	suite.injectController(controller)
  1527  	env = suite.makeEnviron(c, nil)
  1528  	prepared := []network.InterfaceInfo{
  1529  		{InterfaceName: "eth0", CIDR: "10.20.19.0/24", MACAddress: "DEADBEEF"},
  1530  		{InterfaceName: "eth1", CIDR: "10.20.20.0/24", MACAddress: "DEADBEEE"},
  1531  	}
  1532  	ignored := names.NewMachineTag("1/lxd/0")
  1533  	allocated, err := env.AllocateContainerAddresses(instance.Id("1"), ignored, prepared)
  1534  	c.Assert(err, jc.ErrorIsNil)
  1535  	c.Assert(allocated, jc.DeepEquals, []network.InterfaceInfo{{
  1536  		CIDR:             "",
  1537  		ProviderId:       "0",
  1538  		ProviderSubnetId: "",
  1539  		ProviderVLANId:   "0",
  1540  		VLANTag:          0,
  1541  		InterfaceName:    "",
  1542  		InterfaceType:    "ethernet",
  1543  		ConfigType:       "",
  1544  		MTU:              1500,
  1545  		Disabled:         true,
  1546  		NoAutoStart:      true,
  1547  	}, {
  1548  		CIDR:             "",
  1549  		ProviderId:       "0",
  1550  		ProviderSubnetId: "",
  1551  		ProviderVLANId:   "0",
  1552  		VLANTag:          0,
  1553  		InterfaceName:    "",
  1554  		InterfaceType:    "ethernet",
  1555  		ConfigType:       "",
  1556  		MTU:              1500,
  1557  		Disabled:         true,
  1558  		NoAutoStart:      true,
  1559  	}})
  1560  
  1561  	args := getArgs(c, interface_.Calls())
  1562  	maasArgs, ok := args.(gomaasapi.LinkSubnetArgs)
  1563  	c.Assert(ok, jc.IsTrue)
  1564  	expected := gomaasapi.LinkSubnetArgs{
  1565  		Mode:   gomaasapi.LinkModeStatic,
  1566  		Subnet: subnet2,
  1567  	}
  1568  	c.Assert(maasArgs, jc.DeepEquals, expected)
  1569  }
  1570  func (suite *maas2EnvironSuite) TestStorageReturnsStorage(c *gc.C) {
  1571  	controller := newFakeController()
  1572  	env := suite.makeEnviron(c, controller)
  1573  	stor := env.Storage()
  1574  	c.Check(stor, gc.NotNil)
  1575  
  1576  	// The Storage object is really a maas2Storage.
  1577  	specificStorage := stor.(*maas2Storage)
  1578  
  1579  	// Its environment pointer refers back to its environment.
  1580  	c.Check(specificStorage.environ, gc.Equals, env)
  1581  	c.Check(specificStorage.maasController, gc.Equals, controller)
  1582  }
  1583  
  1584  func (suite *maas2EnvironSuite) TestStartInstanceEndToEnd(c *gc.C) {
  1585  	suite.setupFakeTools(c)
  1586  	machine := newFakeMachine("gus", arch.HostArch(), "Deployed")
  1587  	file := &fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}
  1588  	controller := newFakeControllerWithFiles(file)
  1589  	controller.machines = []gomaasapi.Machine{machine}
  1590  	controller.allocateMachine = machine
  1591  	controller.allocateMachineMatches = gomaasapi.ConstraintMatches{
  1592  		Storage: make(map[string][]gomaasapi.BlockDevice),
  1593  	}
  1594  
  1595  	env := suite.makeEnviron(c, controller)
  1596  	err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{
  1597  		ControllerConfig: coretesting.FakeControllerConfig(),
  1598  		AdminSecret:      jujutesting.AdminSecret,
  1599  		CAPrivateKey:     coretesting.CAKey,
  1600  	})
  1601  	c.Assert(err, jc.ErrorIsNil)
  1602  
  1603  	machine.Stub.CheckCallNames(c, "Start", "SetOwnerData")
  1604  	ownerData, ok := machine.Stub.Calls()[1].Args[0].(map[string]string)
  1605  	c.Assert(ok, jc.IsTrue)
  1606  	c.Assert(ownerData, gc.DeepEquals, map[string]string{
  1607  		"claude":              "rains",
  1608  		tags.JujuController:   suite.controllerUUID,
  1609  		tags.JujuIsController: "true",
  1610  		tags.JujuModel:        env.Config().UUID(),
  1611  	})
  1612  
  1613  	// Test the instance id is correctly recorded for the bootstrap node.
  1614  	// Check that ControllerInstances returns the id of the bootstrap machine.
  1615  	instanceIds, err := env.ControllerInstances(suite.controllerUUID)
  1616  	c.Assert(err, jc.ErrorIsNil)
  1617  	c.Assert(instanceIds, gc.HasLen, 1)
  1618  	insts, err := env.AllInstances()
  1619  	c.Assert(err, jc.ErrorIsNil)
  1620  	c.Assert(insts, gc.HasLen, 1)
  1621  	c.Check(insts[0].Id(), gc.Equals, instanceIds[0])
  1622  
  1623  	node1 := newFakeMachine("victor", arch.HostArch(), "Deployed")
  1624  	node1.hostname = "host1"
  1625  	node1.cpuCount = 1
  1626  	node1.memory = 1024
  1627  	node1.zoneName = "test_zone"
  1628  	controller.allocateMachine = node1
  1629  
  1630  	instance, hc := jujutesting.AssertStartInstance(c, env, suite.controllerUUID, "1")
  1631  	c.Check(instance, gc.NotNil)
  1632  	c.Assert(hc, gc.NotNil)
  1633  	c.Check(hc.String(), gc.Equals, fmt.Sprintf("arch=%s cores=1 mem=1024M availability-zone=test_zone", arch.HostArch()))
  1634  
  1635  	node1.Stub.CheckCallNames(c, "Start", "SetOwnerData")
  1636  	startArgs, ok := node1.Stub.Calls()[0].Args[0].(gomaasapi.StartArgs)
  1637  	c.Assert(ok, jc.IsTrue)
  1638  
  1639  	decodedUserData, err := decodeUserData(startArgs.UserData)
  1640  	c.Assert(err, jc.ErrorIsNil)
  1641  	info := machineInfo{"host1"}
  1642  	cloudcfg, err := cloudinit.New("precise")
  1643  	c.Assert(err, jc.ErrorIsNil)
  1644  	cloudinitRunCmd, err := info.cloudinitRunCmd(cloudcfg)
  1645  	c.Assert(err, jc.ErrorIsNil)
  1646  	data, err := goyaml.Marshal(cloudinitRunCmd)
  1647  	c.Assert(err, jc.ErrorIsNil)
  1648  	c.Check(string(decodedUserData), jc.Contains, string(data))
  1649  
  1650  	// Trash the tools and try to start another instance.
  1651  	suite.PatchValue(&envtools.DefaultBaseURL, "")
  1652  	instance, _, _, err = jujutesting.StartInstance(env, suite.controllerUUID, "2")
  1653  	c.Check(instance, gc.IsNil)
  1654  	c.Check(err, jc.Satisfies, errors.IsNotFound)
  1655  }
  1656  
  1657  func (suite *maas2EnvironSuite) TestControllerInstances(c *gc.C) {
  1658  	controller := newFakeControllerWithErrors(gomaasapi.NewNoMatchError("state"))
  1659  	env := suite.makeEnviron(c, controller)
  1660  	_, err := env.ControllerInstances(suite.controllerUUID)
  1661  	c.Assert(err, gc.Equals, environs.ErrNotBootstrapped)
  1662  
  1663  	controller.machinesArgsCheck = func(args gomaasapi.MachinesArgs) {
  1664  		c.Assert(args, gc.DeepEquals, gomaasapi.MachinesArgs{
  1665  			OwnerData: map[string]string{
  1666  				tags.JujuIsController: "true",
  1667  				tags.JujuController:   suite.controllerUUID,
  1668  			},
  1669  		})
  1670  	}
  1671  
  1672  	tests := [][]instance.Id{{"inst-0"}, {"inst-0", "inst-1"}}
  1673  	for _, expected := range tests {
  1674  		controller.machines = make([]gomaasapi.Machine, len(expected))
  1675  		for i := range expected {
  1676  			controller.machines[i] = newFakeMachine(string(expected[i]), "", "")
  1677  		}
  1678  		controllerInstances, err := env.ControllerInstances(suite.controllerUUID)
  1679  		c.Assert(err, jc.ErrorIsNil)
  1680  		c.Assert(controllerInstances, jc.SameContents, expected)
  1681  	}
  1682  }
  1683  
  1684  func (suite *maas2EnvironSuite) TestDestroy(c *gc.C) {
  1685  	file1 := &fakeFile{name: coretesting.ModelTag.Id() + "-provider-state"}
  1686  	file2 := &fakeFile{name: coretesting.ModelTag.Id() + "-horace"}
  1687  	controller := newFakeControllerWithFiles(file1, file2)
  1688  	controller.machines = []gomaasapi.Machine{&fakeMachine{systemID: "pete"}}
  1689  	env := suite.makeEnviron(c, controller)
  1690  	err := env.Destroy()
  1691  	c.Check(err, jc.ErrorIsNil)
  1692  
  1693  	controller.Stub.CheckCallNames(c, "ReleaseMachines", "GetFile", "Files", "GetFile", "GetFile")
  1694  	// Instances have been stopped.
  1695  	controller.Stub.CheckCall(c, 0, "ReleaseMachines", gomaasapi.ReleaseMachinesArgs{
  1696  		SystemIDs: []string{"pete"},
  1697  		Comment:   "Released by Juju MAAS provider",
  1698  	})
  1699  
  1700  	// Files have been cleaned up.
  1701  	c.Check(file1.deleted, jc.IsTrue)
  1702  	c.Check(file2.deleted, jc.IsTrue)
  1703  }
  1704  
  1705  func (suite *maas2EnvironSuite) TestBootstrapFailsIfNoTools(c *gc.C) {
  1706  	env := suite.makeEnviron(c, newFakeController())
  1707  	vers := version.MustParse("1.2.3")
  1708  	err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{
  1709  		ControllerConfig: coretesting.FakeControllerConfig(),
  1710  		AdminSecret:      jujutesting.AdminSecret,
  1711  		CAPrivateKey:     coretesting.CAKey,
  1712  		// Disable auto-uploading by setting the agent version
  1713  		// to something that's not the current version.
  1714  		AgentVersion: &vers,
  1715  	})
  1716  	c.Check(err, gc.ErrorMatches, "Juju cannot bootstrap because no agent binaries are available for your model(.|\n)*")
  1717  }
  1718  
  1719  func (suite *maas2EnvironSuite) TestBootstrapFailsIfNoNodes(c *gc.C) {
  1720  	suite.setupFakeTools(c)
  1721  	controller := newFakeController()
  1722  	controller.allocateMachineError = gomaasapi.NewNoMatchError("oops")
  1723  	env := suite.makeEnviron(c, controller)
  1724  	err := bootstrap.Bootstrap(envjujutesting.BootstrapContext(c), env, bootstrap.BootstrapParams{
  1725  		ControllerConfig: coretesting.FakeControllerConfig(),
  1726  		AdminSecret:      jujutesting.AdminSecret,
  1727  		CAPrivateKey:     coretesting.CAKey,
  1728  	})
  1729  	// Since there are no nodes, the attempt to allocate one returns a
  1730  	// 409: Conflict.
  1731  	c.Check(err, gc.ErrorMatches, ".*cannot run instances.*")
  1732  }
  1733  
  1734  func (suite *maas2EnvironSuite) TestGetToolsMetadataSources(c *gc.C) {
  1735  	// Add a dummy file to storage so we can use that to check the
  1736  	// obtained source later.
  1737  	env := suite.makeEnviron(c, newFakeControllerWithFiles(
  1738  		&fakeFile{name: coretesting.ModelTag.Id() + "-tools/filename", contents: makeRandomBytes(10)},
  1739  	))
  1740  	sources, err := envtools.GetMetadataSources(env)
  1741  	c.Assert(err, jc.ErrorIsNil)
  1742  	c.Assert(sources, gc.HasLen, 0)
  1743  }
  1744  
  1745  func (suite *maas2EnvironSuite) TestConstraintsValidator(c *gc.C) {
  1746  	controller := newFakeController()
  1747  	controller.bootResources = []gomaasapi.BootResource{&fakeBootResource{name: "trusty", architecture: "amd64"}}
  1748  	env := suite.makeEnviron(c, controller)
  1749  	validator, err := env.ConstraintsValidator()
  1750  	c.Assert(err, jc.ErrorIsNil)
  1751  	cons := constraints.MustParse("arch=amd64 cpu-power=10 instance-type=foo virt-type=kvm")
  1752  	unsupported, err := validator.Validate(cons)
  1753  	c.Assert(err, jc.ErrorIsNil)
  1754  	c.Assert(unsupported, jc.SameContents, []string{"cpu-power", "instance-type", "virt-type"})
  1755  }
  1756  
  1757  func (suite *maas2EnvironSuite) TestConstraintsValidatorVocab(c *gc.C) {
  1758  	controller := newFakeController()
  1759  	controller.bootResources = []gomaasapi.BootResource{
  1760  		&fakeBootResource{name: "trusty", architecture: "amd64"},
  1761  		&fakeBootResource{name: "precise", architecture: "armhf"},
  1762  	}
  1763  	env := suite.makeEnviron(c, controller)
  1764  	validator, err := env.ConstraintsValidator()
  1765  	c.Assert(err, jc.ErrorIsNil)
  1766  	cons := constraints.MustParse("arch=ppc64el")
  1767  	_, err = validator.Validate(cons)
  1768  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=ppc64el\nvalid values are: \\[amd64 armhf\\]")
  1769  }
  1770  
  1771  func (suite *maas2EnvironSuite) TestReleaseContainerAddresses(c *gc.C) {
  1772  	dev1 := newFakeDevice("a", "eleven")
  1773  	dev2 := newFakeDevice("b", "will")
  1774  	controller := newFakeController()
  1775  	controller.devices = []gomaasapi.Device{dev1, dev2}
  1776  
  1777  	env := suite.makeEnviron(c, controller)
  1778  	err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{
  1779  		{MACAddress: "will"},
  1780  		{MACAddress: "dustin"},
  1781  		{MACAddress: "eleven"},
  1782  	})
  1783  	c.Assert(err, jc.ErrorIsNil)
  1784  
  1785  	args, ok := getArgs(c, controller.Calls()).(gomaasapi.DevicesArgs)
  1786  	c.Assert(ok, jc.IsTrue)
  1787  	expected := gomaasapi.DevicesArgs{MACAddresses: []string{"will", "dustin", "eleven"}}
  1788  	c.Assert(args, gc.DeepEquals, expected)
  1789  
  1790  	dev1.CheckCallNames(c, "Delete")
  1791  	dev2.CheckCallNames(c, "Delete")
  1792  }
  1793  
  1794  func (suite *maas2EnvironSuite) TestReleaseContainerAddresses_HandlesDupes(c *gc.C) {
  1795  	dev1 := newFakeDevice("a", "eleven")
  1796  	controller := newFakeController()
  1797  	controller.devices = []gomaasapi.Device{dev1, dev1}
  1798  
  1799  	env := suite.makeEnviron(c, controller)
  1800  	err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{
  1801  		{MACAddress: "will"},
  1802  		{MACAddress: "eleven"},
  1803  	})
  1804  	c.Assert(err, jc.ErrorIsNil)
  1805  
  1806  	args, ok := getArgs(c, controller.Calls()).(gomaasapi.DevicesArgs)
  1807  	c.Assert(ok, jc.IsTrue)
  1808  	expected := gomaasapi.DevicesArgs{MACAddresses: []string{"will", "eleven"}}
  1809  	c.Assert(args, gc.DeepEquals, expected)
  1810  
  1811  	dev1.CheckCallNames(c, "Delete")
  1812  }
  1813  
  1814  func (suite *maas2EnvironSuite) TestReleaseContainerAddressesErrorGettingDevices(c *gc.C) {
  1815  	controller := newFakeControllerWithErrors(errors.New("Everything done broke"))
  1816  	env := suite.makeEnviron(c, controller)
  1817  	err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{{MACAddress: "anything"}})
  1818  	c.Assert(err, gc.ErrorMatches, "Everything done broke")
  1819  }
  1820  
  1821  func (suite *maas2EnvironSuite) TestReleaseContainerAddressesErrorDeletingDevice(c *gc.C) {
  1822  	dev1 := newFakeDevice("a", "eleven")
  1823  	dev1.systemID = "hopper"
  1824  	dev1.SetErrors(errors.New("don't delete me"))
  1825  	controller := newFakeController()
  1826  	controller.devices = []gomaasapi.Device{dev1}
  1827  
  1828  	env := suite.makeEnviron(c, controller)
  1829  	err := env.ReleaseContainerAddresses([]network.ProviderInterfaceInfo{
  1830  		{MACAddress: "eleven"},
  1831  	})
  1832  	c.Assert(err, gc.ErrorMatches, "deleting device hopper: don't delete me")
  1833  
  1834  	_, ok := getArgs(c, controller.Calls()).(gomaasapi.DevicesArgs)
  1835  	c.Assert(ok, jc.IsTrue)
  1836  
  1837  	dev1.CheckCallNames(c, "Delete")
  1838  }
  1839  
  1840  func newFakeDevice(systemID, macAddress string) *fakeDevice {
  1841  	return &fakeDevice{
  1842  		Stub:     &testing.Stub{},
  1843  		systemID: systemID,
  1844  		interface_: &fakeInterface{
  1845  			Stub:       &testing.Stub{},
  1846  			macAddress: macAddress,
  1847  		},
  1848  	}
  1849  }