Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type Model interface {
HasStatus
HasStatusHistory

// AgentVersion returns the version currently in use by the model.
AgentVersion() string

Type() string
Cloud() string
CloudRegion() string
Expand Down Expand Up @@ -144,6 +147,9 @@ type Model interface {
// ModelArgs represent the bare minimum information that is needed
// to represent a model.
type ModelArgs struct {
// AgentVersion defines the current version in use by the model.
AgentVersion string

Type string
Owner names.UserTag
Config map[string]interface{}
Expand All @@ -159,7 +165,8 @@ type ModelArgs struct {
// NewModel returns a Model based on the args specified.
func NewModel(args ModelArgs) Model {
m := &model{
Version: 10,
Version: 11,
AgentVersion_: args.AgentVersion,
Type_: args.Type,
Owner_: args.Owner.Id(),
Config_: args.Config,
Expand Down Expand Up @@ -254,6 +261,9 @@ func parentId(machineId string) string {
type model struct {
Version int `yaml:"version"`

// AgentVersion_ defines the agent version in use by the model.
AgentVersion_ string `yaml:"agent-version"`

Type_ string `yaml:"type"`
Owner_ string `yaml:"owner"`
Config_ map[string]interface{} `yaml:"config"`
Expand Down Expand Up @@ -314,6 +324,11 @@ type model struct {
PasswordHash_ string `yaml:"password-hash,omitempty"`
}

// AgentVersion returns the current agent version in use the by the model.
func (m *model) AgentVersion() string {
return m.AgentVersion_
}

func (m *model) Type() string {
return m.Type_
}
Expand Down Expand Up @@ -1092,6 +1107,15 @@ func (m *model) Validate() error {
return errors.NotValidf("missing status")
}

if m.AgentVersion_ != "" {
agentVersion, err := version.Parse(m.AgentVersion_)
if err != nil {
return errors.Annotate(err, "agent version not parsable")
} else if agentVersion == version.Zero {
return errors.NotValidf("agent version cannot be zero")
}
}

validationCtx := newValidationContext()
for _, machine := range m.Machines_.Machines_ {
if err := m.validateMachine(validationCtx, machine); err != nil {
Expand Down Expand Up @@ -1522,6 +1546,7 @@ var modelDeserializationFuncs = map[int]modelDeserializationFunc{
8: newModelImporter(8, schema.FieldMap(modelV8Fields())),
9: newModelImporter(9, schema.FieldMap(modelV9Fields())),
10: newModelImporter(10, schema.FieldMap(modelV10Fields())),
11: newModelImporter(11, schema.FieldMap(modelV11Fields())),
}

func modelV1Fields() (schema.Fields, schema.Defaults) {
Expand Down Expand Up @@ -1635,11 +1660,17 @@ func modelV10Fields() (schema.Fields, schema.Defaults) {
return fields, defaults
}

func modelV11Fields() (schema.Fields, schema.Defaults) {
fields, defaults := modelV10Fields()
fields["agent-version"] = schema.String()
return fields, defaults
}

func newModelFromValid(valid map[string]interface{}, importVersion int) (*model, error) {
// We're always making a version 8 model, no matter what we got on
// the way in.
result := &model{
Version: 10,
Version: 11,
Type_: IAAS,
Owner_: valid["owner"].(string),
Config_: valid["config"].(map[string]interface{}),
Expand Down Expand Up @@ -1903,6 +1934,16 @@ func newModelFromValid(valid map[string]interface{}, importVersion int) (*model,

result.SecretBackendID_ = valid["secret-backend-id"].(string)
}

// When we are importing v11 onwards agent version will be a first class
// citizen on the model. Before this we can attempt to get the value from
// config.
if importVersion >= 11 {
result.AgentVersion_ = valid["agent-version"].(string)
} else if result.Config_ != nil && result.Config_["agent-version"] != nil {
result.AgentVersion_ = result.Config_["agent-version"].(string)
}

return result, nil
}

Expand Down
25 changes: 22 additions & 3 deletions model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ func (s *ModelSerializationSuite) TestParsingYAMLWithMissingModificationStatus(c

func (s *ModelSerializationSuite) testParsingYAMLWithMachine(c *gc.C, machineFn func(Model)) {
args := ModelArgs{
Type: IAAS,
Owner: names.NewUserTag("magic"),
AgentVersion: "3.1.1",
Type: IAAS,
Owner: names.NewUserTag("magic"),
Config: map[string]interface{}{
"name": "awesome",
"uuid": "some-uuid",
Expand Down Expand Up @@ -202,6 +203,7 @@ func (s *ModelSerializationSuite) testParsingYAMLWithMachine(c *gc.C, machineFn
addMinimalApplication(initial)
model := s.exportImport(c, initial)

c.Check(model.AgentVersion(), gc.Equals, "3.1.1")
c.Assert(model.Type(), gc.Equals, IAAS)
c.Assert(model.Owner(), gc.Equals, args.Owner)
c.Assert(model.Tag().Id(), gc.Equals, "some-uuid")
Expand Down Expand Up @@ -1165,7 +1167,7 @@ func (s *ModelSerializationSuite) TestSerializesToLatestVersion(c *gc.C) {
c.Assert(ok, jc.IsTrue)
version, ok := versionValue.(int)
c.Assert(ok, jc.IsTrue)
c.Assert(version, gc.Equals, 10)
c.Assert(version, gc.Equals, 11)
}

func (s *ModelSerializationSuite) TestVersion1Works(c *gc.C) {
Expand Down Expand Up @@ -1634,6 +1636,23 @@ func (s *ModelSerializationSuite) TestRemoteSecretsValidate(c *gc.C) {
c.Assert(err, gc.ErrorMatches, `remote secret\[0\] consumer \(foo\) not valid`)
}

func (s *ModelSerializationSuite) TestAgentVersionPre11Import(c *gc.C) {
initial := s.newModel(ModelArgs{
Config: map[string]any{
"agent-version": "3.3.3",
},
})
data := asStringMap(c, initial)
data["version"] = 10
bytes, err := yaml.Marshal(data)
c.Assert(err, jc.ErrorIsNil)

model, err := Deserialize(bytes)
c.Check(err, jc.ErrorIsNil)

c.Check(model.AgentVersion(), gc.Equals, "3.3.3")
}

// modelV1example was taken from a Juju 2.1 model dump, which is version
// 1, and among other things is missing model status, which version 2 makes
// manditory.
Expand Down