github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/modeldestroy_test.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names/v5"
    11  	jtesting "github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/apiserver/common"
    16  	"github.com/juju/juju/apiserver/facades/agent/metricsender"
    17  	"github.com/juju/juju/core/status"
    18  	"github.com/juju/juju/state"
    19  	stateerrors "github.com/juju/juju/state/errors"
    20  	"github.com/juju/juju/testing"
    21  )
    22  
    23  type destroyModelSuite struct {
    24  	jtesting.IsolationSuite
    25  
    26  	modelManager *mockModelManager
    27  	metricSender *testMetricSender
    28  }
    29  
    30  var _ = gc.Suite(&destroyModelSuite{})
    31  
    32  func (s *destroyModelSuite) SetUpTest(c *gc.C) {
    33  	s.IsolationSuite.SetUpTest(c)
    34  
    35  	otherModelTag := names.NewModelTag("deadbeef-0bad-400d-8000-4b1d0d06f33d")
    36  	s.modelManager = &mockModelManager{
    37  		models: []*mockModel{
    38  			{tag: testing.ModelTag, currentStatus: status.StatusInfo{Status: status.Available}},
    39  			{tag: otherModelTag, currentStatus: status.StatusInfo{Status: status.Available}},
    40  		},
    41  	}
    42  	s.metricSender = &testMetricSender{}
    43  	s.PatchValue(common.SendMetrics, s.metricSender.SendMetrics)
    44  }
    45  
    46  func (s *destroyModelSuite) TestDestroyModelSendsMetrics(c *gc.C) {
    47  	err := common.DestroyModel(s.modelManager, nil, nil, nil, nil)
    48  	c.Assert(err, jc.ErrorIsNil)
    49  	s.metricSender.CheckCalls(c, []jtesting.StubCall{
    50  		{"SendMetrics", []interface{}{s.modelManager}},
    51  	})
    52  }
    53  
    54  func (s *destroyModelSuite) TestDestroyModel(c *gc.C) {
    55  	true_ := true
    56  	false_ := false
    57  	zero := time.Second * 0
    58  	one := time.Second
    59  	s.testDestroyModel(c, nil, nil, nil, nil)
    60  	s.testDestroyModel(c, nil, &false_, nil, nil)
    61  	s.testDestroyModel(c, nil, &false_, &zero, nil)
    62  	s.testDestroyModel(c, nil, &true_, nil, nil)
    63  	s.testDestroyModel(c, nil, &true_, &zero, nil)
    64  	s.testDestroyModel(c, &true_, nil, nil, nil)
    65  	s.testDestroyModel(c, &true_, &false_, nil, nil)
    66  	s.testDestroyModel(c, &true_, &false_, &zero, nil)
    67  	s.testDestroyModel(c, &true_, &true_, nil, nil)
    68  	s.testDestroyModel(c, &true_, &true_, &zero, nil)
    69  	s.testDestroyModel(c, &false_, nil, nil, nil)
    70  	s.testDestroyModel(c, &false_, &false_, nil, nil)
    71  	s.testDestroyModel(c, &false_, &false_, &zero, nil)
    72  	s.testDestroyModel(c, &false_, &true_, nil, nil)
    73  	s.testDestroyModel(c, &false_, &true_, &zero, nil)
    74  	s.testDestroyModel(c, &false_, &true_, &zero, &one)
    75  }
    76  
    77  func (s *destroyModelSuite) testDestroyModel(c *gc.C, destroyStorage, force *bool, maxWait, timeout *time.Duration) {
    78  	s.modelManager.ResetCalls()
    79  	s.modelManager.models[0].ResetCalls()
    80  
    81  	err := common.DestroyModel(s.modelManager, destroyStorage, force, maxWait, timeout)
    82  	c.Assert(err, jc.ErrorIsNil)
    83  
    84  	s.modelManager.CheckCalls(c, []jtesting.StubCall{
    85  		{"GetBlockForType", []interface{}{state.DestroyBlock}},
    86  		{"GetBlockForType", []interface{}{state.RemoveBlock}},
    87  		{"GetBlockForType", []interface{}{state.ChangeBlock}},
    88  		{"Model", nil},
    89  	})
    90  
    91  	expectedModelCalls := []jtesting.StubCall{{"Destroy", []interface{}{state.DestroyModelParams{
    92  		DestroyStorage: destroyStorage,
    93  		Force:          force,
    94  		MaxWait:        common.MaxWait(maxWait),
    95  		Timeout:        timeout,
    96  	}}}}
    97  	notForcing := force == nil || !*force
    98  	if notForcing {
    99  		// We expect to check model status.
   100  		expectedModelCalls = append([]jtesting.StubCall{{"Status", nil}}, expectedModelCalls...)
   101  	}
   102  	s.modelManager.models[0].CheckCalls(c, expectedModelCalls)
   103  }
   104  
   105  func (s *destroyModelSuite) TestDestroyModelBlocked(c *gc.C) {
   106  	s.modelManager.SetErrors(errors.New("nope"))
   107  
   108  	err := common.DestroyModel(s.modelManager, nil, nil, nil, nil)
   109  	c.Assert(err, gc.ErrorMatches, "nope")
   110  
   111  	s.modelManager.CheckCallNames(c, "GetBlockForType")
   112  	s.modelManager.models[0].CheckNoCalls(c)
   113  }
   114  
   115  func (s *destroyModelSuite) TestDestroyModelIgnoresErrorsWithForce(c *gc.C) {
   116  	s.modelManager.models[0].SetErrors(
   117  		errors.New("nope"),
   118  	)
   119  
   120  	true_ := true
   121  	err := common.DestroyModel(s.modelManager, nil, &true_, nil, nil)
   122  	c.Assert(err, jc.ErrorIsNil)
   123  
   124  	s.modelManager.CheckCallNames(c, "GetBlockForType", "GetBlockForType", "GetBlockForType", "Model")
   125  	s.modelManager.models[0].CheckCallNames(c, "Destroy")
   126  }
   127  
   128  func (s *destroyModelSuite) TestDestroyModelNotIgnoreErrorsrWithForce(c *gc.C) {
   129  	s.modelManager.models[0].SetErrors(
   130  		stateerrors.PersistentStorageError,
   131  	)
   132  	true_ := true
   133  	err := common.DestroyModel(s.modelManager, nil, &true_, nil, nil)
   134  	c.Assert(errors.Is(err, stateerrors.PersistentStorageError), jc.IsTrue)
   135  
   136  	s.modelManager.CheckCallNames(c, "GetBlockForType", "GetBlockForType", "GetBlockForType", "Model")
   137  	s.modelManager.models[0].CheckCallNames(c, "Destroy")
   138  }
   139  
   140  func (s *destroyModelSuite) TestDestroyControllerNonControllerModel(c *gc.C) {
   141  	s.modelManager.models[0].tag = s.modelManager.models[1].tag
   142  	err := common.DestroyController(s.modelManager, false, nil, nil, nil, nil)
   143  	c.Assert(err, gc.ErrorMatches, `expected state for controller model UUID deadbeef-0bad-400d-8000-4b1d0d06f33d, got deadbeef-0bad-400d-8000-4b1d0d06f00d`)
   144  }
   145  
   146  func (s *destroyModelSuite) TestDestroyController(c *gc.C) {
   147  	err := common.DestroyController(s.modelManager, false, nil, nil, nil, nil)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  
   150  	s.modelManager.CheckCalls(c, []jtesting.StubCall{
   151  		{"ControllerModelTag", nil},
   152  		{"GetBlockForType", []interface{}{state.DestroyBlock}},
   153  		{"GetBlockForType", []interface{}{state.RemoveBlock}},
   154  		{"GetBlockForType", []interface{}{state.ChangeBlock}},
   155  		{"Model", nil},
   156  	})
   157  	s.modelManager.models[0].CheckCalls(c, []jtesting.StubCall{
   158  		{"Status", nil},
   159  		{"Destroy", []interface{}{state.DestroyModelParams{
   160  			MaxWait: common.MaxWait(nil),
   161  		}}},
   162  	})
   163  }
   164  
   165  func (s *destroyModelSuite) TestDestroyControllerReleaseStorage(c *gc.C) {
   166  	destroyStorage := false
   167  	err := common.DestroyController(s.modelManager, false, &destroyStorage, nil, nil, nil)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  
   170  	s.modelManager.CheckCalls(c, []jtesting.StubCall{
   171  		{"ControllerModelTag", nil},
   172  		{"GetBlockForType", []interface{}{state.DestroyBlock}},
   173  		{"GetBlockForType", []interface{}{state.RemoveBlock}},
   174  		{"GetBlockForType", []interface{}{state.ChangeBlock}},
   175  		{"Model", nil},
   176  	})
   177  	s.modelManager.models[0].CheckCalls(c, []jtesting.StubCall{
   178  		{"Status", nil},
   179  		{"Destroy", []interface{}{state.DestroyModelParams{
   180  			DestroyStorage: &destroyStorage,
   181  			MaxWait:        common.MaxWait(nil),
   182  		}}},
   183  	})
   184  }
   185  
   186  func (s *destroyModelSuite) TestDestroyControllerForce(c *gc.C) {
   187  	force := true
   188  	timeout := time.Hour
   189  	maxWait := time.Second
   190  	err := common.DestroyController(s.modelManager, false, nil, &force, &maxWait, &timeout)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	s.modelManager.CheckCalls(c, []jtesting.StubCall{
   194  		{"ControllerModelTag", nil},
   195  		{"GetBlockForType", []interface{}{state.DestroyBlock}},
   196  		{"GetBlockForType", []interface{}{state.RemoveBlock}},
   197  		{"GetBlockForType", []interface{}{state.ChangeBlock}},
   198  		{"Model", nil},
   199  	})
   200  	s.modelManager.models[0].CheckCalls(c, []jtesting.StubCall{
   201  		{"Destroy", []interface{}{state.DestroyModelParams{
   202  			Force:   &force,
   203  			Timeout: &timeout,
   204  			MaxWait: maxWait,
   205  		}}},
   206  	})
   207  }
   208  
   209  func (s *destroyModelSuite) TestDestroyControllerDestroyHostedModels(c *gc.C) {
   210  	err := common.DestroyController(s.modelManager, true, nil, nil, nil, nil)
   211  	c.Assert(err, jc.ErrorIsNil)
   212  
   213  	s.modelManager.CheckCalls(c, []jtesting.StubCall{
   214  		{"ControllerModelTag", nil},
   215  		{"AllModelUUIDs", nil},
   216  
   217  		{"GetBackend", []interface{}{s.modelManager.models[0].tag.Id()}},
   218  		{"GetBlockForType", []interface{}{state.DestroyBlock}},
   219  		{"GetBlockForType", []interface{}{state.RemoveBlock}},
   220  		{"GetBlockForType", []interface{}{state.ChangeBlock}},
   221  
   222  		{"GetBackend", []interface{}{s.modelManager.models[1].tag.Id()}},
   223  		{"GetBlockForType", []interface{}{state.DestroyBlock}},
   224  		{"GetBlockForType", []interface{}{state.RemoveBlock}},
   225  		{"GetBlockForType", []interface{}{state.ChangeBlock}},
   226  
   227  		{"GetBlockForType", []interface{}{state.DestroyBlock}},
   228  		{"GetBlockForType", []interface{}{state.RemoveBlock}},
   229  		{"GetBlockForType", []interface{}{state.ChangeBlock}},
   230  		{"Model", nil},
   231  	})
   232  	s.modelManager.models[0].CheckCalls(c, []jtesting.StubCall{
   233  		{"Status", nil},
   234  		{"Destroy", []interface{}{state.DestroyModelParams{
   235  			DestroyHostedModels: true,
   236  			MaxWait:             common.MaxWait(nil),
   237  		}}},
   238  	})
   239  	s.metricSender.CheckCalls(c, []jtesting.StubCall{
   240  		// One call per hosted model, and one for the controller model.
   241  		{"SendMetrics", []interface{}{s.modelManager}},
   242  		{"SendMetrics", []interface{}{s.modelManager}},
   243  		{"SendMetrics", []interface{}{s.modelManager}},
   244  	})
   245  }
   246  
   247  func (s *destroyModelSuite) TestDestroyControllerModelErrs(c *gc.C) {
   248  	// This is similar to what we'd see if a model was destroyed
   249  	// but there are still some connections to it lingering.
   250  	s.modelManager.SetErrors(
   251  		nil, // for GetBackend, 1st model
   252  		nil, // for GetBlockForType, 1st model
   253  		nil, // for GetBlockForType, 1st model
   254  		nil, // for GetBlockForType, 1st model
   255  		errors.NotFoundf("pretend I am not here"), // for GetBackend, 2nd model
   256  	)
   257  	err := common.DestroyController(s.modelManager, true, nil, nil, nil, nil)
   258  	// Processing continued despite one model erring out.
   259  	c.Assert(err, jc.ErrorIsNil)
   260  
   261  	s.modelManager.SetErrors(
   262  		nil,                            // for GetBackend, 1st model
   263  		nil,                            // for GetBlockForType, 1st model
   264  		nil,                            // for GetBlockForType, 1st model
   265  		nil,                            // for GetBlockForType, 1st model
   266  		errors.New("I have a problem"), // for GetBackend, 2nd model
   267  	)
   268  	err = common.DestroyController(s.modelManager, true, nil, nil, nil, nil)
   269  	// Processing erred out since a model seriously failed.
   270  	c.Assert(err, gc.ErrorMatches, "I have a problem")
   271  }
   272  
   273  func (s *destroyModelSuite) TestDestroyModelWithInvalidCredentialWithoutForce(c *gc.C) {
   274  	s.modelManager.models[0].currentStatus = status.StatusInfo{Status: status.Suspended}
   275  
   276  	err := common.DestroyModel(s.modelManager, nil, nil, nil, nil)
   277  	c.Assert(err, gc.ErrorMatches, "invalid cloud credential, use --force")
   278  }
   279  
   280  func (s *destroyModelSuite) TestDestroyModelWithInvalidCredentialWithForce(c *gc.C) {
   281  	s.modelManager.models[0].currentStatus = status.StatusInfo{Status: status.Suspended}
   282  	true_ := true
   283  	s.testDestroyModel(c, nil, &true_, nil, nil)
   284  }
   285  
   286  type testMetricSender struct {
   287  	jtesting.Stub
   288  }
   289  
   290  func (t *testMetricSender) SendMetrics(st metricsender.ModelBackend) error {
   291  	t.MethodCall(t, "SendMetrics", st)
   292  	return t.NextErr()
   293  }
   294  
   295  type mockModelManager struct {
   296  	common.ModelManagerBackend
   297  	jtesting.Stub
   298  
   299  	models []*mockModel
   300  }
   301  
   302  func (m *mockModelManager) ControllerModelUUID() string {
   303  	m.MethodCall(m, "ControllerModelUUID")
   304  	return m.models[0].UUID()
   305  }
   306  
   307  func (m *mockModelManager) ControllerModelTag() names.ModelTag {
   308  	m.MethodCall(m, "ControllerModelTag")
   309  	return m.models[0].ModelTag()
   310  }
   311  
   312  func (m *mockModelManager) ModelTag() names.ModelTag {
   313  	return testing.ModelTag
   314  }
   315  
   316  func (m *mockModelManager) GetBlockForType(t state.BlockType) (state.Block, bool, error) {
   317  	m.MethodCall(m, "GetBlockForType", t)
   318  	return nil, false, m.NextErr()
   319  }
   320  
   321  func (m *mockModelManager) AllModelUUIDs() ([]string, error) {
   322  	m.MethodCall(m, "AllModelUUIDs")
   323  	var out []string
   324  	for _, model := range m.models {
   325  		out = append(out, model.UUID())
   326  	}
   327  	return out, nil
   328  }
   329  
   330  func (m *mockModelManager) Model() (common.Model, error) {
   331  	m.MethodCall(m, "Model")
   332  	return m.models[0], m.NextErr()
   333  }
   334  
   335  func (m *mockModelManager) GetBackend(uuid string) (common.ModelManagerBackend, func() bool, error) {
   336  	m.MethodCall(m, "GetBackend", uuid)
   337  	return m, func() bool { return true }, m.NextErr()
   338  }
   339  
   340  func (m *mockModelManager) Close() error {
   341  	m.MethodCall(m, "Close")
   342  	return m.NextErr()
   343  }
   344  
   345  type mockModel struct {
   346  	common.Model
   347  	jtesting.Stub
   348  	tag           names.ModelTag
   349  	currentStatus status.StatusInfo
   350  }
   351  
   352  func (m *mockModel) ModelTag() names.ModelTag {
   353  	return m.tag
   354  }
   355  
   356  func (m *mockModel) UUID() string {
   357  	return m.tag.Id()
   358  }
   359  
   360  func (m *mockModel) Destroy(args state.DestroyModelParams) error {
   361  	m.MethodCall(m, "Destroy", args)
   362  	return m.NextErr()
   363  }
   364  
   365  func (m *mockModel) Status() (status.StatusInfo, error) {
   366  	m.MethodCall(m, "Status")
   367  	return m.currentStatus, m.NextErr()
   368  }