github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "github.com/juju/cmd/cmdtesting" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/macaroon-bakery.v2-unstable/httpbakery" 16 "gopkg.in/macaroon.v2-unstable" 17 18 "github.com/juju/juju/api" 19 "github.com/juju/juju/api/base" 20 "github.com/juju/juju/api/controller" 21 apitesting "github.com/juju/juju/api/testing" 22 "github.com/juju/juju/cmd/modelcmd" 23 "github.com/juju/juju/core/model" 24 "github.com/juju/juju/jujuclient" 25 "github.com/juju/juju/testing" 26 ) 27 28 type MigrateSuite struct { 29 testing.FakeJujuXDGDataHomeSuite 30 api *fakeMigrateAPI 31 targetControllerAPI *fakeTargetControllerAPI 32 modelAPI *fakeModelAPI 33 store *jujuclient.MemStore 34 password string 35 } 36 37 var _ = gc.Suite(&MigrateSuite{}) 38 39 const modelUUID = "deadbeef-0bad-400d-8000-4b1d0d06f00d" 40 const targetControllerUUID = "beefdead-0bad-400d-8000-4b1d0d06f00d" 41 42 func (s *MigrateSuite) SetUpTest(c *gc.C) { 43 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 44 45 s.store = jujuclient.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: "sourceuser", 59 }) 60 c.Assert(err, jc.ErrorIsNil) 61 62 // Define the account for the target controller. 63 err = s.store.UpdateAccount("target", jujuclient.AccountDetails{ 64 User: "targetuser", 65 Password: "secret", 66 }) 67 c.Assert(err, jc.ErrorIsNil) 68 69 // Define the target controller in the config. 70 err = s.store.AddController("target", jujuclient.ControllerDetails{ 71 ControllerUUID: targetControllerUUID, 72 APIEndpoints: []string{"1.2.3.4:5"}, 73 CACert: "cert", 74 }) 75 c.Assert(err, jc.ErrorIsNil) 76 77 s.api = &fakeMigrateAPI{} 78 s.modelAPI = &fakeModelAPI{ 79 models: []base.UserModel{{ 80 Name: "model", 81 UUID: modelUUID, 82 Type: model.IAAS, 83 Owner: "sourceuser", 84 }, { 85 Name: "production", 86 UUID: "prod-1-uuid", 87 Type: model.IAAS, 88 Owner: "alpha", 89 }, { 90 Name: "production", 91 UUID: "prod-2-uuid", 92 Type: model.IAAS, 93 Owner: "sourceuser", 94 }}, 95 } 96 97 mac0, err := macaroon.New([]byte("secret0"), []byte("id0"), "location0") 98 c.Assert(err, jc.ErrorIsNil) 99 mac1, err := macaroon.New([]byte("secret1"), []byte("id1"), "location1") 100 c.Assert(err, jc.ErrorIsNil) 101 102 jar, err := s.store.CookieJar("target") 103 c.Assert(err, jc.ErrorIsNil) 104 105 s.targetControllerAPI = &fakeTargetControllerAPI{ 106 cookieURL: &url.URL{ 107 Scheme: "https", 108 Host: "testing.invalid", 109 Path: "/", 110 }, 111 macaroons: []macaroon.Slice{{mac0}}, 112 } 113 addCookie(c, jar, mac0, s.targetControllerAPI.cookieURL) 114 addCookie(c, jar, mac1, &url.URL{ 115 Scheme: "https", 116 Host: "tasting.invalid", 117 Path: "/", 118 }) 119 120 } 121 122 func addCookie(c *gc.C, jar http.CookieJar, mac *macaroon.Macaroon, url *url.URL) { 123 cookie, err := httpbakery.NewCookie(macaroon.Slice{mac}) 124 c.Assert(err, jc.ErrorIsNil) 125 cookie.Expires = time.Now().Add(time.Hour) // only persistent cookies are stored 126 jar.SetCookies(url, []*http.Cookie{cookie}) 127 } 128 129 func (s *MigrateSuite) TestMissingModel(c *gc.C) { 130 _, err := s.makeAndRun(c) 131 c.Assert(err, gc.ErrorMatches, "model not specified") 132 } 133 134 func (s *MigrateSuite) TestMissingTargetController(c *gc.C) { 135 _, err := s.makeAndRun(c, "mymodel") 136 c.Assert(err, gc.ErrorMatches, "target controller not specified") 137 } 138 139 func (s *MigrateSuite) TestTooManyArgs(c *gc.C) { 140 _, err := s.makeAndRun(c, "one", "too", "many") 141 c.Assert(err, gc.ErrorMatches, "too many arguments specified") 142 } 143 144 func (s *MigrateSuite) TestSuccess(c *gc.C) { 145 ctx, err := s.makeAndRun(c, "model", "target") 146 c.Assert(err, jc.ErrorIsNil) 147 148 c.Check(cmdtesting.Stderr(ctx), gc.Matches, "Migration started with ID \"uuid:0\"\n") 149 c.Check(s.api.specSeen, jc.DeepEquals, &controller.MigrationSpec{ 150 ModelUUID: modelUUID, 151 TargetControllerUUID: targetControllerUUID, 152 TargetAddrs: []string{"1.2.3.4:5"}, 153 TargetCACert: "cert", 154 TargetUser: "targetuser", 155 TargetPassword: "secret", 156 }) 157 } 158 159 func (s *MigrateSuite) TestSuccessMacaroons(c *gc.C) { 160 err := s.store.UpdateAccount("target", jujuclient.AccountDetails{ 161 User: "targetuser", 162 Password: "", 163 }) 164 c.Assert(err, jc.ErrorIsNil) 165 166 ctx, err := s.makeAndRun(c, "model", "target") 167 c.Assert(err, jc.ErrorIsNil) 168 169 c.Check(cmdtesting.Stderr(ctx), gc.Matches, "Migration started with ID \"uuid:0\"\n") 170 // Extract macaroons so we can compare them separately 171 // (as they can't be compared using DeepEquals due to 'UnmarshaledAs') 172 macs := s.api.specSeen.TargetMacaroons 173 s.api.specSeen.TargetMacaroons = nil 174 apitesting.MacaroonsEqual(c, macs, s.targetControllerAPI.macaroons) 175 c.Check(s.api.specSeen, jc.DeepEquals, &controller.MigrationSpec{ 176 ModelUUID: modelUUID, 177 TargetControllerUUID: targetControllerUUID, 178 TargetAddrs: []string{"1.2.3.4:5"}, 179 TargetCACert: "cert", 180 TargetUser: "targetuser", 181 }) 182 } 183 184 func (s *MigrateSuite) TestModelDoesntExist(c *gc.C) { 185 cmd := s.makeCommand() 186 _, err := cmdtesting.RunCommand(c, cmd, "wat", "target") 187 c.Check(err, gc.ErrorMatches, "model .+ not found") 188 c.Check(s.api.specSeen, gc.IsNil) // API shouldn't have been called 189 } 190 191 func (s *MigrateSuite) TestMultipleModelMatch(c *gc.C) { 192 cmd := s.makeCommand() 193 // Disambiguation is done in the standard way by choosing 194 // the current user's model. 195 ctx, err := cmdtesting.RunCommand(c, cmd, "production", "target") 196 c.Assert(err, jc.ErrorIsNil) 197 c.Check(cmdtesting.Stderr(ctx), gc.Matches, "Migration started with ID \"uuid:0\"\n") 198 c.Check(s.api.specSeen, jc.DeepEquals, &controller.MigrationSpec{ 199 ModelUUID: "prod-2-uuid", 200 TargetControllerUUID: targetControllerUUID, 201 TargetAddrs: []string{"1.2.3.4:5"}, 202 TargetCACert: "cert", 203 TargetUser: "targetuser", 204 TargetPassword: "secret", 205 }) 206 } 207 208 func (s *MigrateSuite) TestSpecifyOwner(c *gc.C) { 209 ctx, err := s.makeAndRun(c, "alpha/production", "target") 210 c.Assert(err, jc.ErrorIsNil) 211 212 c.Check(cmdtesting.Stderr(ctx), gc.Matches, "Migration started with ID \"uuid:0\"\n") 213 c.Check(s.api.specSeen.ModelUUID, gc.Equals, "prod-1-uuid") 214 } 215 216 func (s *MigrateSuite) TestControllerDoesntExist(c *gc.C) { 217 _, err := s.makeAndRun(c, "model", "wat") 218 c.Check(err, gc.ErrorMatches, "controller wat not found") 219 c.Check(s.api.specSeen, gc.IsNil) // API shouldn't have been called 220 } 221 222 func (s *MigrateSuite) makeAndRun(c *gc.C, args ...string) (*cmd.Context, error) { 223 return cmdtesting.RunCommand(c, s.makeCommand(), args...) 224 } 225 226 func (s *MigrateSuite) makeCommand() modelcmd.ModelCommand { 227 cmd := newMigrateCommand() 228 cmd.SetClientStore(s.store) 229 cmd.SetModelAPI(s.modelAPI) 230 inner := modelcmd.InnerCommand(cmd).(*migrateCommand) 231 inner.api = s.api 232 inner.newAPIRoot = func(jujuclient.ClientStore, string, string) (api.Connection, error) { 233 return s.targetControllerAPI, nil 234 } 235 return cmd 236 } 237 238 type fakeMigrateAPI struct { 239 specSeen *controller.MigrationSpec 240 } 241 242 func (a *fakeMigrateAPI) InitiateMigration(spec controller.MigrationSpec) (string, error) { 243 a.specSeen = &spec 244 return "uuid:0", nil 245 } 246 247 type fakeModelAPI struct { 248 models []base.UserModel 249 } 250 251 func (m *fakeModelAPI) ListModels(user string) ([]base.UserModel, error) { 252 return m.models, nil 253 } 254 255 func (m *fakeModelAPI) Close() error { 256 return nil 257 } 258 259 type fakeTargetControllerAPI struct { 260 api.Connection 261 cookieURL *url.URL 262 macaroons []macaroon.Slice 263 } 264 265 func (a *fakeTargetControllerAPI) CookieURL() *url.URL { 266 return a.cookieURL 267 } 268 269 func (a *fakeTargetControllerAPI) Close() error { 270 return nil 271 }