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
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