github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/provisioner/provisioninginfo_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner_test
     5  
     6  import (
     7  	jc "github.com/juju/testing/checkers"
     8  	gc "gopkg.in/check.v1"
     9  
    10  	"github.com/juju/juju/apiserver/params"
    11  	"github.com/juju/juju/apiserver/provisioner"
    12  	apiservertesting "github.com/juju/juju/apiserver/testing"
    13  	"github.com/juju/juju/constraints"
    14  	"github.com/juju/juju/environs/tags"
    15  	"github.com/juju/juju/juju/testing"
    16  	"github.com/juju/juju/provider/dummy"
    17  	"github.com/juju/juju/state"
    18  	"github.com/juju/juju/state/multiwatcher"
    19  	"github.com/juju/juju/storage/poolmanager"
    20  	coretesting "github.com/juju/juju/testing"
    21  )
    22  
    23  func (s *withoutControllerSuite) TestProvisioningInfoWithStorage(c *gc.C) {
    24  	pm := poolmanager.New(state.NewStateSettings(s.State), dummy.StorageProviders())
    25  	_, err := pm.Create("static-pool", "static", map[string]interface{}{"foo": "bar"})
    26  	c.Assert(err, jc.ErrorIsNil)
    27  
    28  	cons := constraints.MustParse("cores=123 mem=8G")
    29  	template := state.MachineTemplate{
    30  		Series:      "quantal",
    31  		Jobs:        []state.MachineJob{state.JobHostUnits},
    32  		Constraints: cons,
    33  		Placement:   "valid",
    34  		Volumes: []state.MachineVolumeParams{
    35  			{Volume: state.VolumeParams{Size: 1000, Pool: "static-pool"}},
    36  			{Volume: state.VolumeParams{Size: 2000, Pool: "static-pool"}},
    37  		},
    38  	}
    39  	placementMachine, err := s.State.AddOneMachine(template)
    40  	c.Assert(err, jc.ErrorIsNil)
    41  
    42  	args := params.Entities{Entities: []params.Entity{
    43  		{Tag: s.machines[0].Tag().String()},
    44  		{Tag: placementMachine.Tag().String()},
    45  	}}
    46  	result, err := s.provisioner.ProvisioningInfo(args)
    47  	c.Assert(err, jc.ErrorIsNil)
    48  
    49  	controllerCfg := coretesting.FakeControllerConfig()
    50  	// Dummy provider uses a random port, which is added to cfg used to create environment.
    51  	apiPort := dummy.APIPort(s.Environ.Provider())
    52  	controllerCfg["api-port"] = apiPort
    53  	expected := params.ProvisioningInfoResults{
    54  		Results: []params.ProvisioningInfoResult{
    55  			{Result: &params.ProvisioningInfo{
    56  				ControllerConfig: controllerCfg,
    57  				Series:           "quantal",
    58  				Jobs:             []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
    59  				Tags: map[string]string{
    60  					tags.JujuController: coretesting.ControllerTag.Id(),
    61  					tags.JujuModel:      coretesting.ModelTag.Id(),
    62  				},
    63  			}},
    64  			{Result: &params.ProvisioningInfo{
    65  				ControllerConfig: controllerCfg,
    66  				Series:           "quantal",
    67  				Constraints:      template.Constraints,
    68  				Placement:        template.Placement,
    69  				Jobs:             []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
    70  				Tags: map[string]string{
    71  					tags.JujuController: coretesting.ControllerTag.Id(),
    72  					tags.JujuModel:      coretesting.ModelTag.Id(),
    73  				},
    74  				Volumes: []params.VolumeParams{{
    75  					VolumeTag:  "volume-0",
    76  					Size:       1000,
    77  					Provider:   "static",
    78  					Attributes: map[string]interface{}{"foo": "bar"},
    79  					Tags: map[string]string{
    80  						tags.JujuController: coretesting.ControllerTag.Id(),
    81  						tags.JujuModel:      coretesting.ModelTag.Id(),
    82  					},
    83  					Attachment: &params.VolumeAttachmentParams{
    84  						MachineTag: placementMachine.Tag().String(),
    85  						VolumeTag:  "volume-0",
    86  						Provider:   "static",
    87  					},
    88  				}, {
    89  					VolumeTag:  "volume-1",
    90  					Size:       2000,
    91  					Provider:   "static",
    92  					Attributes: map[string]interface{}{"foo": "bar"},
    93  					Tags: map[string]string{
    94  						tags.JujuController: coretesting.ControllerTag.Id(),
    95  						tags.JujuModel:      coretesting.ModelTag.Id(),
    96  					},
    97  					Attachment: &params.VolumeAttachmentParams{
    98  						MachineTag: placementMachine.Tag().String(),
    99  						VolumeTag:  "volume-1",
   100  						Provider:   "static",
   101  					},
   102  				}},
   103  			}},
   104  		},
   105  	}
   106  	// The order of volumes is not predictable, so we make sure we
   107  	// compare the right ones. This only applies to Results[1] since
   108  	// it is the only result to contain volumes.
   109  	if expected.Results[1].Result.Volumes[0].VolumeTag != result.Results[1].Result.Volumes[0].VolumeTag {
   110  		vols := expected.Results[1].Result.Volumes
   111  		vols[0], vols[1] = vols[1], vols[0]
   112  	}
   113  	c.Assert(result, jc.DeepEquals, expected)
   114  }
   115  
   116  func (s *withoutControllerSuite) TestProvisioningInfoWithSingleNegativeAndPositiveSpaceInConstraints(c *gc.C) {
   117  	s.addSpacesAndSubnets(c)
   118  
   119  	cons := constraints.MustParse("cores=123 mem=8G spaces=^space1,space2")
   120  	template := state.MachineTemplate{
   121  		Series:      "quantal",
   122  		Jobs:        []state.MachineJob{state.JobHostUnits},
   123  		Constraints: cons,
   124  		Placement:   "valid",
   125  	}
   126  	placementMachine, err := s.State.AddOneMachine(template)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  
   129  	args := params.Entities{Entities: []params.Entity{
   130  		{Tag: placementMachine.Tag().String()},
   131  	}}
   132  	result, err := s.provisioner.ProvisioningInfo(args)
   133  	c.Assert(err, jc.ErrorIsNil)
   134  
   135  	controllerCfg := coretesting.FakeControllerConfig()
   136  	// Dummy provider uses a random port, which is added to cfg used to create environment.
   137  	apiPort := dummy.APIPort(s.Environ.Provider())
   138  	controllerCfg["api-port"] = apiPort
   139  	expected := params.ProvisioningInfoResults{
   140  		Results: []params.ProvisioningInfoResult{{
   141  			Result: &params.ProvisioningInfo{
   142  				ControllerConfig: controllerCfg,
   143  				Series:           "quantal",
   144  				Constraints:      template.Constraints,
   145  				Placement:        template.Placement,
   146  				Jobs:             []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   147  				Tags: map[string]string{
   148  					tags.JujuController: coretesting.ControllerTag.Id(),
   149  					tags.JujuModel:      coretesting.ModelTag.Id(),
   150  				},
   151  				SubnetsToZones: map[string][]string{
   152  					"subnet-1": []string{"zone1"},
   153  					"subnet-2": []string{"zone2"},
   154  				},
   155  			},
   156  		}}}
   157  	c.Assert(result, jc.DeepEquals, expected)
   158  }
   159  
   160  func (s *withoutControllerSuite) addSpacesAndSubnets(c *gc.C) {
   161  	// Add a couple of spaces.
   162  	_, err := s.State.AddSpace("space1", "first space id", nil, true)
   163  	c.Assert(err, jc.ErrorIsNil)
   164  	_, err = s.State.AddSpace("space2", "", nil, false) // no provider ID
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	// Add 1 subnet into space1, and 2 into space2.
   167  	// Each subnet is in a matching zone (e.g "subnet-#" in "zone#").
   168  	testing.AddSubnetsWithTemplate(c, s.State, 3, state.SubnetInfo{
   169  		CIDR:             "10.10.{{.}}.0/24",
   170  		ProviderId:       "subnet-{{.}}",
   171  		AvailabilityZone: "zone{{.}}",
   172  		SpaceName:        "{{if (eq . 0)}}space1{{else}}space2{{end}}",
   173  		VLANTag:          42,
   174  	})
   175  }
   176  
   177  func (s *withoutControllerSuite) TestProvisioningInfoWithEndpointBindings(c *gc.C) {
   178  	s.addSpacesAndSubnets(c)
   179  
   180  	wordpressMachine, err := s.State.AddOneMachine(state.MachineTemplate{
   181  		Series: "quantal",
   182  		Jobs:   []state.MachineJob{state.JobHostUnits},
   183  	})
   184  	c.Assert(err, jc.ErrorIsNil)
   185  
   186  	// Use juju names for spaces in bindings, simulating ''juju deploy
   187  	// --bind...' was called.
   188  	bindings := map[string]string{
   189  		"url": "space1", // has both name and provider ID
   190  		"db":  "space2", // has only name, no provider ID
   191  	}
   192  	wordpressCharm := s.AddTestingCharm(c, "wordpress")
   193  	wordpressService := s.AddTestingServiceWithBindings(c, "wordpress", wordpressCharm, bindings)
   194  	wordpressUnit, err := wordpressService.AddUnit()
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	err = wordpressUnit.AssignToMachine(wordpressMachine)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  
   199  	args := params.Entities{Entities: []params.Entity{
   200  		{Tag: wordpressMachine.Tag().String()},
   201  	}}
   202  	result, err := s.provisioner.ProvisioningInfo(args)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  
   205  	controllerCfg := coretesting.FakeControllerConfig()
   206  	// Dummy provider uses a random port, which is added to cfg used to create environment.
   207  	apiPort := dummy.APIPort(s.Environ.Provider())
   208  	controllerCfg["api-port"] = apiPort
   209  	expected := params.ProvisioningInfoResults{
   210  		Results: []params.ProvisioningInfoResult{{
   211  			Result: &params.ProvisioningInfo{
   212  				ControllerConfig: controllerCfg,
   213  				Series:           "quantal",
   214  				Jobs:             []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   215  				Tags: map[string]string{
   216  					tags.JujuController:    coretesting.ControllerTag.Id(),
   217  					tags.JujuModel:         coretesting.ModelTag.Id(),
   218  					tags.JujuUnitsDeployed: wordpressUnit.Name(),
   219  				},
   220  				// Ensure space names are translated to provider IDs, where
   221  				// possible.
   222  				EndpointBindings: map[string]string{
   223  					"db":  "space2",         // just name, no provider ID
   224  					"url": "first space id", // has provider ID
   225  					// We expect none of the unspecified bindings in the result.
   226  				},
   227  			},
   228  		}}}
   229  	c.Assert(result, jc.DeepEquals, expected)
   230  }
   231  
   232  func (s *withoutControllerSuite) TestProvisioningInfoWithUnsuitableSpacesConstraints(c *gc.C) {
   233  	// Add an empty space.
   234  	_, err := s.State.AddSpace("empty", "", nil, true)
   235  	c.Assert(err, jc.ErrorIsNil)
   236  
   237  	consEmptySpace := constraints.MustParse("cores=123 mem=8G spaces=empty")
   238  	consMissingSpace := constraints.MustParse("cores=123 mem=8G spaces=missing")
   239  	templates := []state.MachineTemplate{{
   240  		Series:      "quantal",
   241  		Jobs:        []state.MachineJob{state.JobHostUnits},
   242  		Constraints: consEmptySpace,
   243  		Placement:   "valid",
   244  	}, {
   245  		Series:      "quantal",
   246  		Jobs:        []state.MachineJob{state.JobHostUnits},
   247  		Constraints: consMissingSpace,
   248  		Placement:   "valid",
   249  	}}
   250  	placementMachines, err := s.State.AddMachines(templates...)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	c.Assert(placementMachines, gc.HasLen, 2)
   253  
   254  	args := params.Entities{Entities: []params.Entity{
   255  		{Tag: placementMachines[0].Tag().String()},
   256  		{Tag: placementMachines[1].Tag().String()},
   257  	}}
   258  	result, err := s.provisioner.ProvisioningInfo(args)
   259  	c.Assert(err, jc.ErrorIsNil)
   260  
   261  	expectedErrorEmptySpace := `cannot match subnets to zones: ` +
   262  		`cannot use space "empty" as deployment target: no subnets`
   263  	expectedErrorMissingSpace := `cannot match subnets to zones: ` +
   264  		`space "missing"` // " not found" will be appended by NotFoundError helper below.
   265  	expected := params.ProvisioningInfoResults{Results: []params.ProvisioningInfoResult{
   266  		{Error: apiservertesting.ServerError(expectedErrorEmptySpace)},
   267  		{Error: apiservertesting.NotFoundError(expectedErrorMissingSpace)},
   268  	}}
   269  	c.Assert(result, jc.DeepEquals, expected)
   270  }
   271  
   272  func (s *withoutControllerSuite) TestStorageProviderFallbackToType(c *gc.C) {
   273  	template := state.MachineTemplate{
   274  		Series:    "quantal",
   275  		Jobs:      []state.MachineJob{state.JobHostUnits},
   276  		Placement: "valid",
   277  		Volumes: []state.MachineVolumeParams{
   278  			{Volume: state.VolumeParams{Size: 1000, Pool: "environscoped"}},
   279  			{Volume: state.VolumeParams{Size: 1000, Pool: "static"}},
   280  		},
   281  	}
   282  	placementMachine, err := s.State.AddOneMachine(template)
   283  	c.Assert(err, jc.ErrorIsNil)
   284  
   285  	args := params.Entities{Entities: []params.Entity{
   286  		{Tag: placementMachine.Tag().String()},
   287  	}}
   288  	result, err := s.provisioner.ProvisioningInfo(args)
   289  	c.Assert(err, jc.ErrorIsNil)
   290  
   291  	controllerCfg := coretesting.FakeControllerConfig()
   292  	// Dummy provider uses a random port, which is added to cfg used to create environment.
   293  	apiPort := dummy.APIPort(s.Environ.Provider())
   294  	controllerCfg["api-port"] = apiPort
   295  	c.Assert(result, jc.DeepEquals, params.ProvisioningInfoResults{
   296  		Results: []params.ProvisioningInfoResult{
   297  			{Result: &params.ProvisioningInfo{
   298  				ControllerConfig: controllerCfg,
   299  				Series:           "quantal",
   300  				Constraints:      template.Constraints,
   301  				Placement:        template.Placement,
   302  				Jobs:             []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   303  				Tags: map[string]string{
   304  					tags.JujuController: coretesting.ControllerTag.Id(),
   305  					tags.JujuModel:      coretesting.ModelTag.Id(),
   306  				},
   307  				Volumes: []params.VolumeParams{{
   308  					VolumeTag:  "volume-1",
   309  					Size:       1000,
   310  					Provider:   "static",
   311  					Attributes: nil,
   312  					Tags: map[string]string{
   313  						tags.JujuController: coretesting.ControllerTag.Id(),
   314  						tags.JujuModel:      coretesting.ModelTag.Id(),
   315  					},
   316  					Attachment: &params.VolumeAttachmentParams{
   317  						MachineTag: placementMachine.Tag().String(),
   318  						VolumeTag:  "volume-1",
   319  						Provider:   "static",
   320  					},
   321  				}},
   322  			}},
   323  		},
   324  	})
   325  }
   326  
   327  func (s *withoutControllerSuite) TestProvisioningInfoPermissions(c *gc.C) {
   328  	// Login as a machine agent for machine 0.
   329  	anAuthorizer := s.authorizer
   330  	anAuthorizer.EnvironManager = false
   331  	anAuthorizer.Tag = s.machines[0].Tag()
   332  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	c.Assert(aProvisioner, gc.NotNil)
   335  
   336  	args := params.Entities{Entities: []params.Entity{
   337  		{Tag: s.machines[0].Tag().String()},
   338  		{Tag: s.machines[0].Tag().String() + "-lxd-0"},
   339  		{Tag: "machine-42"},
   340  		{Tag: s.machines[1].Tag().String()},
   341  		{Tag: "application-bar"},
   342  	}}
   343  
   344  	// Only machine 0 and containers therein can be accessed.
   345  	results, err := aProvisioner.ProvisioningInfo(args)
   346  	controllerCfg := coretesting.FakeControllerConfig()
   347  	// Dummy provider uses a random port, which is added to cfg used to create environment.
   348  	apiPort := dummy.APIPort(s.Environ.Provider())
   349  	controllerCfg["api-port"] = apiPort
   350  	c.Assert(results, jc.DeepEquals, params.ProvisioningInfoResults{
   351  		Results: []params.ProvisioningInfoResult{
   352  			{Result: &params.ProvisioningInfo{
   353  				ControllerConfig: controllerCfg,
   354  				Series:           "quantal",
   355  				Jobs:             []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   356  				Tags: map[string]string{
   357  					tags.JujuController: coretesting.ControllerTag.Id(),
   358  					tags.JujuModel:      coretesting.ModelTag.Id(),
   359  				},
   360  			}},
   361  			{Error: apiservertesting.NotFoundError("machine 0/lxd/0")},
   362  			{Error: apiservertesting.ErrUnauthorized},
   363  			{Error: apiservertesting.ErrUnauthorized},
   364  			{Error: apiservertesting.ErrUnauthorized},
   365  		},
   366  	})
   367  }