github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/controller/controller_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package controller_test
     5  
     6  import (
     7  	"encoding/json"
     8  
     9  	"github.com/juju/errors"
    10  	jujutesting "github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/names.v2"
    15  	"gopkg.in/macaroon.v2-unstable"
    16  
    17  	"github.com/juju/juju/api/base"
    18  	apitesting "github.com/juju/juju/api/base/testing"
    19  	"github.com/juju/juju/api/controller"
    20  	"github.com/juju/juju/apiserver/common"
    21  	"github.com/juju/juju/apiserver/params"
    22  	"github.com/juju/juju/environs"
    23  	coretesting "github.com/juju/juju/testing"
    24  )
    25  
    26  type Suite struct {
    27  	jujutesting.IsolationSuite
    28  }
    29  
    30  var _ = gc.Suite(&Suite{})
    31  
    32  func (s *Suite) TestDestroyControllerAPIVersion(c *gc.C) {
    33  	apiCaller := apitesting.BestVersionCaller{BestVersion: 3}
    34  	client := controller.NewClient(apiCaller)
    35  	for _, destroyStorage := range []*bool{nil, new(bool)} {
    36  		err := client.DestroyController(controller.DestroyControllerParams{
    37  			DestroyStorage: destroyStorage,
    38  		})
    39  		c.Assert(err, gc.ErrorMatches, "this Juju controller requires DestroyStorage to be true")
    40  	}
    41  
    42  }
    43  
    44  func (s *Suite) TestDestroyController(c *gc.C) {
    45  	var stub jujutesting.Stub
    46  	apiCaller := apitesting.BestVersionCaller{
    47  		BestVersion: 4,
    48  		APICallerFunc: func(objType string, version int, id, request string, arg, result interface{}) error {
    49  			stub.AddCall(objType+"."+request, arg)
    50  			return stub.NextErr()
    51  		},
    52  	}
    53  	client := controller.NewClient(apiCaller)
    54  
    55  	destroyStorage := true
    56  	err := client.DestroyController(controller.DestroyControllerParams{
    57  		DestroyModels:  true,
    58  		DestroyStorage: &destroyStorage,
    59  	})
    60  	c.Assert(err, jc.ErrorIsNil)
    61  
    62  	stub.CheckCalls(c, []jujutesting.StubCall{
    63  		{"Controller.DestroyController", []interface{}{params.DestroyControllerArgs{
    64  			DestroyModels:  true,
    65  			DestroyStorage: &destroyStorage,
    66  		}}},
    67  	})
    68  }
    69  
    70  func (s *Suite) TestDestroyControllerError(c *gc.C) {
    71  	apiCaller := apitesting.BestVersionCaller{
    72  		BestVersion: 4,
    73  		APICallerFunc: func(objType string, version int, id, request string, arg, result interface{}) error {
    74  			return errors.New("nope")
    75  		},
    76  	}
    77  	client := controller.NewClient(apiCaller)
    78  	err := client.DestroyController(controller.DestroyControllerParams{})
    79  	c.Assert(err, gc.ErrorMatches, "nope")
    80  }
    81  
    82  func (s *Suite) TestInitiateMigration(c *gc.C) {
    83  	s.checkInitiateMigration(c, makeSpec())
    84  }
    85  
    86  func (s *Suite) TestInitiateMigrationEmptyCACert(c *gc.C) {
    87  	spec := makeSpec()
    88  	spec.TargetCACert = ""
    89  	s.checkInitiateMigration(c, spec)
    90  }
    91  
    92  func (s *Suite) checkInitiateMigration(c *gc.C, spec controller.MigrationSpec) {
    93  	client, stub := makeInitiateMigrationClient(params.InitiateMigrationResults{
    94  		Results: []params.InitiateMigrationResult{{
    95  			MigrationId: "id",
    96  		}},
    97  	})
    98  	id, err := client.InitiateMigration(spec)
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	c.Check(id, gc.Equals, "id")
   101  	stub.CheckCalls(c, []jujutesting.StubCall{
   102  		{"Controller.InitiateMigration", []interface{}{specToArgs(spec)}},
   103  	})
   104  }
   105  
   106  func specToArgs(spec controller.MigrationSpec) params.InitiateMigrationArgs {
   107  	var macsJSON []byte
   108  	if len(spec.TargetMacaroons) > 0 {
   109  		var err error
   110  		macsJSON, err = json.Marshal(spec.TargetMacaroons)
   111  		if err != nil {
   112  			panic(err)
   113  		}
   114  	}
   115  	return params.InitiateMigrationArgs{
   116  		Specs: []params.MigrationSpec{{
   117  			ModelTag: names.NewModelTag(spec.ModelUUID).String(),
   118  			TargetInfo: params.MigrationTargetInfo{
   119  				ControllerTag: names.NewControllerTag(spec.TargetControllerUUID).String(),
   120  				Addrs:         spec.TargetAddrs,
   121  				CACert:        spec.TargetCACert,
   122  				AuthTag:       names.NewUserTag(spec.TargetUser).String(),
   123  				Password:      spec.TargetPassword,
   124  				Macaroons:     string(macsJSON),
   125  			},
   126  		}},
   127  	}
   128  }
   129  
   130  func (s *Suite) TestInitiateMigrationError(c *gc.C) {
   131  	client, _ := makeInitiateMigrationClient(params.InitiateMigrationResults{
   132  		Results: []params.InitiateMigrationResult{{
   133  			Error: common.ServerError(errors.New("boom")),
   134  		}},
   135  	})
   136  	id, err := client.InitiateMigration(makeSpec())
   137  	c.Check(id, gc.Equals, "")
   138  	c.Check(err, gc.ErrorMatches, "boom")
   139  }
   140  
   141  func (s *Suite) TestInitiateMigrationResultMismatch(c *gc.C) {
   142  	client, _ := makeInitiateMigrationClient(params.InitiateMigrationResults{
   143  		Results: []params.InitiateMigrationResult{
   144  			{MigrationId: "id"},
   145  			{MigrationId: "wtf"},
   146  		},
   147  	})
   148  	id, err := client.InitiateMigration(makeSpec())
   149  	c.Check(id, gc.Equals, "")
   150  	c.Check(err, gc.ErrorMatches, "unexpected number of results returned")
   151  }
   152  
   153  func (s *Suite) TestInitiateMigrationCallError(c *gc.C) {
   154  	apiCaller := apitesting.APICallerFunc(func(string, int, string, string, interface{}, interface{}) error {
   155  		return errors.New("boom")
   156  	})
   157  	client := controller.NewClient(apiCaller)
   158  	id, err := client.InitiateMigration(makeSpec())
   159  	c.Check(id, gc.Equals, "")
   160  	c.Check(err, gc.ErrorMatches, "boom")
   161  }
   162  
   163  func (s *Suite) TestInitiateMigrationValidationError(c *gc.C) {
   164  	client, stub := makeInitiateMigrationClient(params.InitiateMigrationResults{})
   165  	spec := makeSpec()
   166  	spec.ModelUUID = "not-a-uuid"
   167  	id, err := client.InitiateMigration(spec)
   168  	c.Check(id, gc.Equals, "")
   169  	c.Check(err, gc.ErrorMatches, "client-side validation failed: model UUID not valid")
   170  	c.Check(stub.Calls(), gc.HasLen, 0) // API call shouldn't have happened
   171  }
   172  
   173  func (s *Suite) TestHostedModelConfigs_CallError(c *gc.C) {
   174  	apiCaller := apitesting.APICallerFunc(func(string, int, string, string, interface{}, interface{}) error {
   175  		return errors.New("boom")
   176  	})
   177  	client := controller.NewClient(apiCaller)
   178  	config, err := client.HostedModelConfigs()
   179  	c.Check(config, gc.HasLen, 0)
   180  	c.Check(err, gc.ErrorMatches, "boom")
   181  }
   182  
   183  func (s *Suite) TestHostedModelConfigs_FormatResults(c *gc.C) {
   184  	apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   185  		c.Assert(objType, gc.Equals, "Controller")
   186  		c.Assert(request, gc.Equals, "HostedModelConfigs")
   187  		c.Assert(arg, gc.IsNil)
   188  		out := result.(*params.HostedModelConfigsResults)
   189  		c.Assert(out, gc.NotNil)
   190  		*out = params.HostedModelConfigsResults{
   191  			Models: []params.HostedModelConfig{
   192  				{
   193  					Name:     "first",
   194  					OwnerTag: "user-foo@bar",
   195  					Config: map[string]interface{}{
   196  						"name": "first",
   197  					},
   198  					CloudSpec: &params.CloudSpec{
   199  						Type: "magic",
   200  						Name: "first",
   201  					},
   202  				}, {
   203  					Name:     "second",
   204  					OwnerTag: "bad-tag",
   205  				}, {
   206  					Name:     "third",
   207  					OwnerTag: "user-foo@bar",
   208  					Config: map[string]interface{}{
   209  						"name": "third",
   210  					},
   211  					CloudSpec: &params.CloudSpec{
   212  						Name: "third",
   213  					},
   214  				},
   215  			},
   216  		}
   217  		return nil
   218  	})
   219  	client := controller.NewClient(apiCaller)
   220  	config, err := client.HostedModelConfigs()
   221  	c.Assert(config, gc.HasLen, 3)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	first := config[0]
   224  	c.Assert(first.Name, gc.Equals, "first")
   225  	c.Assert(first.Owner, gc.Equals, names.NewUserTag("foo@bar"))
   226  	c.Assert(first.Config, gc.DeepEquals, map[string]interface{}{
   227  		"name": "first",
   228  	})
   229  	c.Assert(first.CloudSpec, gc.DeepEquals, environs.CloudSpec{
   230  		Type: "magic",
   231  		Name: "first",
   232  	})
   233  	second := config[1]
   234  	c.Assert(second.Name, gc.Equals, "second")
   235  	c.Assert(second.Error.Error(), gc.Equals, `"bad-tag" is not a valid tag`)
   236  	third := config[2]
   237  	c.Assert(third.Name, gc.Equals, "third")
   238  	c.Assert(third.Error.Error(), gc.Equals, "validating CloudSpec: empty Type not valid")
   239  }
   240  
   241  func makeInitiateMigrationClient(results params.InitiateMigrationResults) (
   242  	*controller.Client, *jujutesting.Stub,
   243  ) {
   244  	var stub jujutesting.Stub
   245  	apiCaller := apitesting.APICallerFunc(
   246  		func(objType string, version int, id, request string, arg, result interface{}) error {
   247  			stub.AddCall(objType+"."+request, arg)
   248  			out := result.(*params.InitiateMigrationResults)
   249  			*out = results
   250  			return nil
   251  		},
   252  	)
   253  	client := controller.NewClient(apiCaller)
   254  	return client, &stub
   255  }
   256  
   257  func makeSpec() controller.MigrationSpec {
   258  	mac, err := macaroon.New([]byte("secret"), []byte("id"), "location")
   259  	if err != nil {
   260  		panic(err)
   261  	}
   262  	return controller.MigrationSpec{
   263  		ModelUUID:            randomUUID(),
   264  		TargetControllerUUID: randomUUID(),
   265  		TargetAddrs:          []string{"1.2.3.4:5"},
   266  		TargetCACert:         "cert",
   267  		TargetUser:           "someone",
   268  		TargetPassword:       "secret",
   269  		TargetMacaroons:      []macaroon.Slice{{mac}},
   270  	}
   271  }
   272  
   273  func randomUUID() string {
   274  	return utils.MustNewUUID().String()
   275  }
   276  
   277  func (s *Suite) TestModelStatusEmpty(c *gc.C) {
   278  	apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   279  		c.Check(objType, gc.Equals, "Controller")
   280  		c.Check(id, gc.Equals, "")
   281  		c.Check(request, gc.Equals, "ModelStatus")
   282  		c.Check(result, gc.FitsTypeOf, &params.ModelStatusResults{})
   283  
   284  		return nil
   285  	})
   286  
   287  	client := controller.NewClient(apiCaller)
   288  	results, err := client.ModelStatus()
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Assert(results, jc.DeepEquals, []base.ModelStatus{})
   291  }
   292  
   293  func (s *Suite) TestModelStatus(c *gc.C) {
   294  	apiCaller := apitesting.BestVersionCaller{
   295  		BestVersion: 4,
   296  		APICallerFunc: func(objType string, version int, id, request string, arg, result interface{}) error {
   297  			c.Check(objType, gc.Equals, "Controller")
   298  			c.Check(id, gc.Equals, "")
   299  			c.Check(request, gc.Equals, "ModelStatus")
   300  			c.Check(arg, jc.DeepEquals, params.Entities{
   301  				[]params.Entity{
   302  					{Tag: coretesting.ModelTag.String()},
   303  					{Tag: coretesting.ModelTag.String()},
   304  				},
   305  			})
   306  			c.Check(result, gc.FitsTypeOf, &params.ModelStatusResults{})
   307  
   308  			out := result.(*params.ModelStatusResults)
   309  			out.Results = []params.ModelStatus{
   310  				{
   311  					ModelTag:           coretesting.ModelTag.String(),
   312  					OwnerTag:           "user-glenda",
   313  					ApplicationCount:   3,
   314  					HostedMachineCount: 2,
   315  					Life:               "alive",
   316  					Machines: []params.ModelMachineInfo{{
   317  						Id:         "0",
   318  						InstanceId: "inst-ance",
   319  						Status:     "pending",
   320  					}},
   321  				},
   322  				{Error: common.ServerError(errors.New("model error"))},
   323  			}
   324  			return nil
   325  		},
   326  	}
   327  
   328  	client := controller.NewClient(apiCaller)
   329  	results, err := client.ModelStatus(coretesting.ModelTag, coretesting.ModelTag)
   330  	c.Assert(err, jc.ErrorIsNil)
   331  	c.Assert(results[0], jc.DeepEquals, base.ModelStatus{
   332  		UUID:               coretesting.ModelTag.Id(),
   333  		TotalMachineCount:  1,
   334  		HostedMachineCount: 2,
   335  		ApplicationCount:   3,
   336  		Owner:              "glenda",
   337  		Life:               string(params.Alive),
   338  		Machines:           []base.Machine{{Id: "0", InstanceId: "inst-ance", Status: "pending"}},
   339  	})
   340  	c.Assert(results[1].Error, gc.ErrorMatches, "model error")
   341  }
   342  
   343  func (s *Suite) TestModelStatusError(c *gc.C) {
   344  	apiCaller := apitesting.APICallerFunc(
   345  		func(objType string, version int, id, request string, args, result interface{}) error {
   346  			return errors.New("model error")
   347  		})
   348  	client := controller.NewClient(apiCaller)
   349  	out, err := client.ModelStatus(coretesting.ModelTag, coretesting.ModelTag)
   350  	c.Assert(err, gc.ErrorMatches, "model error")
   351  	c.Assert(out, gc.IsNil)
   352  }
   353  
   354  func (s *Suite) TestConfigSet(c *gc.C) {
   355  	apiCaller := apitesting.BestVersionCaller{
   356  		BestVersion: 5,
   357  		APICallerFunc: func(objType string, version int, id, request string, args, result interface{}) error {
   358  			c.Assert(objType, gc.Equals, "Controller")
   359  			c.Assert(version, gc.Equals, 5)
   360  			c.Assert(request, gc.Equals, "ConfigSet")
   361  			c.Assert(result, gc.IsNil)
   362  			c.Assert(args, gc.DeepEquals, params.ControllerConfigSet{Config: map[string]interface{}{
   363  				"some-setting": 345,
   364  			}})
   365  			return errors.New("ruth mundy")
   366  		},
   367  	}
   368  	client := controller.NewClient(apiCaller)
   369  	err := client.ConfigSet(map[string]interface{}{
   370  		"some-setting": 345,
   371  	})
   372  	c.Assert(err, gc.ErrorMatches, "ruth mundy")
   373  }
   374  
   375  func (s *Suite) TestConfigSetAgainstOlderAPIVersion(c *gc.C) {
   376  	apiCaller := apitesting.BestVersionCaller{BestVersion: 4}
   377  	client := controller.NewClient(apiCaller)
   378  	err := client.ConfigSet(map[string]interface{}{
   379  		"some-setting": 345,
   380  	})
   381  	c.Assert(err, gc.ErrorMatches, "this controller version doesn't support updating controller config")
   382  }