github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/initialize_test.go (about)

     1  // Copyright Canonical Ltd. 2013
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"github.com/juju/clock"
     8  	mgotesting "github.com/juju/mgo/v3/testing"
     9  	"github.com/juju/names/v5"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/cloud"
    14  	"github.com/juju/juju/controller"
    15  	"github.com/juju/juju/core/constraints"
    16  	"github.com/juju/juju/core/network"
    17  	"github.com/juju/juju/core/permission"
    18  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    19  	"github.com/juju/juju/environs/config"
    20  	"github.com/juju/juju/state"
    21  	statetesting "github.com/juju/juju/state/testing"
    22  	"github.com/juju/juju/storage"
    23  	"github.com/juju/juju/storage/poolmanager"
    24  	"github.com/juju/juju/storage/provider/dummy"
    25  	"github.com/juju/juju/testing"
    26  )
    27  
    28  type InitializeSuite struct {
    29  	mgotesting.MgoSuite
    30  	testing.BaseSuite
    31  	Pool  *state.StatePool
    32  	State *state.State
    33  	Model *state.Model
    34  }
    35  
    36  var _ = gc.Suite(&InitializeSuite{})
    37  
    38  func (s *InitializeSuite) SetUpSuite(c *gc.C) {
    39  	s.BaseSuite.SetUpSuite(c)
    40  	s.MgoSuite.SetUpSuite(c)
    41  }
    42  
    43  func (s *InitializeSuite) TearDownSuite(c *gc.C) {
    44  	s.MgoSuite.TearDownSuite(c)
    45  	s.BaseSuite.TearDownSuite(c)
    46  }
    47  
    48  func (s *InitializeSuite) SetUpTest(c *gc.C) {
    49  	s.BaseSuite.SetUpTest(c)
    50  	s.MgoSuite.SetUpTest(c)
    51  }
    52  
    53  func (s *InitializeSuite) openState(c *gc.C, modelTag names.ModelTag) {
    54  	pool, err := state.OpenStatePool(state.OpenParams{
    55  		Clock:              clock.WallClock,
    56  		ControllerTag:      testing.ControllerTag,
    57  		ControllerModelTag: modelTag,
    58  		MongoSession:       s.Session,
    59  	})
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	st, err := pool.SystemState()
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	s.Pool = pool
    64  	s.State = st
    65  
    66  	m, err := st.Model()
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	s.Model = m
    69  }
    70  
    71  func (s *InitializeSuite) TearDownTest(c *gc.C) {
    72  	if s.Pool != nil {
    73  		err := s.Pool.Close()
    74  		c.Check(err, jc.ErrorIsNil)
    75  	}
    76  	s.MgoSuite.TearDownTest(c)
    77  	s.BaseSuite.TearDownTest(c)
    78  }
    79  
    80  func (s *InitializeSuite) TestInitialize(c *gc.C) {
    81  	cfg := testing.ModelConfig(c)
    82  	uuid := cfg.UUID()
    83  	owner := names.NewLocalUserTag("initialize-admin")
    84  
    85  	userPassCredentialTag := names.NewCloudCredentialTag(
    86  		"dummy/" + owner.Id() + "/some-credential",
    87  	)
    88  	emptyCredentialTag := names.NewCloudCredentialTag(
    89  		"dummy/" + owner.Id() + "/empty-credential",
    90  	)
    91  	userpassCredential := cloud.NewCredential(
    92  		cloud.UserPassAuthType,
    93  		map[string]string{
    94  			"username": "alice",
    95  			"password": "hunter2",
    96  		},
    97  	)
    98  	userpassCredential.Label = userPassCredentialTag.Name()
    99  	expectedUserpassCredential := statetesting.CloudCredential(
   100  		cloud.UserPassAuthType,
   101  		map[string]string{
   102  			"username": "alice",
   103  			"password": "hunter2",
   104  		},
   105  	)
   106  	expectedUserpassCredential.DocID = "dummy#initialize-admin#some-credential"
   107  	expectedUserpassCredential.Owner = "initialize-admin"
   108  	expectedUserpassCredential.Cloud = "dummy"
   109  	expectedUserpassCredential.Name = "some-credential"
   110  
   111  	emptyCredential := cloud.NewEmptyCredential()
   112  	emptyCredential.Label = emptyCredentialTag.Name()
   113  	expectedEmptyCredential := statetesting.NewEmptyCredential()
   114  	expectedEmptyCredential.DocID = "dummy#initialize-admin#empty-credential"
   115  	expectedEmptyCredential.Owner = "initialize-admin"
   116  	expectedEmptyCredential.Cloud = "dummy"
   117  	expectedEmptyCredential.Name = "empty-credential"
   118  
   119  	cloudCredentialsIn := map[names.CloudCredentialTag]cloud.Credential{
   120  		userPassCredentialTag: userpassCredential,
   121  		emptyCredentialTag:    emptyCredential,
   122  	}
   123  	controllerCfg := testing.FakeControllerConfig()
   124  
   125  	ctlr, err := state.Initialize(state.InitializeParams{
   126  		Clock:            clock.WallClock,
   127  		ControllerConfig: controllerCfg,
   128  		ControllerModelArgs: state.ModelArgs{
   129  			Type:                    state.ModelTypeIAAS,
   130  			Owner:                   owner,
   131  			Config:                  cfg,
   132  			CloudName:               "dummy",
   133  			CloudRegion:             "dummy-region",
   134  			CloudCredential:         userPassCredentialTag,
   135  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   136  		},
   137  		Cloud: cloud.Cloud{
   138  			Name: "dummy",
   139  			Type: "dummy",
   140  			AuthTypes: []cloud.AuthType{
   141  				cloud.EmptyAuthType, cloud.UserPassAuthType,
   142  			},
   143  			Regions: []cloud.Region{{Name: "dummy-region"}},
   144  		},
   145  		CloudCredentials: cloudCredentialsIn,
   146  		MongoSession:     s.Session,
   147  		AdminPassword:    "dummy-secret",
   148  	})
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	c.Assert(ctlr, gc.NotNil)
   151  	st, err := ctlr.SystemState()
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	m, err := st.Model()
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	modelTag := m.ModelTag()
   156  	c.Assert(modelTag.Id(), gc.Equals, uuid)
   157  
   158  	err = ctlr.Close()
   159  	c.Assert(err, jc.ErrorIsNil)
   160  
   161  	s.openState(c, modelTag)
   162  
   163  	cfg, err = s.Model.ModelConfig()
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	expected := cfg.AllAttrs()
   166  	for k, v := range config.ConfigDefaults() {
   167  		if _, ok := expected[k]; !ok {
   168  			expected[k] = v
   169  		}
   170  	}
   171  	c.Assert(cfg.AllAttrs(), jc.DeepEquals, expected)
   172  	// Check that the model has been created.
   173  	model, err := s.State.Model()
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	c.Assert(model.Tag(), gc.Equals, modelTag)
   176  	c.Assert(model.CloudRegion(), gc.Equals, "dummy-region")
   177  	// Check that the owner has been created.
   178  	c.Assert(model.Owner(), gc.Equals, owner)
   179  	// Check that the owner can be retrieved by the tag.
   180  	entity, err := s.State.FindEntity(model.Owner())
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	c.Assert(entity.Tag(), gc.Equals, owner)
   183  	// Check that the owner has an ModelUser created for the bootstrapped model.
   184  	modelUser, err := s.State.UserAccess(model.Owner(), model.Tag())
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	c.Assert(modelUser.UserTag, gc.Equals, owner)
   187  	c.Assert(modelUser.Object, gc.Equals, model.Tag())
   188  
   189  	// Check that the model can be found through the tag.
   190  	entity, err = s.State.FindEntity(modelTag)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	cons, err := s.State.ModelConstraints()
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	c.Assert(&cons, jc.Satisfies, constraints.IsEmpty)
   195  
   196  	addrs, err := s.State.APIHostPortsForClients()
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Assert(addrs, gc.HasLen, 0)
   199  
   200  	info, err := s.State.ControllerInfo()
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	c.Assert(info, jc.DeepEquals, &state.ControllerInfo{ModelTag: modelTag, CloudName: "dummy"})
   203  
   204  	// Check that the model's cloud and credential names are as
   205  	// expected, and the owner's cloud credentials are initialised.
   206  	c.Assert(model.CloudName(), gc.Equals, "dummy")
   207  	credentialTag, ok := model.CloudCredentialTag()
   208  	c.Assert(ok, jc.IsTrue)
   209  	c.Assert(credentialTag, gc.Equals, userPassCredentialTag)
   210  	cred, credentialSet, err := model.CloudCredential()
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	c.Assert(credentialSet, jc.IsTrue)
   213  	stateCred, err := s.State.CloudCredential(credentialTag)
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	c.Assert(cred, jc.DeepEquals, stateCred)
   216  	cloudCredentials, err := s.State.CloudCredentials(model.Owner(), "dummy")
   217  	c.Assert(err, jc.ErrorIsNil)
   218  	c.Assert(cloudCredentials, jc.DeepEquals, map[string]state.Credential{
   219  		"dummy/initialize-admin/some-credential":  expectedUserpassCredential,
   220  		"dummy/initialize-admin/empty-credential": expectedEmptyCredential,
   221  	})
   222  
   223  	// Check that the cloud owner has admin access.
   224  	access, err := s.State.GetCloudAccess("dummy", owner)
   225  	c.Assert(err, jc.ErrorIsNil)
   226  	c.Assert(access, gc.Equals, permission.AdminAccess)
   227  
   228  	// Check that the cloud's model count is initially 1.
   229  	cl, err := s.State.Cloud("dummy")
   230  	c.Assert(err, jc.ErrorIsNil)
   231  
   232  	refCount, err := state.CloudModelRefCount(s.State, cl.Name)
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Assert(refCount, gc.Equals, 1)
   235  
   236  	// Check that the alpha space is created.
   237  	_, err = s.State.SpaceByName(network.AlphaSpaceName)
   238  	c.Assert(err, jc.ErrorIsNil)
   239  
   240  	// Check that the bakery config is created.
   241  	bakeryConfig := s.State.NewBakeryConfig()
   242  	_, err = bakeryConfig.GetLocalUsersKey()
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	_, err = bakeryConfig.GetLocalUsersThirdPartyKey()
   245  	c.Assert(err, jc.ErrorIsNil)
   246  	_, err = bakeryConfig.GetExternalUsersThirdPartyKey()
   247  	c.Assert(err, jc.ErrorIsNil)
   248  	_, err = bakeryConfig.GetOffersThirdPartyKey()
   249  	c.Assert(err, jc.ErrorIsNil)
   250  }
   251  
   252  func (s *InitializeSuite) TestInitializeWithInvalidCredentialType(c *gc.C) {
   253  	owner := names.NewLocalUserTag("initialize-admin")
   254  	modelCfg := testing.ModelConfig(c)
   255  	controllerCfg := testing.FakeControllerConfig()
   256  	credentialTag := names.NewCloudCredentialTag("dummy/" + owner.Id() + "/borken")
   257  	_, err := state.Initialize(state.InitializeParams{
   258  		Clock:            clock.WallClock,
   259  		ControllerConfig: controllerCfg,
   260  		ControllerModelArgs: state.ModelArgs{
   261  			Type:                    state.ModelTypeIAAS,
   262  			CloudName:               "dummy",
   263  			Owner:                   owner,
   264  			Config:                  modelCfg,
   265  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   266  		},
   267  		Cloud: cloud.Cloud{
   268  			Name: "dummy",
   269  			Type: "dummy",
   270  			AuthTypes: []cloud.AuthType{
   271  				cloud.AccessKeyAuthType, cloud.OAuth1AuthType,
   272  			},
   273  		},
   274  		CloudCredentials: map[names.CloudCredentialTag]cloud.Credential{
   275  			credentialTag: cloud.NewCredential(cloud.UserPassAuthType, nil),
   276  		},
   277  		MongoSession:  s.Session,
   278  		AdminPassword: "dummy-secret",
   279  	})
   280  	c.Assert(err, gc.ErrorMatches,
   281  		`validating initialization args: validating credential "dummy/initialize-admin/borken" for cloud "dummy": supported auth-types \["access-key" "oauth1"\], "userpass" not supported`,
   282  	)
   283  }
   284  
   285  func (s *InitializeSuite) TestInitializeWithControllerInheritedConfig(c *gc.C) {
   286  	cfg := testing.ModelConfig(c)
   287  	uuid := cfg.UUID()
   288  	initial := cfg.AllAttrs()
   289  	controllerInheritedConfigIn := map[string]interface{}{
   290  		"charmhub-url": initial["charmhub-url"],
   291  	}
   292  	owner := names.NewLocalUserTag("initialize-admin")
   293  	controllerCfg := testing.FakeControllerConfig()
   294  
   295  	ctlr, err := state.Initialize(state.InitializeParams{
   296  		Clock:            clock.WallClock,
   297  		ControllerConfig: controllerCfg,
   298  		ControllerModelArgs: state.ModelArgs{
   299  			Type:                    state.ModelTypeIAAS,
   300  			CloudName:               "dummy",
   301  			Owner:                   owner,
   302  			Config:                  cfg,
   303  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   304  		},
   305  		Cloud: cloud.Cloud{
   306  			Name:      "dummy",
   307  			Type:      "dummy",
   308  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType},
   309  		},
   310  		ControllerInheritedConfig: controllerInheritedConfigIn,
   311  		MongoSession:              s.Session,
   312  		AdminPassword:             "dummy-secret",
   313  	})
   314  	c.Assert(err, jc.ErrorIsNil)
   315  	c.Assert(ctlr, gc.NotNil)
   316  	st, err := ctlr.SystemState()
   317  	c.Assert(err, jc.ErrorIsNil)
   318  	m, err := st.Model()
   319  	c.Assert(err, jc.ErrorIsNil)
   320  	modelTag := m.ModelTag()
   321  	c.Assert(modelTag.Id(), gc.Equals, uuid)
   322  
   323  	err = ctlr.Close()
   324  	c.Assert(err, jc.ErrorIsNil)
   325  
   326  	s.openState(c, modelTag)
   327  
   328  	controllerInheritedConfig, err := s.State.ReadSettings(state.GlobalSettingsC, state.CloudGlobalKey("dummy"))
   329  	c.Assert(err, jc.ErrorIsNil)
   330  	c.Assert(controllerInheritedConfig.Map(), jc.DeepEquals, controllerInheritedConfigIn)
   331  
   332  	expected := cfg.AllAttrs()
   333  	for k, v := range config.ConfigDefaults() {
   334  		if _, ok := expected[k]; !ok {
   335  			expected[k] = v
   336  		}
   337  	}
   338  	// Config as read from state has resources tags coerced to a map.
   339  	expected["resource-tags"] = map[string]string{}
   340  	cfg, err = s.Model.ModelConfig()
   341  	c.Assert(err, jc.ErrorIsNil)
   342  	c.Assert(cfg.AllAttrs(), jc.DeepEquals, expected)
   343  }
   344  
   345  func (s *InitializeSuite) TestDoubleInitializeConfig(c *gc.C) {
   346  	cfg := testing.ModelConfig(c)
   347  	owner := names.NewLocalUserTag("initialize-admin")
   348  
   349  	controllerCfg := testing.FakeControllerConfig()
   350  
   351  	args := state.InitializeParams{
   352  		Clock:            clock.WallClock,
   353  		ControllerConfig: controllerCfg,
   354  		ControllerModelArgs: state.ModelArgs{
   355  			Type:                    state.ModelTypeIAAS,
   356  			CloudName:               "dummy",
   357  			Owner:                   owner,
   358  			Config:                  cfg,
   359  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   360  		},
   361  		Cloud: cloud.Cloud{
   362  			Name:      "dummy",
   363  			Type:      "dummy",
   364  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType},
   365  		},
   366  		MongoSession:  s.Session,
   367  		AdminPassword: "dummy-secret",
   368  	}
   369  	ctlr, err := state.Initialize(args)
   370  	c.Assert(err, jc.ErrorIsNil)
   371  	err = ctlr.Close()
   372  	c.Check(err, jc.ErrorIsNil)
   373  
   374  	ctlr, err = state.Initialize(args)
   375  	c.Check(err, gc.ErrorMatches, "already initialized")
   376  	c.Check(ctlr, gc.IsNil)
   377  }
   378  
   379  func (s *InitializeSuite) TestModelConfigWithAdminSecret(c *gc.C) {
   380  	update := map[string]interface{}{"admin-secret": "foo"}
   381  	remove := []string{}
   382  	s.testBadModelConfig(c, update, remove, "admin-secret should never be written to the state")
   383  }
   384  
   385  func (s *InitializeSuite) TestModelConfigWithCAPrivateKey(c *gc.C) {
   386  	update := map[string]interface{}{"ca-private-key": "foo"}
   387  	remove := []string{}
   388  	s.testBadModelConfig(c, update, remove, "ca-private-key should never be written to the state")
   389  }
   390  
   391  func (s *InitializeSuite) TestModelConfigWithoutAgentVersion(c *gc.C) {
   392  	update := map[string]interface{}{}
   393  	remove := []string{"agent-version"}
   394  	s.testBadModelConfig(c, update, remove, "agent-version must always be set in state")
   395  }
   396  
   397  func (s *InitializeSuite) testBadModelConfig(c *gc.C, update map[string]interface{}, remove []string, expect string) {
   398  	good := testing.CustomModelConfig(c, testing.Attrs{"uuid": testing.ModelTag.Id()})
   399  	bad, err := good.Apply(update)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	bad, err = bad.Remove(remove)
   402  	c.Assert(err, jc.ErrorIsNil)
   403  
   404  	owner := names.NewLocalUserTag("initialize-admin")
   405  	controllerCfg := testing.FakeControllerConfig()
   406  
   407  	args := state.InitializeParams{
   408  		Clock:            clock.WallClock,
   409  		ControllerConfig: controllerCfg,
   410  		ControllerModelArgs: state.ModelArgs{
   411  			Type:                    state.ModelTypeIAAS,
   412  			CloudName:               "dummy",
   413  			CloudRegion:             "dummy-region",
   414  			Owner:                   owner,
   415  			Config:                  bad,
   416  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   417  		},
   418  		Cloud: cloud.Cloud{
   419  			Name:      "dummy",
   420  			Type:      "dummy",
   421  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType},
   422  			Regions:   []cloud.Region{{Name: "dummy-region"}},
   423  		},
   424  		MongoSession:  s.Session,
   425  		AdminPassword: "dummy-secret",
   426  	}
   427  	_, err = state.Initialize(args)
   428  	c.Assert(err, gc.ErrorMatches, expect)
   429  
   430  	args.ControllerModelArgs.Config = good
   431  	ctlr, err := state.Initialize(args)
   432  	c.Assert(err, jc.ErrorIsNil)
   433  	sysState, err := ctlr.SystemState()
   434  	c.Assert(err, jc.ErrorIsNil)
   435  	modelUUID := sysState.ModelUUID()
   436  	ctlr.Close()
   437  
   438  	s.openState(c, names.NewModelTag(modelUUID))
   439  	m, err := s.State.Model()
   440  	c.Assert(err, jc.ErrorIsNil)
   441  
   442  	err = m.UpdateModelConfig(update, remove)
   443  	c.Assert(err, gc.ErrorMatches, expect)
   444  
   445  	// ModelConfig remains inviolate.
   446  	cfg, err := s.Model.ModelConfig()
   447  	c.Assert(err, jc.ErrorIsNil)
   448  	goodWithDefaults, err := config.New(config.UseDefaults, good.AllAttrs())
   449  	c.Assert(err, jc.ErrorIsNil)
   450  	c.Assert(cfg.AllAttrs(), jc.DeepEquals, goodWithDefaults.AllAttrs())
   451  }
   452  
   453  func (s *InitializeSuite) TestCloudConfigWithForbiddenValues(c *gc.C) {
   454  	badAttrNames := []string{
   455  		"admin-secret",
   456  		"ca-private-key",
   457  		config.AgentVersionKey,
   458  	}
   459  	for _, attr := range controller.ControllerOnlyConfigAttributes {
   460  		badAttrNames = append(badAttrNames, attr)
   461  	}
   462  
   463  	modelCfg := testing.ModelConfig(c)
   464  	controllerCfg := testing.FakeControllerConfig()
   465  	args := state.InitializeParams{
   466  		Clock:            clock.WallClock,
   467  		ControllerConfig: controllerCfg,
   468  		ControllerModelArgs: state.ModelArgs{
   469  			Type:                    state.ModelTypeIAAS,
   470  			CloudName:               "dummy",
   471  			Owner:                   names.NewLocalUserTag("initialize-admin"),
   472  			Config:                  modelCfg,
   473  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   474  		},
   475  		Cloud: cloud.Cloud{
   476  			Name:      "dummy",
   477  			Type:      "dummy",
   478  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType},
   479  		},
   480  		MongoSession:  s.Session,
   481  		AdminPassword: "dummy-secret",
   482  	}
   483  
   484  	for _, badAttrName := range badAttrNames {
   485  		badAttrs := map[string]interface{}{badAttrName: "foo"}
   486  		args.ControllerInheritedConfig = badAttrs
   487  		_, err := state.Initialize(args)
   488  		c.Assert(err, gc.ErrorMatches, "local cloud config cannot contain .*")
   489  	}
   490  }
   491  
   492  func (s *InitializeSuite) TestInitializeWithCloudRegionConfig(c *gc.C) {
   493  	cfg := testing.ModelConfig(c)
   494  	uuid := cfg.UUID()
   495  
   496  	// Phony region-config
   497  	regionInheritedConfigIn := cloud.RegionConfig{
   498  		"a-region": cloud.Attrs{
   499  			"a-key": "a-value",
   500  		},
   501  		"b-region": cloud.Attrs{
   502  			"b-key": "b-value",
   503  		},
   504  	}
   505  	owner := names.NewLocalUserTag("initialize-admin")
   506  	controllerCfg := testing.FakeControllerConfig()
   507  
   508  	ctlr, err := state.Initialize(state.InitializeParams{
   509  		Clock:            clock.WallClock,
   510  		ControllerConfig: controllerCfg,
   511  		ControllerModelArgs: state.ModelArgs{
   512  			Type:                    state.ModelTypeIAAS,
   513  			CloudName:               "dummy",
   514  			Owner:                   owner,
   515  			Config:                  cfg,
   516  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   517  		},
   518  		Cloud: cloud.Cloud{
   519  			Name:         "dummy",
   520  			Type:         "dummy",
   521  			AuthTypes:    []cloud.AuthType{cloud.EmptyAuthType},
   522  			RegionConfig: regionInheritedConfigIn, // Init with phony region-config
   523  		},
   524  		MongoSession:  s.Session,
   525  		AdminPassword: "dummy-secret",
   526  	})
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	c.Assert(ctlr, gc.NotNil)
   529  	st, err := ctlr.SystemState()
   530  	c.Assert(err, jc.ErrorIsNil)
   531  	m, err := st.Model()
   532  	c.Assert(err, jc.ErrorIsNil)
   533  	modelTag := m.ModelTag()
   534  	c.Assert(modelTag.Id(), gc.Equals, uuid)
   535  
   536  	err = ctlr.Close()
   537  	c.Assert(err, jc.ErrorIsNil)
   538  
   539  	s.openState(c, modelTag)
   540  
   541  	for k := range regionInheritedConfigIn {
   542  		// Query for config for each region
   543  		regionInheritedConfig, err := s.State.ReadSettings(
   544  			state.GlobalSettingsC,
   545  			"dummy#"+k)
   546  		c.Assert(err, jc.ErrorIsNil)
   547  		c.Assert(
   548  			cloud.Attrs(regionInheritedConfig.Map()),
   549  			jc.DeepEquals,
   550  			regionInheritedConfigIn[k])
   551  	}
   552  }
   553  
   554  func (s *InitializeSuite) TestInitializeWithCloudRegionMisses(c *gc.C) {
   555  	cfg := testing.ModelConfig(c)
   556  	uuid := cfg.UUID()
   557  	controllerInheritedConfigIn := map[string]interface{}{
   558  		"no-proxy": "local",
   559  	}
   560  	// Phony region-config
   561  	regionInheritedConfigIn := cloud.RegionConfig{
   562  		"a-region": cloud.Attrs{
   563  			"no-proxy": "a-value",
   564  		},
   565  		"b-region": cloud.Attrs{
   566  			"no-proxy": "b-value",
   567  		},
   568  	}
   569  	owner := names.NewLocalUserTag("initialize-admin")
   570  	controllerCfg := testing.FakeControllerConfig()
   571  
   572  	ctlr, err := state.Initialize(state.InitializeParams{
   573  		Clock:            clock.WallClock,
   574  		ControllerConfig: controllerCfg,
   575  		ControllerModelArgs: state.ModelArgs{
   576  			Type:                    state.ModelTypeIAAS,
   577  			CloudName:               "dummy",
   578  			Owner:                   owner,
   579  			Config:                  cfg,
   580  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   581  		},
   582  		Cloud: cloud.Cloud{
   583  			Name:         "dummy",
   584  			Type:         "dummy",
   585  			AuthTypes:    []cloud.AuthType{cloud.EmptyAuthType},
   586  			RegionConfig: regionInheritedConfigIn, // Init with phony region-config
   587  		},
   588  		ControllerInheritedConfig: controllerInheritedConfigIn,
   589  		MongoSession:              s.Session,
   590  		AdminPassword:             "dummy-secret",
   591  	})
   592  	c.Assert(err, jc.ErrorIsNil)
   593  	c.Assert(ctlr, gc.NotNil)
   594  	sysState, err := ctlr.SystemState()
   595  	c.Assert(err, jc.ErrorIsNil)
   596  	m, err := sysState.Model()
   597  	c.Assert(err, jc.ErrorIsNil)
   598  	modelTag := m.ModelTag()
   599  	c.Assert(modelTag.Id(), gc.Equals, uuid)
   600  
   601  	err = ctlr.Close()
   602  	c.Assert(err, jc.ErrorIsNil)
   603  
   604  	s.openState(c, modelTag)
   605  
   606  	var attrs map[string]interface{}
   607  	rspec := &environscloudspec.CloudRegionSpec{Cloud: "dummy", Region: "c-region"}
   608  	got, err := s.State.ComposeNewModelConfig(attrs, rspec)
   609  	c.Check(err, jc.ErrorIsNil)
   610  	c.Assert(got["no-proxy"], gc.Equals, "local")
   611  }
   612  
   613  func (s *InitializeSuite) TestInitializeWithCloudRegionHits(c *gc.C) {
   614  	cfg := testing.ModelConfig(c)
   615  	uuid := cfg.UUID()
   616  
   617  	controllerInheritedConfigIn := map[string]interface{}{
   618  		"no-proxy": "local",
   619  	}
   620  	// Phony region-config
   621  	regionInheritedConfigIn := cloud.RegionConfig{
   622  		"a-region": cloud.Attrs{
   623  			"no-proxy": "a-value",
   624  		},
   625  		"b-region": cloud.Attrs{
   626  			"no-proxy": "b-value",
   627  		},
   628  	}
   629  	owner := names.NewLocalUserTag("initialize-admin")
   630  	controllerCfg := testing.FakeControllerConfig()
   631  
   632  	ctlr, err := state.Initialize(state.InitializeParams{
   633  		Clock:            clock.WallClock,
   634  		ControllerConfig: controllerCfg,
   635  		ControllerModelArgs: state.ModelArgs{
   636  			Type:                    state.ModelTypeIAAS,
   637  			CloudName:               "dummy",
   638  			Owner:                   owner,
   639  			Config:                  cfg,
   640  			StorageProviderRegistry: storage.StaticProviderRegistry{},
   641  		},
   642  		Cloud: cloud.Cloud{
   643  			Name:         "dummy",
   644  			Type:         "dummy",
   645  			AuthTypes:    []cloud.AuthType{cloud.EmptyAuthType},
   646  			RegionConfig: regionInheritedConfigIn, // Init with phony region-config
   647  		},
   648  		ControllerInheritedConfig: controllerInheritedConfigIn,
   649  		MongoSession:              s.Session,
   650  		AdminPassword:             "dummy-secret",
   651  	})
   652  	c.Assert(err, jc.ErrorIsNil)
   653  	c.Assert(ctlr, gc.NotNil)
   654  	sysState, err := ctlr.SystemState()
   655  	c.Assert(err, jc.ErrorIsNil)
   656  	m, err := sysState.Model()
   657  	c.Assert(err, jc.ErrorIsNil)
   658  	modelTag := m.ModelTag()
   659  	c.Assert(modelTag.Id(), gc.Equals, uuid)
   660  
   661  	err = ctlr.Close()
   662  	c.Assert(err, jc.ErrorIsNil)
   663  
   664  	s.openState(c, modelTag)
   665  
   666  	var attrs map[string]interface{}
   667  	for r := range regionInheritedConfigIn {
   668  		rspec := &environscloudspec.CloudRegionSpec{Cloud: "dummy", Region: r}
   669  		got, err := s.State.ComposeNewModelConfig(attrs, rspec)
   670  		c.Check(err, jc.ErrorIsNil)
   671  		c.Assert(got["no-proxy"], gc.Equals, regionInheritedConfigIn[r]["no-proxy"])
   672  	}
   673  }
   674  
   675  func (s *InitializeSuite) TestInitializeWithStoragePool(c *gc.C) {
   676  	cfg := testing.ModelConfig(c)
   677  	uuid := cfg.UUID()
   678  
   679  	owner := names.NewLocalUserTag("initialize-admin")
   680  	controllerCfg := testing.FakeControllerConfig()
   681  
   682  	staticProvider := &dummy.StorageProvider{
   683  		IsDynamic:    true,
   684  		StorageScope: storage.ScopeEnviron,
   685  		SupportsFunc: func(storage.StorageKind) bool {
   686  			return false
   687  		},
   688  	}
   689  	registry := storage.StaticProviderRegistry{
   690  		Providers: map[storage.ProviderType]storage.Provider{
   691  			"dummy": staticProvider,
   692  		},
   693  	}
   694  	ctlr, err := state.Initialize(state.InitializeParams{
   695  		Clock:            clock.WallClock,
   696  		ControllerConfig: controllerCfg,
   697  		ControllerModelArgs: state.ModelArgs{
   698  			Type:                    state.ModelTypeIAAS,
   699  			CloudName:               "dummy",
   700  			Owner:                   owner,
   701  			Config:                  cfg,
   702  			StorageProviderRegistry: registry,
   703  		},
   704  		Cloud: cloud.Cloud{
   705  			Name:      "dummy",
   706  			Type:      "dummy",
   707  			AuthTypes: []cloud.AuthType{cloud.EmptyAuthType},
   708  		},
   709  		MongoSession:  s.Session,
   710  		AdminPassword: "dummy-secret",
   711  		StoragePools: map[string]storage.Attrs{
   712  			"spool": {
   713  				"type": "dummy",
   714  				"foo":  "bar",
   715  			},
   716  		},
   717  	})
   718  	c.Assert(err, jc.ErrorIsNil)
   719  	c.Assert(ctlr, gc.NotNil)
   720  	sysState, err := ctlr.SystemState()
   721  	c.Assert(err, jc.ErrorIsNil)
   722  	m, err := sysState.Model()
   723  	c.Assert(err, jc.ErrorIsNil)
   724  	modelTag := m.ModelTag()
   725  	c.Assert(modelTag.Id(), gc.Equals, uuid)
   726  
   727  	err = ctlr.Close()
   728  	c.Assert(err, jc.ErrorIsNil)
   729  
   730  	s.openState(c, modelTag)
   731  
   732  	pm := poolmanager.New(state.NewStateSettings(s.State), registry)
   733  	storageCfg, err := pm.Get("spool")
   734  	c.Assert(err, jc.ErrorIsNil)
   735  	expectedCfg, err := storage.NewConfig("spool", "dummy", map[string]interface{}{"foo": "bar"})
   736  	c.Assert(err, jc.ErrorIsNil)
   737  	c.Assert(storageCfg, jc.DeepEquals, expectedCfg)
   738  }