github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/commands/migrate_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package commands
     5  
     6  import (
     7  	"net/http"
     8  	"net/url"
     9  	"time"
    10  
    11  	"github.com/juju/cmd"
    12  	cookiejar "github.com/juju/persistent-cookiejar"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/macaroon-bakery.v1/httpbakery"
    16  	"gopkg.in/macaroon.v1"
    17  
    18  	"github.com/juju/juju/api"
    19  	"github.com/juju/juju/api/base"
    20  	"github.com/juju/juju/api/controller"
    21  	"github.com/juju/juju/cmd/modelcmd"
    22  	"github.com/juju/juju/feature"
    23  	"github.com/juju/juju/jujuclient"
    24  	"github.com/juju/juju/jujuclient/jujuclienttesting"
    25  	"github.com/juju/juju/testing"
    26  )
    27  
    28  type MigrateSuite struct {
    29  	testing.FakeJujuXDGDataHomeSuite
    30  	api                 *fakeMigrateAPI
    31  	targetControllerAPI *fakeTargetControllerAPI
    32  	store               *jujuclienttesting.MemStore
    33  	password            string
    34  }
    35  
    36  var _ = gc.Suite(&MigrateSuite{})
    37  
    38  const modelUUID = "deadbeef-0bad-400d-8000-4b1d0d06f00d"
    39  const targetControllerUUID = "beefdead-0bad-400d-8000-4b1d0d06f00d"
    40  
    41  func (s *MigrateSuite) SetUpTest(c *gc.C) {
    42  	s.SetInitialFeatureFlags(feature.Migration)
    43  	s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
    44  
    45  	s.store = jujuclienttesting.NewMemStore()
    46  
    47  	// Define the source controller in the config and set it as the default.
    48  	err := s.store.AddController("source", jujuclient.ControllerDetails{
    49  		ControllerUUID: "eeeeeeee-0bad-400d-8000-4b1d0d06f00d",
    50  		CACert:         "somecert",
    51  	})
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	err = s.store.SetCurrentController("source")
    54  	c.Assert(err, jc.ErrorIsNil)
    55  
    56  	// Define an account for the model in the source controller in the config.
    57  	err = s.store.UpdateAccount("source", jujuclient.AccountDetails{
    58  		User: "source@local",
    59  	})
    60  	c.Assert(err, jc.ErrorIsNil)
    61  
    62  	// Define the model to migrate in the config.
    63  	err = s.store.UpdateModel("source", "source@local/model", jujuclient.ModelDetails{
    64  		ModelUUID: modelUUID,
    65  	})
    66  	c.Assert(err, jc.ErrorIsNil)
    67  
    68  	// Define the account for the target controller.
    69  	err = s.store.UpdateAccount("target", jujuclient.AccountDetails{
    70  		User:     "target@local",
    71  		Password: "secret",
    72  	})
    73  	c.Assert(err, jc.ErrorIsNil)
    74  
    75  	// Define the target controller in the config.
    76  	err = s.store.AddController("target", jujuclient.ControllerDetails{
    77  		ControllerUUID: targetControllerUUID,
    78  		APIEndpoints:   []string{"1.2.3.4:5"},
    79  		CACert:         "cert",
    80  	})
    81  	c.Assert(err, jc.ErrorIsNil)
    82  
    83  	s.api = &fakeMigrateAPI{}
    84  
    85  	mac0, err := macaroon.New([]byte("secret0"), "id0", "location0")
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	mac1, err := macaroon.New([]byte("secret1"), "id1", "location1")
    88  	c.Assert(err, jc.ErrorIsNil)
    89  
    90  	jar, err := cookiejar.New(&cookiejar.Options{
    91  		Filename: cookiejar.DefaultCookieFile(),
    92  	})
    93  	c.Assert(err, jc.ErrorIsNil)
    94  
    95  	s.targetControllerAPI = &fakeTargetControllerAPI{
    96  		cookieURL: &url.URL{
    97  			Scheme: "https",
    98  			Host:   "testing.invalid",
    99  			Path:   "/",
   100  		},
   101  		macaroons: []macaroon.Slice{{mac0}},
   102  	}
   103  	addCookie(c, jar, mac0, s.targetControllerAPI.cookieURL)
   104  	addCookie(c, jar, mac1, &url.URL{
   105  		Scheme: "https",
   106  		Host:   "tasting.invalid",
   107  		Path:   "/",
   108  	})
   109  
   110  	err = jar.Save()
   111  	c.Assert(err, jc.ErrorIsNil)
   112  }
   113  
   114  func addCookie(c *gc.C, jar *cookiejar.Jar, mac *macaroon.Macaroon, url *url.URL) {
   115  	cookie, err := httpbakery.NewCookie(macaroon.Slice{mac})
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	cookie.Expires = time.Now().Add(time.Hour) // only persistent cookies are stored
   118  	jar.SetCookies(url, []*http.Cookie{cookie})
   119  }
   120  
   121  func (s *MigrateSuite) TestMissingModel(c *gc.C) {
   122  	_, err := s.makeAndRun(c)
   123  	c.Assert(err, gc.ErrorMatches, "model not specified")
   124  }
   125  
   126  func (s *MigrateSuite) TestMissingTargetController(c *gc.C) {
   127  	_, err := s.makeAndRun(c, "mymodel")
   128  	c.Assert(err, gc.ErrorMatches, "target controller not specified")
   129  }
   130  
   131  func (s *MigrateSuite) TestTooManyArgs(c *gc.C) {
   132  	_, err := s.makeAndRun(c, "one", "too", "many")
   133  	c.Assert(err, gc.ErrorMatches, "too many arguments specified")
   134  }
   135  
   136  func (s *MigrateSuite) TestSuccess(c *gc.C) {
   137  	ctx, err := s.makeAndRun(c, "model", "target")
   138  	c.Assert(err, jc.ErrorIsNil)
   139  
   140  	c.Check(testing.Stderr(ctx), gc.Matches, "Migration started with ID \"uuid:0\"\n")
   141  	c.Check(s.api.specSeen, jc.DeepEquals, &controller.MigrationSpec{
   142  		ModelUUID:            modelUUID,
   143  		TargetControllerUUID: targetControllerUUID,
   144  		TargetAddrs:          []string{"1.2.3.4:5"},
   145  		TargetCACert:         "cert",
   146  		TargetUser:           "target@local",
   147  		TargetPassword:       "secret",
   148  	})
   149  }
   150  
   151  func (s *MigrateSuite) TestSuccessMacaroons(c *gc.C) {
   152  	err := s.store.UpdateAccount("target", jujuclient.AccountDetails{
   153  		User:     "target@local",
   154  		Password: "",
   155  	})
   156  	c.Assert(err, jc.ErrorIsNil)
   157  
   158  	ctx, err := s.makeAndRun(c, "model", "target")
   159  	c.Assert(err, jc.ErrorIsNil)
   160  
   161  	c.Check(testing.Stderr(ctx), gc.Matches, "Migration started with ID \"uuid:0\"\n")
   162  	c.Check(s.api.specSeen, jc.DeepEquals, &controller.MigrationSpec{
   163  		ModelUUID:            modelUUID,
   164  		TargetControllerUUID: targetControllerUUID,
   165  		TargetAddrs:          []string{"1.2.3.4:5"},
   166  		TargetCACert:         "cert",
   167  		TargetUser:           "target@local",
   168  		TargetMacaroons:      s.targetControllerAPI.macaroons,
   169  	})
   170  }
   171  
   172  func (s *MigrateSuite) TestModelDoesntExist(c *gc.C) {
   173  	cmd := s.makeCommand()
   174  	cmd.SetModelAPI(&fakeModelAPI{})
   175  	_, err := s.run(c, cmd, "wat", "target")
   176  	c.Check(err, gc.ErrorMatches, "model .+ not found")
   177  	c.Check(s.api.specSeen, gc.IsNil) // API shouldn't have been called
   178  }
   179  
   180  func (s *MigrateSuite) TestModelDoesntExistBeforeRefresh(c *gc.C) {
   181  	cmd := s.makeCommand()
   182  	cmd.SetModelAPI(&fakeModelAPI{model: "wat"}) // Model is available after refresh
   183  	_, err := s.run(c, cmd, "wat", "target")
   184  	c.Check(err, jc.ErrorIsNil)
   185  	c.Check(s.api.specSeen, gc.NotNil)
   186  }
   187  
   188  func (s *MigrateSuite) TestControllerDoesntExist(c *gc.C) {
   189  	_, err := s.makeAndRun(c, "model", "wat")
   190  	c.Check(err, gc.ErrorMatches, "controller wat not found")
   191  	c.Check(s.api.specSeen, gc.IsNil) // API shouldn't have been called
   192  }
   193  
   194  func (s *MigrateSuite) makeAndRun(c *gc.C, args ...string) (*cmd.Context, error) {
   195  	return s.run(c, s.makeCommand(), args...)
   196  }
   197  
   198  func (s *MigrateSuite) makeCommand() *migrateCommand {
   199  	cmd := &migrateCommand{
   200  		api: s.api,
   201  		newAPIRoot: func(jujuclient.ClientStore, string, string) (api.Connection, error) {
   202  			return s.targetControllerAPI, nil
   203  		},
   204  	}
   205  	cmd.SetClientStore(s.store)
   206  	return cmd
   207  }
   208  
   209  func (s *MigrateSuite) run(c *gc.C, cmd *migrateCommand, args ...string) (*cmd.Context, error) {
   210  	return testing.RunCommand(c, modelcmd.WrapController(cmd), args...)
   211  }
   212  
   213  type fakeMigrateAPI struct {
   214  	specSeen *controller.MigrationSpec
   215  }
   216  
   217  func (a *fakeMigrateAPI) InitiateMigration(spec controller.MigrationSpec) (string, error) {
   218  	a.specSeen = &spec
   219  	return "uuid:0", nil
   220  }
   221  
   222  type fakeModelAPI struct {
   223  	model string
   224  }
   225  
   226  func (m *fakeModelAPI) ListModels(user string) ([]base.UserModel, error) {
   227  	if m.model == "" {
   228  		return []base.UserModel{}, nil
   229  	}
   230  	return []base.UserModel{{
   231  		Name:  m.model,
   232  		UUID:  modelUUID,
   233  		Owner: "source@local",
   234  	}}, nil
   235  }
   236  
   237  func (m *fakeModelAPI) Close() error {
   238  	return nil
   239  }
   240  
   241  type fakeTargetControllerAPI struct {
   242  	api.Connection
   243  	cookieURL *url.URL
   244  	macaroons []macaroon.Slice
   245  }
   246  
   247  func (a *fakeTargetControllerAPI) CookieURL() *url.URL {
   248  	return a.cookieURL
   249  }
   250  
   251  func (a *fakeTargetControllerAPI) Close() error {
   252  	return nil
   253  }