Skip to content

Commit

Permalink
Introduces agent version as a top level key.
Browse files Browse the repository at this point in the history
Moving to DQlite in Juju 4.0 we are removing agent version from model
config and making it a first class attribute of a model. To support
migrating from 3.6 and 4.0 we need the agent version to be available at
the root of model migration.

To do this new uses of v11 will have to set the key and for older
versions of the description package an attempt will be made to pull this
information from the model config.
  • Loading branch information
tlm committed Jun 11, 2024
1 parent d734bcd commit 1f7f17b
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 5 deletions.
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

0 comments on commit 1f7f17b

Please sign in to comment.