github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"fmt"
     8  	"path/filepath"
     9  
    10  	"github.com/golang/mock/gomock"
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/cmd/cmdtesting"
    13  	"github.com/juju/errors"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/names.v2"
    17  
    18  	"github.com/juju/juju/api/base"
    19  	"github.com/juju/juju/apiserver/params"
    20  	"github.com/juju/juju/cmd/juju/backups"
    21  	"github.com/juju/juju/core/model"
    22  	"github.com/juju/juju/core/status"
    23  	"github.com/juju/juju/jujuclient"
    24  	_ "github.com/juju/juju/provider/dummy"
    25  	_ "github.com/juju/juju/provider/lxd"
    26  	"github.com/juju/juju/testing"
    27  )
    28  
    29  type restoreSuite struct {
    30  	BaseBackupsSuite
    31  	wrappedCommand cmd.Command
    32  	command        *backups.RestoreCommand
    33  
    34  	store *jujuclient.MemStore
    35  }
    36  
    37  var _ = gc.Suite(&restoreSuite{})
    38  
    39  const (
    40  	controllerUUID      = "deadbeef-0bad-400d-8000-5b1d0d06f00d"
    41  	controllerModelUUID = "deadbeef-0bad-400d-8000-5b1d0d06f000"
    42  	test1ModelUUID      = "deadbeef-0bad-400d-8000-5b1d0d06f001"
    43  )
    44  
    45  func (s *restoreSuite) SetUpTest(c *gc.C) {
    46  	s.BaseBackupsSuite.SetUpTest(c)
    47  
    48  	controllerName := "test-master"
    49  	s.store = jujuclient.NewMemStore()
    50  	s.store.Controllers["testing"] = jujuclient.ControllerDetails{
    51  		ControllerUUID: controllerUUID,
    52  		CACert:         testing.CACert,
    53  		Cloud:          "mycloud",
    54  		CloudRegion:    "a-region",
    55  		APIEndpoints:   []string{"10.0.1.1:17777"},
    56  	}
    57  	s.store.CurrentControllerName = controllerName
    58  	s.store.Accounts[controllerName] = jujuclient.AccountDetails{
    59  		User: "bob",
    60  	}
    61  	s.store.Models[controllerName] = &jujuclient.ControllerModels{
    62  		Models: map[string]jujuclient.ModelDetails{
    63  			"bob/test1":      {ModelUUID: test1ModelUUID, ModelType: model.IAAS},
    64  			"bob/controller": {ModelUUID: controllerModelUUID, ModelType: model.IAAS},
    65  		},
    66  		CurrentModel: "controller",
    67  	}
    68  	s.wrappedCommand, s.command = backups.NewRestoreCommandForTest(s.store)
    69  }
    70  
    71  func (s *restoreSuite) patch(c *gc.C, archiveErr error) (*gomock.Controller, *MockAPIClient, *MockArchiveReader, *MockModelStatusAPI) {
    72  	ctrl := gomock.NewController(c)
    73  	apiClient := NewMockAPIClient(ctrl)
    74  	s.PatchValue(backups.NewAPIClient,
    75  		func(*backups.CommandBase) (backups.APIClient, error) {
    76  			return apiClient, nil
    77  		},
    78  	)
    79  	archiveClient := NewMockArchiveReader(ctrl)
    80  	s.PatchValue(backups.GetArchive,
    81  		func(string) (backups.ArchiveReader, *params.BackupsMetadataResult, error) {
    82  			return archiveClient, &params.BackupsMetadataResult{}, archiveErr
    83  		},
    84  	)
    85  	modelStatusClient := NewMockModelStatusAPI(ctrl)
    86  	s.command.AssignGetModelStatusAPI(
    87  		func() (backups.ModelStatusAPI, error) {
    88  			return modelStatusClient, nil
    89  		},
    90  	)
    91  
    92  	return ctrl, apiClient, archiveClient, modelStatusClient
    93  }
    94  
    95  // expectModelStatus is a convenience functions for the expectations
    96  // concerning successful ModelStatus based on a non HA config.
    97  func expectModelStatus(modelStatusClient *MockModelStatusAPI) {
    98  	controllerModelTag := names.NewModelTag(controllerModelUUID)
    99  	gomock.InOrder(
   100  		modelStatusClient.EXPECT().ModelStatus(controllerModelTag).Return(
   101  			[]base.ModelStatus{{
   102  				UUID: controllerModelUUID,
   103  				Machines: []base.Machine{
   104  					{HasVote: true, WantsVote: true, Status: string(status.Active)},
   105  				},
   106  			}}, nil,
   107  		),
   108  		modelStatusClient.EXPECT().Close(),
   109  	)
   110  }
   111  
   112  type restoreBackupArgParsing struct {
   113  	title    string
   114  	args     []string
   115  	errMatch string
   116  	id       string
   117  	filename string
   118  }
   119  
   120  var testRestoreBackupArgParsing = []restoreBackupArgParsing{
   121  	{
   122  		title:    "no args",
   123  		args:     []string{"--id", "anid", "--file", "afile"},
   124  		errMatch: "you must specify either a file or a backup id but not both.",
   125  	},
   126  	{
   127  		title:    "arg mismatch: id and file",
   128  		args:     []string{"--id", "anid", "--file", "afile"},
   129  		errMatch: "you must specify either a file or a backup id but not both.",
   130  	},
   131  	{
   132  		title: "id",
   133  		args:  []string{"--id", "anid"},
   134  		id:    "anid",
   135  	},
   136  	{
   137  		title:    "file",
   138  		args:     []string{"--file", "afile"},
   139  		filename: "afile",
   140  	},
   141  }
   142  
   143  func (s *restoreSuite) TestArgParsing(c *gc.C) {
   144  	for i, test := range testRestoreBackupArgParsing {
   145  		c.Logf("%d: %s", i, test.title)
   146  		err := cmdtesting.InitCommand(s.wrappedCommand, test.args)
   147  		if test.errMatch == "" {
   148  			c.Assert(err, jc.ErrorIsNil)
   149  			obtainedName := filepath.Base(s.command.Filename)
   150  			expectedName := filepath.Base(test.filename)
   151  			c.Assert(obtainedName, gc.Equals, expectedName)
   152  			c.Assert(s.command.BackupId, gc.Equals, test.id)
   153  		} else {
   154  			c.Assert(err, gc.ErrorMatches, test.errMatch)
   155  		}
   156  	}
   157  }
   158  
   159  func (s *restoreSuite) TestRestoreFromBackupFilename(c *gc.C) {
   160  	ctlr, apiClient, archiveReader, modelStatusClient := s.patch(c, nil)
   161  	defer ctlr.Finish()
   162  	expectModelStatus(modelStatusClient)
   163  	gomock.InOrder(
   164  		apiClient.EXPECT().RestoreReader(archiveReader, &params.BackupsMetadataResult{}, gomock.Any()).Return(
   165  			nil,
   166  		),
   167  		apiClient.EXPECT().Close(),
   168  		archiveReader.EXPECT().Close(),
   169  	)
   170  	ctx, err := cmdtesting.RunCommand(c, s.wrappedCommand, "restore", "--file", "afile")
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	out := fmt.Sprintf("restore from %q completed\n", s.command.Filename)
   173  	c.Check(cmdtesting.Stdout(ctx), gc.Equals, out)
   174  }
   175  
   176  func (s *restoreSuite) TestRestoreFromBackupFilenameFail(c *gc.C) {
   177  	ctlr, apiClient, archiveReader, modelStatusClient := s.patch(c, nil)
   178  	defer ctlr.Finish()
   179  	expectModelStatus(modelStatusClient)
   180  	gomock.InOrder(
   181  		apiClient.EXPECT().RestoreReader(archiveReader, &params.BackupsMetadataResult{}, gomock.Any()).Return(
   182  			errors.New("restore failed"),
   183  		),
   184  		apiClient.EXPECT().Close(),
   185  		archiveReader.EXPECT().Close(),
   186  	)
   187  	_, err := cmdtesting.RunCommand(c, s.wrappedCommand, "restore", "--file", "afile")
   188  	c.Assert(err, gc.ErrorMatches, "restore failed")
   189  }
   190  
   191  func (s *restoreSuite) TestRestoreFromBackupId(c *gc.C) {
   192  	ctlr, apiClient, _, modelStatusClient := s.patch(c, nil)
   193  	defer ctlr.Finish()
   194  	expectModelStatus(modelStatusClient)
   195  	gomock.InOrder(
   196  		apiClient.EXPECT().Restore("an_id", gomock.Any()).Return(
   197  			nil,
   198  		),
   199  		apiClient.EXPECT().Close(),
   200  	)
   201  	ctx, err := cmdtesting.RunCommand(c, s.wrappedCommand, "restore", "--id", "an_id")
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	out := fmt.Sprintf("restore from %q completed\n", s.command.BackupId)
   204  	c.Check(cmdtesting.Stdout(ctx), gc.Equals, out)
   205  }
   206  
   207  func (s *restoreSuite) TestRestoreFromBackupIdFail(c *gc.C) {
   208  	ctlr, apiClient, _, modelStatusClient := s.patch(c, nil)
   209  	defer ctlr.Finish()
   210  	expectModelStatus(modelStatusClient)
   211  	gomock.InOrder(
   212  		apiClient.EXPECT().Restore("an_id", gomock.Any()).Return(
   213  			errors.New("restore failed"),
   214  		),
   215  		apiClient.EXPECT().Close(),
   216  	)
   217  	_, err := cmdtesting.RunCommand(c, s.wrappedCommand, "restore", "--id", "an_id")
   218  	c.Assert(err, gc.ErrorMatches, "restore failed")
   219  }
   220  
   221  func (s *restoreSuite) TestRestoreFromBackupGetArchiveFail(c *gc.C) {
   222  	ctlr, _, _, modelStatusClient := s.patch(c, errors.New("get archive fail"))
   223  	defer ctlr.Finish()
   224  	expectModelStatus(modelStatusClient)
   225  	_, err := cmdtesting.RunCommand(c, s.wrappedCommand, "restore", "--file", "afile")
   226  	c.Assert(err, gc.ErrorMatches, "get archive fail")
   227  }
   228  
   229  func (s *restoreSuite) TestRestoreFromBackupGetModelStatusFail(c *gc.C) {
   230  	ctlr, _, _, modelStatusClient := s.patch(c, nil)
   231  	defer ctlr.Finish()
   232  	controllerModelTag := names.NewModelTag(controllerModelUUID)
   233  	gomock.InOrder(
   234  		modelStatusClient.EXPECT().ModelStatus(controllerModelTag).Return(
   235  			[]base.ModelStatus{{
   236  				UUID: "",
   237  				Machines: []base.Machine{
   238  					{},
   239  				},
   240  			}}, errors.New("get model status fail"),
   241  		),
   242  		modelStatusClient.EXPECT().Close(),
   243  	)
   244  	_, err := cmdtesting.RunCommand(c, s.wrappedCommand, "restore", "--file", "afile")
   245  	c.Assert(err, gc.ErrorMatches, "cannot refresh controller model: get model status fail")
   246  }
   247  
   248  func (s *restoreSuite) TestRestoreFromBackupHAFail(c *gc.C) {
   249  	ctlr, _, _, modelStatusClient := s.patch(c, nil)
   250  	defer ctlr.Finish()
   251  	controllerModelTag := names.NewModelTag(controllerModelUUID)
   252  	gomock.InOrder(
   253  		modelStatusClient.EXPECT().ModelStatus(controllerModelTag).Return(
   254  			[]base.ModelStatus{{
   255  				UUID: controllerModelUUID,
   256  				Machines: []base.Machine{
   257  					{HasVote: true, WantsVote: true, Status: string(status.Active)},
   258  					{HasVote: true, WantsVote: true, Status: string(status.Active)},
   259  					{HasVote: true, WantsVote: true, Status: string(status.Active)},
   260  				},
   261  			}}, nil,
   262  		),
   263  		modelStatusClient.EXPECT().Close(),
   264  	)
   265  	_, err := cmdtesting.RunCommand(c, s.wrappedCommand, "restore", "--id", "an_id")
   266  	c.Assert(err, gc.ErrorMatches, "unable to restore backup in HA configuration.  For help see https://docs.jujucharms.com/stable/controllers-backup")
   267  }