Skip to content

Commit

Permalink
Merge pull request #148 from SimonRichardson/cherry-pick-charm-metada…
Browse files Browse the repository at this point in the history
…ta-manifest

#148

Cherry-pick of #147 to v7, to allow landing of 3.6 without bringing in #139

We can't land this into v6 as that is for main. So effectively, v6 branch is dead and we should never have cut a release for main against v6. The main branch is unfinished and we should have been just iterating on a hash.
  • Loading branch information
jujubot authored Jul 29, 2024
2 parents e21a788 + 003ba47 commit dca60b8
Show file tree
Hide file tree
Showing 9 changed files with 1,953 additions and 7 deletions.
70 changes: 70 additions & 0 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type Application interface {
CharmOrigin() CharmOrigin
SetCharmOrigin(CharmOriginArgs)

CharmMetadata() CharmMetadata
SetCharmMetadata(CharmMetadataArgs)

CharmManifest() CharmManifest
SetCharmManifest(CharmManifestArgs)

Tools() AgentTools
SetTools(AgentToolsArgs)

Expand Down Expand Up @@ -147,6 +153,9 @@ type application struct {

// CharmOrigin fields
CharmOrigin_ *charmOrigin `yaml:"charm-origin,omitempty"`
// CharmMetadata and CharmManifest fields
CharmMetadata_ *charmMetadata `yaml:"charm-metadata,omitempty"`
CharmManifest_ *charmManifest `yaml:"charm-manifest,omitempty"`
}

// ApplicationArgs is an argument struct used to add an application to the Model.
Expand Down Expand Up @@ -517,6 +526,34 @@ func (a *application) SetCharmOrigin(args CharmOriginArgs) {
a.CharmOrigin_ = newCharmOrigin(args)
}

// CharmMetadata implements Application.
func (a *application) CharmMetadata() CharmMetadata {
// To avoid a typed nil, check before returning.
if a.CharmMetadata_ == nil {
return nil
}
return a.CharmMetadata_
}

// SetCharmMetadata implements Application.
func (a *application) SetCharmMetadata(args CharmMetadataArgs) {
a.CharmMetadata_ = newCharmMetadata(args)
}

// CharmManifest implements Application.
func (a *application) CharmManifest() CharmManifest {
// To avoid a typed nil, check before returning.
if a.CharmManifest_ == nil {
return nil
}
return a.CharmManifest_
}

// SetCharmManifest implements Application.
func (a *application) SetCharmManifest(args CharmManifestArgs) {
a.CharmManifest_ = newCharmManifest(args)
}

// Offers implements Application.
func (a *application) Offers() []ApplicationOffer {
if a.Offers_ == nil || len(a.Offers_.Offers) == 0 {
Expand Down Expand Up @@ -639,6 +676,7 @@ var applicationDeserializationFuncs = map[int]applicationDeserializationFunc{
10: importApplicationV10,
11: importApplicationV11,
12: importApplicationV12,
13: importApplicationV13,
}

func applicationV1Fields() (schema.Fields, schema.Defaults) {
Expand Down Expand Up @@ -769,6 +807,15 @@ func applicationV12Fields() (schema.Fields, schema.Defaults) {
return fields, defaults
}

func applicationV13Fields() (schema.Fields, schema.Defaults) {
fields, defaults := applicationV12Fields()
fields["charm-metadata"] = schema.StringMap(schema.Any())
fields["charm-manifest"] = schema.StringMap(schema.Any())
defaults["charm-metadata"] = schema.Omit
defaults["charm-manifest"] = schema.Omit
return fields, defaults
}

func importApplicationV1(source map[string]interface{}) (*application, error) {
fields, defaults := applicationV1Fields()
return importApplication(fields, defaults, 1, source)
Expand Down Expand Up @@ -829,6 +876,11 @@ func importApplicationV12(source map[string]interface{}) (*application, error) {
return importApplication(fields, defaults, 12, source)
}

func importApplicationV13(source map[string]interface{}) (*application, error) {
fields, defaults := applicationV13Fields()
return importApplication(fields, defaults, 13, source)
}

func importApplication(fields schema.Fields, defaults schema.Defaults, importVersion int, source map[string]interface{}) (*application, error) {
checker := schema.FieldMap(fields, defaults)

Expand Down Expand Up @@ -938,6 +990,24 @@ func importApplication(fields schema.Fields, defaults schema.Defaults, importVer

}

if importVersion >= 13 {
if charmMetadataMap, ok := valid["charm-metadata"]; ok {
charmMetadata, err := importCharmMetadata(charmMetadataMap.(map[string]interface{}))
if err != nil {
return nil, errors.Trace(err)
}
result.CharmMetadata_ = charmMetadata
}

if charmManifestMap, ok := valid["charm-manifest"]; ok {
charmManifest, err := importCharmManifest(charmManifestMap.(map[string]interface{}))
if err != nil {
return nil, errors.Trace(err)
}
result.CharmManifest_ = charmManifest
}
}

result.importAnnotations(valid)

if err := result.importStatusHistory(valid); err != nil {
Expand Down
20 changes: 18 additions & 2 deletions application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ func minimalApplicationMap() map[interface{}]interface{} {
minimalUnitMap(),
},
},
"charm-origin": minimalCharmOriginMap(),
"charm-origin": minimalCharmOriginMap(),
"charm-metadata": minimalCharmMetadataMap(),
"charm-manifest": minimalCharmManifestMap(),
}
}

Expand Down Expand Up @@ -104,6 +106,8 @@ func minimalApplicationMapCAAS() map[interface{}]interface{} {
result["tools"] = minimalAgentToolsMap()
result["operator-status"] = minimalStatusMap()
result["charm-origin"] = minimalCharmOriginMap()
result["charm-metadata"] = minimalCharmMetadataMap()
result["charm-manifest"] = minimalCharmManifestMap()
return result
}

Expand All @@ -124,6 +128,8 @@ func minimalApplication(args ...ApplicationArgs) *application {
u.SetTools(minimalAgentToolsArgs())
}
a.SetCharmOrigin(minimalCharmOriginArgs())
a.SetCharmMetadata(minimalCharmMetadataArgs())
a.SetCharmManifest(minimalCharmManifestArgs())
return a
}

Expand Down Expand Up @@ -362,7 +368,7 @@ func (s *ApplicationSerializationSuite) exportImportVersion(c *gc.C, application
}

func (s *ApplicationSerializationSuite) exportImportLatest(c *gc.C, application_ *application) *application {
return s.exportImportVersion(c, application_, 12)
return s.exportImportVersion(c, application_, 13)
}

func (s *ApplicationSerializationSuite) TestV1ParsingReturnsLatest(c *gc.C) {
Expand All @@ -383,6 +389,8 @@ func (s *ApplicationSerializationSuite) TestV1ParsingReturnsLatest(c *gc.C) {
appLatest.OperatorStatus_ = nil
appLatest.Offers_ = nil
appLatest.CharmOrigin_ = nil
appLatest.CharmMetadata_ = nil
appLatest.CharmManifest_ = nil

appResult := s.exportImportVersion(c, appV1, 1)
appLatest.Series_ = ""
Expand All @@ -406,6 +414,8 @@ func (s *ApplicationSerializationSuite) TestV2ParsingReturnsLatest(c *gc.C) {
appLatest.OperatorStatus_ = nil
appLatest.Offers_ = nil
appLatest.CharmOrigin_ = nil
appLatest.CharmMetadata_ = nil
appLatest.CharmManifest_ = nil

appResult := s.exportImportVersion(c, appV1, 2)
appLatest.Series_ = ""
Expand All @@ -425,6 +435,8 @@ func (s *ApplicationSerializationSuite) TestV3ParsingReturnsLatest(c *gc.C) {
appLatest.OperatorStatus_ = nil
appLatest.Offers_ = nil
appLatest.CharmOrigin_ = nil
appLatest.CharmMetadata_ = nil
appLatest.CharmManifest_ = nil

appResult := s.exportImportVersion(c, appV2, 3)
appLatest.Series_ = ""
Expand All @@ -440,6 +452,8 @@ func (s *ApplicationSerializationSuite) TestV5ParsingReturnsLatest(c *gc.C) {
appLatest := appV5
appLatest.HasResources_ = false
appLatest.CharmOrigin_ = nil
appLatest.CharmMetadata_ = nil
appLatest.CharmManifest_ = nil

appResult := s.exportImportVersion(c, appV5, 5)
appLatest.Series_ = ""
Expand All @@ -454,6 +468,8 @@ func (s *ApplicationSerializationSuite) TestV6ParsingReturnsLatest(c *gc.C) {
// Make an app with fields not in v6 removed.
appLatest := appV6
appLatest.CharmOrigin_ = nil
appLatest.CharmMetadata_ = nil
appLatest.CharmManifest_ = nil

appResult := s.exportImportVersion(c, appV6, 6)
appLatest.Series_ = ""
Expand Down
161 changes: 161 additions & 0 deletions charmmanifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright 2020 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package description

import (
"github.com/juju/errors"
"github.com/juju/schema"
)

// CharmManifestArgs is an argument struct used to create a new
// CharmManifest.
type CharmManifestArgs struct {
Bases []CharmManifestBase
}

func newCharmManifest(args CharmManifestArgs) *charmManifest {
var bases []charmManifestBase
if args.Bases != nil {
bases = make([]charmManifestBase, len(args.Bases))
for i, b := range args.Bases {
bases[i] = charmManifestBase{
Name_: b.Name(),
Channel_: b.Channel(),
Architectures_: b.Architectures(),
}
}
}

return &charmManifest{
Version_: 1,
Bases_: bases,
}
}

// charmManifest represents the metadata of a charm.
type charmManifest struct {
Version_ int `yaml:"version"`
Bases_ []charmManifestBase `yaml:"bases"`
}

// Bases returns the list of the base the charm supports.
func (m *charmManifest) Bases() []CharmManifestBase {
bases := make([]CharmManifestBase, len(m.Bases_))
for i, b := range m.Bases_ {
bases[i] = b
}
return bases
}

func importCharmManifest(source map[string]interface{}) (*charmManifest, error) {
version, err := getVersion(source)
if err != nil {
return nil, errors.Annotate(err, "charmManifest version schema check failed")
}

importFunc, ok := charmManifestDeserializationFuncs[version]
if !ok {
return nil, errors.NotValidf("version %d", version)
}

return importFunc(source)
}

type charmManifestDeserializationFunc func(map[string]interface{}) (*charmManifest, error)

var charmManifestDeserializationFuncs = map[int]charmManifestDeserializationFunc{
1: importCharmManifestV1,
}

func importCharmManifestV1(source map[string]interface{}) (*charmManifest, error) {
return importCharmManifestVersion(source, 1)
}

func importCharmManifestVersion(source map[string]interface{}, importVersion int) (*charmManifest, error) {
fields := schema.Fields{
"bases": schema.List(schema.Any()),
}
defaults := schema.Defaults{
"bases": schema.Omit,
}
checker := schema.FieldMap(fields, defaults)

coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "charmOrigin v1 schema check failed")
}
valid := coerced.(map[string]interface{})

var bases []charmManifestBase
if valid["bases"] != nil {
bases = make([]charmManifestBase, len(valid["bases"].([]interface{})))
for k, v := range valid["bases"].([]interface{}) {
var err error
bases[k], err = importCharmManifestBase(v, importVersion)
if err != nil {
return nil, errors.Annotate(err, "charmManifest bases schema check failed")
}
}
}

return &charmManifest{
Version_: 1,
Bases_: bases,
}, nil
}

func importCharmManifestBase(source interface{}, importVersion int) (charmManifestBase, error) {
fields := schema.Fields{
"name": schema.String(),
"channel": schema.String(),
"architectures": schema.List(schema.String()),
}
defaults := schema.Defaults{
"name": schema.Omit,
"channel": schema.Omit,
"architectures": schema.Omit,
}
checker := schema.FieldMap(fields, defaults)

coerced, err := checker.Coerce(source, nil)
if err != nil {
return charmManifestBase{}, errors.Annotate(err, "charmManifestBase schema check failed")
}
valid := coerced.(map[string]interface{})

var architectures []string
if valid["architectures"] != nil {
architectures = make([]string, len(valid["architectures"].([]interface{})))
for i, a := range valid["architectures"].([]interface{}) {
architectures[i] = a.(string)
}
}

return charmManifestBase{
Name_: valid["name"].(string),
Channel_: valid["channel"].(string),
Architectures_: architectures,
}, nil
}

type charmManifestBase struct {
Name_ string `yaml:"name"`
Channel_ string `yaml:"channel"`
Architectures_ []string `yaml:"architectures"`
}

// Name returns the name of the base.
func (r charmManifestBase) Name() string {
return r.Name_
}

// Channel returns the channel of the base.
func (r charmManifestBase) Channel() string {
return r.Channel_
}

// Architectures returns the architectures of the base.
func (r charmManifestBase) Architectures() []string {
return r.Architectures_
}
Loading

0 comments on commit dca60b8

Please sign in to comment.