github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/backups/restore_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package backups_test 5 6 import ( 7 "io" 8 "sort" 9 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 apibackups "github.com/juju/juju/api/backups" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/cloud" 17 "github.com/juju/juju/cmd/juju/backups" 18 "github.com/juju/juju/controller" 19 "github.com/juju/juju/environs" 20 "github.com/juju/juju/environs/bootstrap" 21 "github.com/juju/juju/instance" 22 "github.com/juju/juju/jujuclient" 23 "github.com/juju/juju/jujuclient/jujuclienttesting" 24 "github.com/juju/juju/network" 25 _ "github.com/juju/juju/provider/dummy" 26 _ "github.com/juju/juju/provider/lxd" 27 "github.com/juju/juju/testing" 28 "github.com/juju/juju/version" 29 ) 30 31 type restoreSuite struct { 32 BaseBackupsSuite 33 store *jujuclienttesting.MemStore 34 } 35 36 var _ = gc.Suite(&restoreSuite{}) 37 38 func (s *restoreSuite) SetUpTest(c *gc.C) { 39 s.BaseBackupsSuite.SetUpTest(c) 40 clouds := map[string]cloud.Cloud{ 41 "mycloud": { 42 Type: "openstack", 43 AuthTypes: []cloud.AuthType{"userpass", "access-key"}, 44 Endpoint: "http://homestack", 45 Regions: []cloud.Region{ 46 {Name: "a-region", Endpoint: "http://london/1.0"}, 47 }, 48 }, 49 } 50 err := cloud.WritePersonalCloudMetadata(clouds) 51 c.Assert(err, jc.ErrorIsNil) 52 53 s.store = jujuclienttesting.NewMemStore() 54 s.store.Controllers["testing"] = jujuclient.ControllerDetails{ 55 ControllerUUID: "deadbeef-0bad-400d-8000-5b1d0d06f00d", 56 CACert: testing.CACert, 57 Cloud: "mycloud", 58 CloudRegion: "a-region", 59 APIEndpoints: []string{"10.0.1.1:17777"}, 60 UnresolvedAPIEndpoints: []string{"10.0.1.1:17777"}, 61 } 62 s.store.CurrentControllerName = "testing" 63 s.store.Models["testing"] = &jujuclient.ControllerModels{ 64 Models: map[string]jujuclient.ModelDetails{ 65 "admin": {"test1-uuid"}, 66 }, 67 CurrentModel: "admin", 68 } 69 s.store.Accounts["testing"] = jujuclient.AccountDetails{ 70 User: "current-user@local", 71 Password: "old-password", 72 } 73 s.store.BootstrapConfig["testing"] = jujuclient.BootstrapConfig{ 74 Cloud: "mycloud", 75 CloudType: "dummy", 76 CloudRegion: "a-region", 77 Config: map[string]interface{}{ 78 "type": "dummy", 79 "name": "admin", 80 }, 81 ControllerModelUUID: testing.ModelTag.Id(), 82 ControllerConfig: controller.Config{ 83 "api-port": 17070, 84 "state-port": 37017, 85 }, 86 } 87 s.store.Credentials["dummy"] = cloud.CloudCredential{ 88 AuthCredentials: map[string]cloud.Credential{ 89 "me": cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 90 "username": "user", 91 "password": "sekret", 92 }), 93 }, 94 } 95 } 96 97 func (s *restoreSuite) TestRestoreArgs(c *gc.C) { 98 s.command = backups.NewRestoreCommandForTest(s.store, nil, nil, nil, nil) 99 _, err := testing.RunCommand(c, s.command, "restore") 100 c.Assert(err, gc.ErrorMatches, "you must specify either a file or a backup id.") 101 102 _, err = testing.RunCommand(c, s.command, "restore", "--id", "anid", "--file", "afile") 103 c.Assert(err, gc.ErrorMatches, "you must specify either a file or a backup id but not both.") 104 105 _, err = testing.RunCommand(c, s.command, "restore", "--id", "anid", "-b") 106 c.Assert(err, gc.ErrorMatches, "it is not possible to rebootstrap and restore from an id.") 107 } 108 109 // TODO(wallyworld) - add more api related unit tests 110 type mockRestoreAPI struct { 111 backups.RestoreAPI 112 } 113 114 func (*mockRestoreAPI) Close() error { 115 return nil 116 } 117 118 func (*mockRestoreAPI) RestoreReader(io.ReadSeeker, *params.BackupsMetadataResult, apibackups.ClientConnection) error { 119 return nil 120 } 121 122 type mockArchiveReader struct { 123 backups.ArchiveReader 124 } 125 126 func (*mockArchiveReader) Close() error { 127 return nil 128 } 129 130 func (s *restoreSuite) TestRestoreReboostrapControllerExists(c *gc.C) { 131 fakeEnv := fakeEnviron{controllerInstances: []instance.Id{"1"}} 132 s.command = backups.NewRestoreCommandForTest( 133 s.store, &mockRestoreAPI{}, 134 func(string) (backups.ArchiveReader, *params.BackupsMetadataResult, error) { 135 return &mockArchiveReader{}, ¶ms.BackupsMetadataResult{}, nil 136 }, 137 backups.GetEnvironFunc(fakeEnv), 138 backups.GetRebootstrapParamsFunc("mycloud"), 139 ) 140 _, err := testing.RunCommand(c, s.command, "restore", "--file", "afile", "-b") 141 c.Assert(err, gc.ErrorMatches, ".*still seems to exist.*") 142 } 143 144 func (s *restoreSuite) TestRestoreReboostrapNoControllers(c *gc.C) { 145 fakeEnv := fakeEnviron{} 146 s.command = backups.NewRestoreCommandForTest( 147 s.store, &mockRestoreAPI{}, 148 func(string) (backups.ArchiveReader, *params.BackupsMetadataResult, error) { 149 return &mockArchiveReader{}, ¶ms.BackupsMetadataResult{ 150 CACert: testing.CACert, 151 }, nil 152 }, 153 backups.GetEnvironFunc(fakeEnv), 154 backups.GetRebootstrapParamsFunc("mycloud"), 155 ) 156 s.PatchValue(&backups.BootstrapFunc, func(ctx environs.BootstrapContext, environ environs.Environ, args bootstrap.BootstrapParams) error { 157 return errors.New("failed to bootstrap new controller") 158 }) 159 160 _, err := testing.RunCommand(c, s.command, "restore", "--file", "afile", "-b") 161 c.Assert(err, gc.ErrorMatches, ".*failed to bootstrap new controller") 162 } 163 164 func (s *restoreSuite) TestRestoreReboostrapReadsMetadata(c *gc.C) { 165 metadata := params.BackupsMetadataResult{ 166 CACert: testing.CACert, 167 CAPrivateKey: testing.CAKey, 168 } 169 s.command = backups.NewRestoreCommandForTest( 170 s.store, &mockRestoreAPI{}, 171 func(string) (backups.ArchiveReader, *params.BackupsMetadataResult, error) { 172 return &mockArchiveReader{}, &metadata, nil 173 }, 174 backups.GetEnvironFunc(fakeEnviron{}), 175 backups.GetRebootstrapParamsFunc("mycloud"), 176 ) 177 s.PatchValue(&backups.BootstrapFunc, func(ctx environs.BootstrapContext, environ environs.Environ, args bootstrap.BootstrapParams) error { 178 return errors.New("failed to bootstrap new controller") 179 }) 180 181 _, err := testing.RunCommand(c, s.command, "restore", "-m", "testing:test1", "--file", "afile", "-b") 182 c.Assert(err, gc.ErrorMatches, ".*failed to bootstrap new controller") 183 } 184 185 func (s *restoreSuite) TestFailedRestoreReboostrapMaintainsControllerInfo(c *gc.C) { 186 metadata := params.BackupsMetadataResult{ 187 CACert: testing.CACert, 188 CAPrivateKey: testing.CAKey, 189 } 190 s.command = backups.NewRestoreCommandForTest( 191 s.store, &mockRestoreAPI{}, 192 func(string) (backups.ArchiveReader, *params.BackupsMetadataResult, error) { 193 return &mockArchiveReader{}, &metadata, nil 194 }, 195 nil, 196 backups.GetRebootstrapParamsFuncWithError(), 197 ) 198 s.PatchValue(&backups.BootstrapFunc, func(ctx environs.BootstrapContext, environ environs.Environ, args bootstrap.BootstrapParams) error { 199 // We should not call bootstrap. 200 c.Fail() 201 return nil 202 }) 203 204 _, err := testing.RunCommand(c, s.command, "restore", "-m", "testing:test1", "--file", "afile", "-b") 205 c.Assert(err, gc.ErrorMatches, "failed") 206 // The details below are as per what was done in test setup, so no changes. 207 c.Assert(s.store.Controllers["testing"], jc.DeepEquals, jujuclient.ControllerDetails{ 208 Cloud: "mycloud", 209 CloudRegion: "a-region", 210 CACert: testing.CACert, 211 ControllerUUID: "deadbeef-0bad-400d-8000-5b1d0d06f00d", 212 APIEndpoints: []string{"10.0.1.1:17777"}, 213 UnresolvedAPIEndpoints: []string{"10.0.1.1:17777"}, 214 }) 215 } 216 217 func (s *restoreSuite) TestRestoreReboostrapWritesUpdatedControllerInfo(c *gc.C) { 218 metadata := params.BackupsMetadataResult{ 219 CACert: testing.CACert, 220 CAPrivateKey: testing.CAKey, 221 } 222 fakeEnv := fakeEnviron{} 223 s.command = backups.NewRestoreCommandForTest( 224 s.store, &mockRestoreAPI{}, 225 func(string) (backups.ArchiveReader, *params.BackupsMetadataResult, error) { 226 return &mockArchiveReader{}, &metadata, nil 227 }, 228 backups.GetEnvironFunc(fakeEnv), 229 backups.GetRebootstrapParamsFunc("mycloud"), 230 ) 231 boostrapped := false 232 s.PatchValue(&backups.BootstrapFunc, func(ctx environs.BootstrapContext, environ environs.Environ, args bootstrap.BootstrapParams) error { 233 c.Assert(args.ControllerConfig, jc.DeepEquals, controller.Config{ 234 "controller-uuid": "deadbeef-1bad-500d-9000-4b1d0d06f00d", 235 "ca-cert": testing.CACert, 236 "state-port": 1234, 237 "api-port": 17777, 238 "set-numa-control-policy": false, 239 }) 240 boostrapped = true 241 return nil 242 }) 243 244 intPtr := func(i int) *int { 245 return &i 246 } 247 248 _, err := testing.RunCommand(c, s.command, "restore", "-m", "testing:test1", "--file", "afile", "-b") 249 c.Assert(err, jc.ErrorIsNil) 250 c.Assert(boostrapped, jc.IsTrue) 251 c.Assert(s.store.Controllers["testing"], jc.DeepEquals, jujuclient.ControllerDetails{ 252 Cloud: "mycloud", 253 CloudRegion: "a-region", 254 CACert: testing.CACert, 255 ControllerUUID: "deadbeef-1bad-500d-9000-4b1d0d06f00d", 256 APIEndpoints: []string{"10.0.0.1:17777"}, 257 UnresolvedAPIEndpoints: []string{"10.0.0.1:17777"}, 258 AgentVersion: version.Current.String(), 259 ModelCount: intPtr(2), 260 MachineCount: intPtr(1), 261 ControllerMachineCount: 1, 262 }) 263 } 264 265 func (s *restoreSuite) TestRestoreReboostrapBuiltInProvider(c *gc.C) { 266 metadata := params.BackupsMetadataResult{ 267 CACert: testing.CACert, 268 CAPrivateKey: testing.CAKey, 269 } 270 fakeEnv := fakeEnviron{} 271 s.command = backups.NewRestoreCommandForTest( 272 s.store, &mockRestoreAPI{}, 273 func(string) (backups.ArchiveReader, *params.BackupsMetadataResult, error) { 274 return &mockArchiveReader{}, &metadata, nil 275 }, 276 backups.GetEnvironFunc(fakeEnv), 277 backups.GetRebootstrapParamsFunc("lxd"), 278 ) 279 boostrapped := false 280 s.PatchValue(&backups.BootstrapFunc, func(ctx environs.BootstrapContext, environ environs.Environ, args bootstrap.BootstrapParams) error { 281 boostrapped = true 282 sort.Sort(args.Cloud.AuthTypes) 283 c.Assert(args.Cloud, jc.DeepEquals, cloud.Cloud{ 284 Type: "lxd", 285 AuthTypes: []cloud.AuthType{"empty"}, 286 Regions: []cloud.Region{{Name: "localhost"}}, 287 }) 288 return nil 289 }) 290 291 _, err := testing.RunCommand(c, s.command, "restore", "-m", "testing:test1", "--file", "afile", "-b") 292 c.Assert(err, jc.ErrorIsNil) 293 c.Assert(boostrapped, jc.IsTrue) 294 } 295 296 type fakeInstance struct { 297 instance.Instance 298 id instance.Id 299 } 300 301 func (f fakeInstance) Addresses() ([]network.Address, error) { 302 return []network.Address{ 303 {Value: "10.0.0.1"}, 304 }, nil 305 } 306 307 type fakeEnviron struct { 308 environs.Environ 309 controllerInstances []instance.Id 310 } 311 312 func (f fakeEnviron) ControllerInstances(_ string) ([]instance.Id, error) { 313 return f.controllerInstances, nil 314 } 315 316 func (f fakeEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) { 317 return []instance.Instance{fakeInstance{id: "1"}}, nil 318 } 319 320 func (f fakeEnviron) AllInstances() ([]instance.Instance, error) { 321 return []instance.Instance{fakeInstance{id: "1"}}, nil 322 }