Skip to content

Commit

Permalink
Introduce environ/provider versioning
Browse files Browse the repository at this point in the history
Environs (i.e. the cloud representation of
a model) are now versioned, so that we can
upgrade them as necessary. Previously this
has been done as part of controller upgrades,
but this poses a problem when the environs
cannot be opened; thuse we seek to decouple
the upgrading of a model's environ from the
upgrading of the controller.

EnvironProvider now has a Version method,
which returns the current/most up-to-date
environ version. Model docs now record the
version of the model's associated environ.
Environ upgrade operations specify an
environ/provider version, rather than
controller/agent version.

Once this lands, we will introduce a new
worker that will run under the model
dependency engine to upgrade the model's
environ, and which will gate access to the
model until that has happened.
  • Loading branch information
axw committed Jul 7, 2017
1 parent 2d5a2e9 commit 5fae823
Show file tree
Hide file tree
Showing 42 changed files with 383 additions and 72 deletions.
2 changes: 2 additions & 0 deletions agent/agentbootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func InitializeState(
CloudRegion: args.ControllerCloudRegion,
CloudCredential: cloudCredentialTag,
StorageProviderRegistry: args.StorageProviderRegistry,
EnvironVersion: args.ControllerModelEnvironVersion,
},
Cloud: args.ControllerCloud,
CloudCredentials: cloudCredentials,
Expand Down Expand Up @@ -206,6 +207,7 @@ func InitializeState(
CloudRegion: args.ControllerCloudRegion,
CloudCredential: cloudCredentialTag,
StorageProviderRegistry: args.StorageProviderRegistry,
EnvironVersion: hostedModelEnv.Provider().Version(),
})
if err != nil {
return nil, nil, errors.Annotate(err, "creating hosted model")
Expand Down
34 changes: 26 additions & 8 deletions agent/agentbootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,13 @@ LXC_BRIDGE="ignored"`[1:])
Regions: []cloud.Region{{Name: "dummy-region"}},
RegionConfig: regionConfig,
},
ControllerCloudRegion: "dummy-region",
ControllerConfig: controllerCfg,
ControllerModelConfig: modelCfg,
ModelConstraints: expectModelConstraints,
ControllerInheritedConfig: controllerInheritedConfig,
HostedModelConfig: hostedModelConfigAttrs,
ControllerCloudRegion: "dummy-region",
ControllerConfig: controllerCfg,
ControllerModelConfig: modelCfg,
ControllerModelEnvironVersion: 666,
ModelConstraints: expectModelConstraints,
ControllerInheritedConfig: controllerInheritedConfig,
HostedModelConfig: hostedModelConfigAttrs,
},
BootstrapMachineAddresses: initialAddrs,
BootstrapMachineJobs: []multiwatcher.MachineJob{multiwatcher.JobManageModel},
Expand All @@ -200,10 +201,11 @@ LXC_BRIDGE="ignored"`[1:])
err = cfg.Write()
c.Assert(err, jc.ErrorIsNil)

// Check that the environment has been set up.
// Check that the model has been set up.
model, err := st.Model()
c.Assert(err, jc.ErrorIsNil)
c.Assert(model.UUID(), gc.Equals, modelCfg.UUID())
c.Assert(model.EnvironVersion(), gc.Equals, 666)

// Check that initial admin user has been set up correctly.
modelTag := model.Tag().(names.ModelTag)
Expand Down Expand Up @@ -256,6 +258,7 @@ LXC_BRIDGE="ignored"`[1:])
c.Assert(err, jc.ErrorIsNil)
c.Assert(hostedModel.Name(), gc.Equals, "hosted")
c.Assert(hostedModel.CloudRegion(), gc.Equals, "dummy-region")
c.Assert(hostedModel.EnvironVersion(), gc.Equals, 123)
hostedCfg, err := hostedModelSt.ModelConfig()
c.Assert(err, jc.ErrorIsNil)
_, hasUnexpected := hostedCfg.AllAttrs()["not-for-hosted"]
Expand Down Expand Up @@ -321,6 +324,8 @@ LXC_BRIDGE="ignored"`[1:])
"Validate",
"Open",
"Create",
"Provider",
"Version",
)
envProvider.CheckCall(c, 2, "Open", environs.OpenParams{
Cloud: environs.CloudSpec{
Expand Down Expand Up @@ -494,15 +499,28 @@ func (p *fakeProvider) Validate(newCfg, oldCfg *config.Config) (*config.Config,

func (p *fakeProvider) Open(args environs.OpenParams) (environs.Environ, error) {
p.MethodCall(p, "Open", args)
return &fakeEnviron{Stub: &p.Stub}, p.NextErr()
return &fakeEnviron{Stub: &p.Stub, provider: p}, p.NextErr()
}

func (p *fakeProvider) Version() int {
p.MethodCall(p, "Version")
p.PopNoErr()
return 123
}

type fakeEnviron struct {
environs.Environ
*gitjujutesting.Stub
provider *fakeProvider
}

func (e *fakeEnviron) Create(args environs.CreateParams) error {
e.MethodCall(e, "Create", args)
return e.NextErr()
}

func (e *fakeEnviron) Provider() environs.EnvironProvider {
e.MethodCall(e, "Provider")
e.PopNoErr()
return e.provider
}
1 change: 1 addition & 0 deletions apiserver/modelmanager/modelmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ func (m *ModelManagerAPI) CreateModel(args params.ModelCreateArgs) (params.Model
Config: newConfig,
Owner: ownerTag,
StorageProviderRegistry: storageProviderRegistry,
EnvironVersion: env.Provider().Version(),
})
if err != nil {
return result, errors.Annotate(err, "failed to create new model")
Expand Down
7 changes: 7 additions & 0 deletions cloudconfig/instancecfg/instancecfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ type StateInitializationParams struct {
// ControllerModelConfig holds the initial controller model configuration.
ControllerModelConfig *config.Config

// ControllerModelEnvironVersion holds the initial controller model
// environ version.
ControllerModelEnvironVersion int

// ControllerCloud contains the properties of the cloud that Juju will
// be bootstrapped in.
ControllerCloud cloud.Cloud
Expand Down Expand Up @@ -287,6 +291,7 @@ type StateInitializationParams struct {
type stateInitializationParamsInternal struct {
ControllerConfig map[string]interface{} `yaml:"controller-config"`
ControllerModelConfig map[string]interface{} `yaml:"controller-model-config"`
ControllerModelEnvironVersion int `yaml:"controller-model-version"`
ControllerInheritedConfig map[string]interface{} `yaml:"controller-config-defaults,omitempty"`
RegionInheritedConfig cloud.RegionConfig `yaml:"region-inherited-config,omitempty"`
HostedModelConfig map[string]interface{} `yaml:"hosted-model-config,omitempty"`
Expand Down Expand Up @@ -314,6 +319,7 @@ func (p *StateInitializationParams) Marshal() ([]byte, error) {
internal := stateInitializationParamsInternal{
p.ControllerConfig,
p.ControllerModelConfig.AllAttrs(),
p.ControllerModelEnvironVersion,
p.ControllerInheritedConfig,
p.RegionInheritedConfig,
p.HostedModelConfig,
Expand Down Expand Up @@ -352,6 +358,7 @@ func (p *StateInitializationParams) Unmarshal(data []byte) error {
*p = StateInitializationParams{
ControllerConfig: internal.ControllerConfig,
ControllerModelConfig: cfg,
ControllerModelEnvironVersion: internal.ControllerModelEnvironVersion,
ControllerInheritedConfig: internal.ControllerInheritedConfig,
RegionInheritedConfig: internal.RegionInheritedConfig,
HostedModelConfig: internal.HostedModelConfig,
Expand Down
7 changes: 6 additions & 1 deletion environs/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,10 @@ func Bootstrap(ctx environs.BootstrapContext, environ environs.Environ, args Boo
// Make sure we have the most recent environ config as the specified
// tools version has been updated there.
cfg = environ.Config()
if err := finalizeInstanceBootstrapConfig(ctx, instanceConfig, args, cfg, customImageMetadata); err != nil {
environVersion := environ.Provider().Version()
if err := finalizeInstanceBootstrapConfig(
ctx, instanceConfig, args, cfg, environVersion, customImageMetadata,
); err != nil {
return errors.Annotate(err, "finalizing bootstrap instance config")
}
if err := result.Finalize(ctx, instanceConfig, args.DialOpts); err != nil {
Expand All @@ -426,6 +429,7 @@ func finalizeInstanceBootstrapConfig(
icfg *instancecfg.InstanceConfig,
args BootstrapParams,
cfg *config.Config,
environVersion int,
customImageMetadata []*imagemetadata.ImageMetadata,
) error {
if icfg.APIInfo != nil || icfg.Controller.MongoInfo != nil {
Expand Down Expand Up @@ -466,6 +470,7 @@ func finalizeInstanceBootstrapConfig(
}

icfg.Bootstrap.ControllerModelConfig = cfg
icfg.Bootstrap.ControllerModelEnvironVersion = environVersion
icfg.Bootstrap.CustomImageMetadata = customImageMetadata
icfg.Bootstrap.ControllerCloud = args.Cloud
icfg.Bootstrap.ControllerCloudRegion = args.CloudRegion
Expand Down
13 changes: 13 additions & 0 deletions environs/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ func (s *bootstrapSuite) TestBootstrapImage(c *gc.C) {
expectedCons := bootstrapCons
expectedCons.Mem = intPtr(3584)
c.Assert(env.instanceConfig.Bootstrap.BootstrapMachineConstraints, jc.DeepEquals, expectedCons)
c.Assert(env.instanceConfig.Bootstrap.ControllerModelEnvironVersion, gc.Equals, 123)
}

func (s *bootstrapSuite) TestBootstrapAddsArchFromImageToExistingProviderSupportedArches(c *gc.C) {
Expand Down Expand Up @@ -1343,6 +1344,18 @@ func (e *bootstrapEnviron) ConstraintsValidator() (constraints.Validator, error)
return v, nil
}

func (e *bootstrapEnviron) Provider() environs.EnvironProvider {
return bootstrapEnvironProvider{}
}

type bootstrapEnvironProvider struct {
environs.EnvironProvider
}

func (p bootstrapEnvironProvider) Version() int {
return 123
}

type bootstrapEnvironWithRegion struct {
*bootstrapEnviron
region simplestreams.CloudSpec
Expand Down
19 changes: 14 additions & 5 deletions environs/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ type EnvironProvider interface {
config.Validator
ProviderCredentials

// Version returns the version of the provider. This is recorded as the
// environ version for each model, and used to identify which upgrade
// operations to run when upgrading a model's environ. Providers should
// start out at version 0.
Version() int

// CloudSchema returns the schema used to validate input for add-cloud. If
// a provider does not suppport custom clouds, CloudSchema should return
// nil.
Expand Down Expand Up @@ -411,17 +417,20 @@ type Upgrader interface {
// UpgradeOperationsParams contains the parameters for
// Upgrader.UpgradeOperations.
type UpgradeOperationsParams struct {
// ControllerUUID is the UUID of the controller to be that contains
// the Environ that is to be upgraded.
// ControllerUUID is the UUID of the controller that manages
// the Environ being upgraded.
ControllerUUID string
}

// UpgradeOperation contains a target agent version and sequence of upgrade
// steps to apply to get to that version.
type UpgradeOperation struct {
// TargetVersion is the target agent version number to which the
// upgrade steps pertain.
TargetVersion version.Number
// TargetVersion is the target environ provider version number to
// which the upgrade steps pertain. When a model is upgraded, all
// upgrade operations will be run for versions greater than the
// recorded environ version. This version number is independent of
// the agent and controller versions.
TargetVersion int

// Steps contains the sequence of upgrade steps to apply when
// upgrading to the accompanying target version number.
Expand Down
14 changes: 14 additions & 0 deletions provider/azure/environprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ import (
"github.com/juju/juju/provider/azure/internal/azurestorage"
)

const (
// provider version 1 introduces the "common" deployment,
// which contains common resources such as the virtual
// network and network security group.
providerVersion1 = 1

currentProviderVersion = providerVersion1
)

// Logger for the Azure provider.
var logger = loggo.GetLogger("juju.provider.azure")

Expand Down Expand Up @@ -94,6 +103,11 @@ func NewEnvironProvider(config ProviderConfig) (*azureEnvironProvider, error) {
}, nil
}

// Version is part of the EnvironProvider interface.
func (prov *azureEnvironProvider) Version() int {
return currentProviderVersion
}

// Open is part of the EnvironProvider interface.
func (prov *azureEnvironProvider) Open(args environs.OpenParams) (environs.Environ, error) {
logger.Debugf("opening model %q", args.Config.Name())
Expand Down
3 changes: 1 addition & 2 deletions provider/azure/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/to"
"github.com/juju/errors"
"github.com/juju/version"

"github.com/juju/juju/environs"
"github.com/juju/juju/environs/tags"
Expand All @@ -18,7 +17,7 @@ import (
// UpgradeOperations is part of the upgrades.OperationSource interface.
func (env *azureEnviron) UpgradeOperations(environs.UpgradeOperationsParams) []environs.UpgradeOperation {
return []environs.UpgradeOperation{{
version.MustParse("2.2-alpha1"),
providerVersion1,
[]environs.UpgradeStep{
commonDeploymentUpgradeStep{env},
},
Expand Down
3 changes: 1 addition & 2 deletions provider/azure/upgrades_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/Azure/azure-sdk-for-go/arm/storage"
"github.com/Azure/go-autorest/autorest/to"
jc "github.com/juju/testing/checkers"
"github.com/juju/version"
gc "gopkg.in/check.v1"

"github.com/juju/juju/environs"
Expand Down Expand Up @@ -54,7 +53,7 @@ func (s *environUpgradeSuite) TestEnvironUpgradeOperations(c *gc.C) {
upgrader := s.env.(environs.Upgrader)
ops := upgrader.UpgradeOperations(environs.UpgradeOperationsParams{})
c.Assert(ops, gc.HasLen, 1)
c.Assert(ops[0].TargetVersion, gc.Equals, version.MustParse("2.2-alpha1"))
c.Assert(ops[0].TargetVersion, gc.Equals, 1)
c.Assert(ops[0].Steps, gc.HasLen, 1)
c.Assert(ops[0].Steps[0].Description(), gc.Equals, "Create common resource deployment")
}
Expand Down
5 changes: 5 additions & 0 deletions provider/cloudsigma/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func init() {
environs.RegisterImageDataSourceFunc("cloud sigma image source", getImageSource)
}

// Version is part of the EnvironProvider interface.
func (environProvider) Version() int {
return 0
}

// Open opens the environment and returns it.
// The configuration must have come from a previously
// prepared environment.
Expand Down
5 changes: 5 additions & 0 deletions provider/dummy/environs.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,11 @@ func (e *environ) state() (*environState, error) {
return state, nil
}

// Version is part of the EnvironProvider interface.
func (*environProvider) Version() int {
return 0
}

func (p *environProvider) Open(args environs.OpenParams) (environs.Environ, error) {
p.mu.Lock()
defer p.mu.Unlock()
Expand Down
5 changes: 5 additions & 0 deletions provider/ec2/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ type environProvider struct {

var providerInstance environProvider

// Version is part of the EnvironProvider interface.
func (environProvider) Version() int {
return 0
}

// Open is specified in the EnvironProvider interface.
func (p environProvider) Open(args environs.OpenParams) (environs.Environ, error) {
logger.Infof("opening model %q", args.Config.Name())
Expand Down
5 changes: 5 additions & 0 deletions provider/gce/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ type environProvider struct {

var providerInstance environProvider

// Version is part of the EnvironProvider interface.
func (environProvider) Version() int {
return 0
}

// Open implements environs.EnvironProvider.
func (environProvider) Open(args environs.OpenParams) (environs.Environ, error) {
if err := validateCloudSpec(args.Cloud); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions provider/joyent/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ func credentials(cloud environs.CloudSpec) (*auth.Credentials, error) {
}, nil
}

// Version is part of the EnvironProvider interface.
func (joyentProvider) Version() int {
return 0
}

func (joyentProvider) Open(args environs.OpenParams) (environs.Environ, error) {
if err := validateCloudSpec(args.Cloud); err != nil {
return nil, errors.Annotate(err, "validating cloud spec")
Expand Down
7 changes: 6 additions & 1 deletion provider/lxd/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ func NewProvider() environs.EnvironProvider {
}
}

// Version is part of the EnvironProvider interface.
func (*environProvider) Version() int {
return 0
}

// Open implements environs.EnvironProvider.
func (p *environProvider) Open(args environs.OpenParams) (environs.Environ, error) {
local, err := p.validateCloudSpec(args.Cloud)
Expand All @@ -64,7 +69,7 @@ func (p *environProvider) CloudSchema() *jsonschema.Schema {
}

// Ping tests the connection to the cloud, to verify the endpoint is valid.
func (p environProvider) Ping(endpoint string) error {
func (p *environProvider) Ping(endpoint string) error {
return errors.NotImplementedf("Ping")
}

Expand Down
5 changes: 5 additions & 0 deletions provider/maas/environprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ var _ environs.EnvironProvider = (*MaasEnvironProvider)(nil)

var providerInstance MaasEnvironProvider

// Version is part of the EnvironProvider interface.
func (MaasEnvironProvider) Version() int {
return 0
}

func (MaasEnvironProvider) Open(args environs.OpenParams) (environs.Environ, error) {
logger.Debugf("opening model %q.", args.Config.Name())
if err := validateCloudSpec(args.Cloud); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions provider/manual/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ func (p ManualProvider) PrepareConfig(args environs.PrepareConfigParams) (*confi
return args.Config.Apply(envConfig.attrs)
}

// Version is part of the EnvironProvider interface.
func (ManualProvider) Version() int {
return 0
}

func (p ManualProvider) Open(args environs.OpenParams) (environs.Environ, error) {
if err := validateCloudSpec(args.Cloud); err != nil {
return nil, errors.Trace(err)
Expand Down
Loading

0 comments on commit 5fae823

Please sign in to comment.