Skip to content

Commit

Permalink
Merge pull request #128 from hpidcock/v3-provisioningstate
Browse files Browse the repository at this point in the history
Add provisioning-state onto application for CAAS sidecar
  • Loading branch information
hpidcock authored May 28, 2023
2 parents 2881212 + d908797 commit deffde3
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 19 deletions.
68 changes: 51 additions & 17 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type Application interface {
AddOffer(ApplicationOfferArgs) ApplicationOffer

Validate() error

ProvisioningState() ProvisioningState
}

// ExposedEndpoint encapsulates the details about the CIDRs and/or spaces that
Expand Down Expand Up @@ -125,14 +127,15 @@ type application struct {
StorageConstraints_ map[string]*storageconstraint `yaml:"storage-constraints,omitempty"`

// CAAS application fields.
PasswordHash_ string `yaml:"password-hash,omitempty"`
PodSpec_ string `yaml:"pod-spec,omitempty"`
Placement_ string `yaml:"placement,omitempty"`
HasResources_ bool `yaml:"has-resources,omitempty"`
DesiredScale_ int `yaml:"desired-scale,omitempty"`
CloudService_ *cloudService `yaml:"cloud-service,omitempty"`
Tools_ *agentTools `yaml:"tools,omitempty"`
OperatorStatus_ *status `yaml:"operator-status,omitempty"`
PasswordHash_ string `yaml:"password-hash,omitempty"`
PodSpec_ string `yaml:"pod-spec,omitempty"`
Placement_ string `yaml:"placement,omitempty"`
HasResources_ bool `yaml:"has-resources,omitempty"`
DesiredScale_ int `yaml:"desired-scale,omitempty"`
CloudService_ *cloudService `yaml:"cloud-service,omitempty"`
Tools_ *agentTools `yaml:"tools,omitempty"`
OperatorStatus_ *status `yaml:"operator-status,omitempty"`
ProvisioningState_ *provisioningState `yaml:"provisioning-state,omitempty"`

// Offer-related fields
Offers_ *applicationOffers `yaml:"offers,omitempty"`
Expand Down Expand Up @@ -168,6 +171,7 @@ type ApplicationArgs struct {
LeadershipSettings map[string]interface{}
StorageConstraints map[string]StorageConstraintArgs
MetricsCredentials []byte
ProvisioningState *ProvisioningStateArgs
}

func newApplication(args ApplicationArgs) *application {
Expand Down Expand Up @@ -196,6 +200,7 @@ func newApplication(args ApplicationArgs) *application {
LeadershipSettings_: args.LeadershipSettings,
MetricsCredentials_: creds,
StatusHistory_: newStatusHistory(),
ProvisioningState_: newProvisioningState(args.ProvisioningState),
}
app.setUnits(nil)
app.setResources(nil)
Expand Down Expand Up @@ -549,6 +554,14 @@ func (a *application) Validate() error {
return nil
}

// ProvisioningState implements Application.
func (a *application) ProvisioningState() ProvisioningState {
if a.ProvisioningState_ == nil {
return nil
}
return a.ProvisioningState_
}

func importApplications(source map[string]interface{}) ([]*application, error) {
checker := versionedChecker("applications")
coerced, err := checker.Coerce(source, nil)
Expand Down Expand Up @@ -585,15 +598,16 @@ func importApplicationList(sourceList []interface{}, importFunc applicationDeser
type applicationDeserializationFunc func(map[string]interface{}) (*application, error)

var applicationDeserializationFuncs = map[int]applicationDeserializationFunc{
1: importApplicationV1,
2: importApplicationV2,
3: importApplicationV3,
4: importApplicationV4,
5: importApplicationV5,
6: importApplicationV6,
7: importApplicationV7,
8: importApplicationV8,
9: importApplicationV9,
1: importApplicationV1,
2: importApplicationV2,
3: importApplicationV3,
4: importApplicationV4,
5: importApplicationV5,
6: importApplicationV6,
7: importApplicationV7,
8: importApplicationV8,
9: importApplicationV9,
10: importApplicationV10,
}

func applicationV1Fields() (schema.Fields, schema.Defaults) {
Expand Down Expand Up @@ -701,6 +715,13 @@ func applicationV9Fields() (schema.Fields, schema.Defaults) {
return fields, defaults
}

func applicationV10Fields() (schema.Fields, schema.Defaults) {
fields, defaults := applicationV9Fields()
fields["provisioning-state"] = schema.StringMap(schema.Any())
defaults["provisioning-state"] = 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 @@ -746,6 +767,11 @@ func importApplicationV9(source map[string]interface{}) (*application, error) {
return importApplication(fields, defaults, 9, source)
}

func importApplicationV10(source map[string]interface{}) (*application, error) {
fields, defaults := applicationV10Fields()
return importApplication(fields, defaults, 10, 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 @@ -823,6 +849,14 @@ func importApplication(fields schema.Fields, defaults schema.Defaults, importVer
}
}

if importVersion >= 10 {
if provisioningState, ok := valid["provisioning-state"].(map[string]interface{}); ok {
if result.ProvisioningState_, err = importProvisioningState(provisioningState); err != nil {
return nil, errors.Trace(err)
}
}
}

series, hasSeries := valid["series"].(string)
// If we have a series but no platform defined lets make a platform from the series
if hasSeries && (result.CharmOrigin_ == nil || result.CharmOrigin_.Platform_ == "") {
Expand Down
15 changes: 14 additions & 1 deletion application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ func (s *ApplicationSerializationSuite) exportImportVersion(c *gc.C, application
}

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

func (s *ApplicationSerializationSuite) TestV1ParsingReturnsLatest(c *gc.C) {
Expand Down Expand Up @@ -571,6 +571,19 @@ func (s *ApplicationSerializationSuite) TestDesiredScale(c *gc.C) {
c.Assert(application.DesiredScale(), gc.Equals, 3)
}

func (s *ApplicationSerializationSuite) TestProvisioningState(c *gc.C) {
args := minimalApplicationArgs(CAAS)
args.ProvisioningState = &ProvisioningStateArgs{
Scaling: true,
ScaleTarget: 10,
}
initial := minimalApplication(args)

application := s.exportImportLatest(c, initial)
c.Assert(application.ProvisioningState().Scaling(), jc.IsTrue)
c.Assert(application.ProvisioningState().ScaleTarget(), gc.Equals, 10)
}

func (s *ApplicationSerializationSuite) TestCloudService(c *gc.C) {
args := minimalApplicationArgs(CAAS)
initial := minimalApplication(args)
Expand Down
2 changes: 1 addition & 1 deletion model.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ func (m *model) AddApplication(args ApplicationArgs) Application {

func (m *model) setApplications(applicationList []*application) {
m.Applications_ = applications{
Version: 9,
Version: 10,
Applications_: applicationList,
}
}
Expand Down
96 changes: 96 additions & 0 deletions provisioningstate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2023 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package description

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

type ProvisioningState interface {
Scaling() bool
ScaleTarget() int
}

type provisioningState struct {
Version_ int `yaml:"version"`
Scaling_ bool `yaml:"scaling"`
ScaleTarget_ int `yaml:"scale-target"`
}

func (i *provisioningState) Scaling() bool {
return i.Scaling_
}

func (i *provisioningState) ScaleTarget() int {
return i.ScaleTarget_
}

// ProvisioningStateArgs is an argument struct used to create a
// new internal provisioningState type that supports the ProvisioningState interface.
type ProvisioningStateArgs struct {
Scaling bool
ScaleTarget int
}

func newProvisioningState(args *ProvisioningStateArgs) *provisioningState {
if args == nil {
return nil
}
return &provisioningState{
Version_: 1,
Scaling_: args.Scaling,
ScaleTarget_: args.ScaleTarget,
}
}

func importProvisioningState(source map[string]interface{}) (*provisioningState, error) {
version, err := getVersion(source)
if err != nil {
return nil, errors.Annotate(err, "provisioning-state version schema check failed")
}
importFunc, ok := provisioningStateDeserializationFuncs[version]
if !ok {
return nil, errors.NotValidf("version %d", version)
}
return importFunc(source)
}

type provisioningStateDeserializationFunc func(map[string]interface{}) (*provisioningState, error)

var provisioningStateDeserializationFuncs = map[int]provisioningStateDeserializationFunc{
1: importProvisioningStateV1,
}

func importProvisioningStateV1(source map[string]interface{}) (*provisioningState, error) {
fields, defaults := provisioningStateV1Schema()
checker := schema.FieldMap(fields, defaults)

coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "provisioning-state v1 schema check failed")
}

return provisioningStateV1(coerced.(map[string]interface{})), nil
}

func provisioningStateV1Schema() (schema.Fields, schema.Defaults) {
fields := schema.Fields{
"scaling": schema.Bool(),
"scale-target": schema.Int(),
}
defaults := schema.Defaults{
"scaling": false,
"scale-target": 0,
}
return fields, defaults
}

func provisioningStateV1(valid map[string]interface{}) *provisioningState {
return &provisioningState{
Version_: 1,
Scaling_: valid["scaling"].(bool),
ScaleTarget_: int(valid["scale-target"].(int64)),
}
}
119 changes: 119 additions & 0 deletions provisioningstate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2023 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package description

import (
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"gopkg.in/yaml.v2"
)

type ProvisioningStateSerializationSuite struct {
SerializationSuite
}

var _ = gc.Suite(&ProvisioningStateSerializationSuite{})

func (s *ProvisioningStateSerializationSuite) SetUpTest(c *gc.C) {
s.importName = "provisioning-state"
s.importFunc = func(m map[string]interface{}) (interface{}, error) {
return importProvisioningState(m)
}
}

func (s *ProvisioningStateSerializationSuite) TestNewProvisioningState(c *gc.C) {
args := ProvisioningStateArgs{
Scaling: true,
ScaleTarget: 10,
}
instance := newProvisioningState(&args)
c.Assert(instance.Scaling(), jc.IsTrue)
c.Assert(instance.ScaleTarget(), gc.Equals, 10)
}

func minimalProvisioningStateMap() map[interface{}]interface{} {
return map[interface{}]interface{}{
"version": 1,
"scaling": true,
"scale-target": 10,
}
}

func minimalProvisioningStateArgs() *ProvisioningStateArgs {
return &ProvisioningStateArgs{
Scaling: true,
ScaleTarget: 10,
}
}

func minimalProvisioningState() *provisioningState {
return newProvisioningState(minimalProvisioningStateArgs())
}

func maximalProvisioningStateMap() map[interface{}]interface{} {
return map[interface{}]interface{}{
"version": 1,
"scaling": true,
"scale-target": 10,
}
}

func maximalProvisioningStateArgs() *ProvisioningStateArgs {
return &ProvisioningStateArgs{
Scaling: true,
ScaleTarget: 10,
}
}

func maximalProvisioningState() *provisioningState {
return newProvisioningState(maximalProvisioningStateArgs())
}

func (s *ProvisioningStateSerializationSuite) TestMinimalMatches(c *gc.C) {
bytes, err := yaml.Marshal(minimalProvisioningState())
c.Assert(err, jc.ErrorIsNil)

var source map[interface{}]interface{}
err = yaml.Unmarshal(bytes, &source)
c.Assert(err, jc.ErrorIsNil)
c.Assert(source, jc.DeepEquals, minimalProvisioningStateMap())
}

func (s *ProvisioningStateSerializationSuite) TestMaximalMatches(c *gc.C) {
bytes, err := yaml.Marshal(maximalProvisioningState())
c.Assert(err, jc.ErrorIsNil)

var source map[interface{}]interface{}
err = yaml.Unmarshal(bytes, &source)
c.Assert(err, jc.ErrorIsNil)
c.Assert(source, jc.DeepEquals, maximalProvisioningStateMap())
}

func (s *ProvisioningStateSerializationSuite) TestParsingSerializedData(c *gc.C) {
initial := maximalProvisioningState()
bytes, err := yaml.Marshal(initial)
c.Assert(err, jc.ErrorIsNil)

var source map[string]interface{}
err = yaml.Unmarshal(bytes, &source)
c.Assert(err, jc.ErrorIsNil)

instance, err := importProvisioningState(source)
c.Assert(err, jc.ErrorIsNil)
c.Assert(instance, jc.DeepEquals, initial)
}

func (s *ProvisioningStateSerializationSuite) exportImportVersion(c *gc.C, origin_ *provisioningState, version int) *provisioningState {
origin_.Version_ = version
bytes, err := yaml.Marshal(origin_)
c.Assert(err, jc.ErrorIsNil)

var source map[string]interface{}
err = yaml.Unmarshal(bytes, &source)
c.Assert(err, jc.ErrorIsNil)

origin, err := importProvisioningState(source)
c.Assert(err, jc.ErrorIsNil)
return origin
}

0 comments on commit deffde3

Please sign in to comment.