Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extra description fields for controller-persisted unit state data #75

Merged
merged 1 commit into from Apr 6, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 115 additions & 19 deletions unit.go
Expand Up @@ -9,10 +9,30 @@ import (
"gopkg.in/juju/names.v3"
)

// UnitStateGetSetter describes the state-related operations that can be
// performed against an instance of a unit in a model.
type UnitStateGetSetter interface {
CharmState() map[string]string
SetCharmState(map[string]string)

RelationState() map[int]string
SetRelationState(map[int]string)

UniterState() string
SetUniterState(string)

StorageState() string
SetStorageState(string)

MeterStatusState() string
SetMeterStatusState(string)
}

// Unit represents an instance of a unit in a model.
type Unit interface {
HasAnnotations
HasConstraints
UnitStateGetSetter

Tag() names.UnitTag
Name() string
Expand Down Expand Up @@ -56,9 +76,6 @@ type Unit interface {
CloudContainer() CloudContainer
SetCloudContainer(CloudContainerArgs)

State() map[string]string
SetState(map[string]string)

Validate() error
}

Expand Down Expand Up @@ -102,7 +119,11 @@ type unit struct {

CloudContainer_ *cloudContainer `yaml:"cloud-container,omitempty"`

State_ map[string]string `yaml:"state,omitempty"`
CharmState_ map[string]string `yaml:"charm-state,omitempty"`
RelationState_ map[int]string `yaml:"relation-state,omitempty"`
UniterState_ string `yaml:"uniter-state,omitempty"`
StorageState_ string `yaml:"storage-state,omitempty"`
MeterStatusState_ string `yaml:"meter-status-state,omitempty"`
}

// UnitArgs is an argument struct used to add a Unit to a Application in the Model.
Expand All @@ -120,7 +141,11 @@ type UnitArgs struct {

CloudContainer *CloudContainerArgs

State map[string]string
CharmState map[string]string
RelationState map[int]string
UniterState string
StorageState string
MeterStatusState string

// TODO: storage attachment count
}
Expand All @@ -144,7 +169,11 @@ func newUnit(args UnitArgs) *unit {
WorkloadStatusHistory_: newStatusHistory(),
WorkloadVersionHistory_: newStatusHistory(),
AgentStatusHistory_: newStatusHistory(),
State_: args.State,
CharmState_: args.CharmState,
RelationState_: args.RelationState,
UniterState_: args.UniterState,
StorageState_: args.StorageState,
MeterStatusState_: args.MeterStatusState,
}
u.setResources(nil)
u.setPayloads(nil)
Expand Down Expand Up @@ -352,14 +381,54 @@ func (u *unit) setPayloads(payloadList []*payload) {
}
}

// State implements Unit.
func (u *unit) State() map[string]string {
return u.State_
// CharmState implements Unit.
func (u *unit) CharmState() map[string]string {
return u.CharmState_
}

// SetCharmState implements Unit.
func (u *unit) SetCharmState(st map[string]string) {
u.CharmState_ = st
}

// RelationState implements Unit.
func (u *unit) RelationState() map[int]string {
return u.RelationState_
}

// SetRelationState implements Unit.
func (u *unit) SetRelationState(st map[int]string) {
u.RelationState_ = st
}

// UniterState implements Unit.
func (u *unit) UniterState() string {
return u.UniterState_
}

// SetState implements Unit.
func (u *unit) SetState(st map[string]string) {
u.State_ = st
// SetUniterState implements Unit.
func (u *unit) SetUniterState(st string) {
u.UniterState_ = st
}

// StorageState implements Unit.
func (u *unit) StorageState() string {
return u.StorageState_
}

// SetStorageState implements Unit.
func (u *unit) SetStorageState(st string) {
u.StorageState_ = st
}

// MeterStatusState implements Unit.
func (u *unit) MeterStatusState() string {
return u.MeterStatusState_
}

// SetMeterStatusState implements Unit.
func (u *unit) SetMeterStatusState(st string) {
u.MeterStatusState_ = st
}

// Validate implements Unit.
Expand Down Expand Up @@ -466,8 +535,17 @@ func unitV2Fields() (schema.Fields, schema.Defaults) {

func unitV3Fields() (schema.Fields, schema.Defaults) {
fields, defaults := unitV2Fields()
fields["state"] = schema.StringMap(schema.String())
defaults["state"] = schema.Omit
fields["charm-state"] = schema.StringMap(schema.String())
fields["relation-state"] = schema.Map(schema.Int(), schema.String())
fields["uniter-state"] = schema.String()
fields["storage-state"] = schema.String()
fields["meter-status-state"] = schema.String()

defaults["charm-state"] = schema.Omit
defaults["relation-state"] = schema.Omit
defaults["uniter-state"] = schema.Omit
defaults["storage-state"] = schema.Omit
defaults["meter-status-state"] = schema.Omit
return fields, defaults
}

Expand Down Expand Up @@ -580,12 +658,30 @@ func importUnit(fields schema.Fields, defaults schema.Defaults, importVersion in
}
result.setPayloads(payloads)

if stateCoercedMap, ok := valid["state"].(map[string]interface{}); ok {
stateMap := make(map[string]string, len(stateCoercedMap))
for k, v := range stateCoercedMap {
stateMap[k] = v.(string)
if charmStateCoercedMap, ok := valid["charm-state"].(map[string]interface{}); ok {
charmStateMap := make(map[string]string, len(charmStateCoercedMap))
for k, v := range charmStateCoercedMap {
charmStateMap[k] = v.(string)
}
result.SetState(stateMap)
result.SetCharmState(charmStateMap)
}

if relationStateCoercedMap, ok := valid["relation-state"].(map[interface{}]interface{}); ok {
relationStateMap := make(map[int]string, len(relationStateCoercedMap))
for k, v := range relationStateCoercedMap {
relationStateMap[int(k.(int64))] = v.(string)
}
result.SetRelationState(relationStateMap)
}

if v := valid["uniter-state"]; v != nil {
result.SetUniterState(v.(string))
}
if v := valid["storage-state"]; v != nil {
result.SetStorageState(v.(string))
}
if v := valid["meter-status-state"]; v != nil {
result.SetMeterStatusState(v.(string))
}

return result, nil
Expand Down
96 changes: 83 additions & 13 deletions unit_test.go
Expand Up @@ -47,9 +47,16 @@ func minimalUnitMap() map[interface{}]interface{} {
"version": 1,
"payloads": []interface{}{},
},
"state": map[interface{}]interface{}{
"charm-state": "0xbadc0ffee",
"charm-state": map[interface{}]interface{}{
"some-charm-key": "0xbadc0ffee",
},
"relation-state": map[interface{}]interface{}{
1: "yaml-encoded state for relation 1",
2: "yaml-encoded state for relation 2",
},
"uniter-state": "yaml-encoded state for uniter",
"storage-state": "yaml-encoded state for storage",
"meter-status-state": "yaml-encoded state for meter status worker",
}
}

Expand Down Expand Up @@ -92,9 +99,16 @@ func minimalUnitArgs(modelType string) UnitArgs {
Type: modelType,
Machine: names.NewMachineTag("0"),
PasswordHash: "secure-hash",
State: map[string]string{
"charm-state": "0xbadc0ffee",
CharmState: map[string]string{
"some-charm-key": "0xbadc0ffee",
},
RelationState: map[int]string{
1: "yaml-encoded state for relation 1",
2: "yaml-encoded state for relation 2",
},
UniterState: "yaml-encoded state for uniter",
StorageState: "yaml-encoded state for storage",
MeterStatusState: "yaml-encoded state for meter status worker",
}
if modelType == CAAS {
result.CloudContainer = &CloudContainerArgs{
Expand Down Expand Up @@ -128,9 +142,16 @@ func (s *UnitSerializationSuite) completeUnit() *unit {
unit.SetWorkloadStatus(minimalStatusArgs())
unit.SetTools(minimalAgentToolsArgs())
unit.SetCloudContainer(minimalCloudContainerArgs())
unit.SetState(map[string]string{
"charm-state": "0xbadc0ffee",
unit.SetCharmState(map[string]string{
"some-charm-key": "0xbadc0ffee",
})
unit.SetRelationState(map[int]string{
1: "yaml-encoded state for relation 1",
2: "yaml-encoded state for relation 2",
})
unit.SetUniterState("yaml-encoded state for uniter")
unit.SetStorageState("yaml-encoded state for storage")
unit.SetMeterStatusState("yaml-encoded state for meter status worker")
return unit
}

Expand All @@ -153,9 +174,16 @@ func (s *UnitSerializationSuite) TestNewUnit(c *gc.C) {
c.Assert(unit.WorkloadStatus(), gc.NotNil)
c.Assert(unit.AgentStatus(), gc.NotNil)
c.Assert(unit.CloudContainer(), gc.NotNil)
c.Assert(unit.State(), gc.DeepEquals, map[string]string{
"charm-state": "0xbadc0ffee",
c.Assert(unit.CharmState(), gc.DeepEquals, map[string]string{
"some-charm-key": "0xbadc0ffee",
})
c.Assert(unit.RelationState(), gc.DeepEquals, map[int]string{
1: "yaml-encoded state for relation 1",
2: "yaml-encoded state for relation 2",
})
c.Assert(unit.UniterState(), gc.Equals, "yaml-encoded state for uniter")
c.Assert(unit.StorageState(), gc.Equals, "yaml-encoded state for storage")
c.Assert(unit.MeterStatusState(), gc.Equals, "yaml-encoded state for meter status worker")
}

func (s *UnitSerializationSuite) TestMinimalUnitValid(c *gc.C) {
Expand Down Expand Up @@ -218,7 +246,11 @@ func (s *UnitSerializationSuite) TestV1ParsingReturnsLatest(c *gc.C) {
unitLatest := minimalUnit()
unitLatest.CloudContainer_ = nil
unitLatest.Type_ = ""
unitLatest.State_ = nil
unitLatest.CharmState_ = nil
unitLatest.RelationState_ = nil
unitLatest.UniterState_ = ""
unitLatest.StorageState_ = ""
unitLatest.MeterStatusState_ = ""

unitResult := s.exportImportVersion(c, unitV1, 1)
c.Assert(unitResult, jc.DeepEquals, unitLatest)
Expand All @@ -229,7 +261,11 @@ func (s *UnitSerializationSuite) TestV2ParsingReturnsLatest(c *gc.C) {

// Make a unit with fields not in v2 removed.
unitLatest := s.completeUnit()
unitLatest.State_ = nil
unitLatest.CharmState_ = nil
unitLatest.RelationState_ = nil
unitLatest.UniterState_ = ""
unitLatest.StorageState_ = ""
unitLatest.MeterStatusState_ = ""

unitResult := s.exportImportVersion(c, unitV2, 2)
c.Assert(unitResult, jc.DeepEquals, unitLatest)
Expand Down Expand Up @@ -273,14 +309,48 @@ func (s *UnitSerializationSuite) TestCloudContainer(c *gc.C) {
c.Assert(unit.CloudContainer(), jc.DeepEquals, newCloudContainer(&args))
}

func (s *UnitSerializationSuite) TestState(c *gc.C) {
func (s *UnitSerializationSuite) TestCharmState(c *gc.C) {
initial := minimalUnit()
initial.SetState(map[string]string{
initial.SetCharmState(map[string]string{
"foo": "bar",
})

unit := s.exportImportLatest(c, initial)
c.Assert(unit.State(), jc.DeepEquals, initial.State())
c.Assert(unit.CharmState(), jc.DeepEquals, initial.CharmState())
}

func (s *UnitSerializationSuite) TestRelationState(c *gc.C) {
initial := minimalUnit()
initial.SetRelationState(map[int]string{
42: "random",
})

unit := s.exportImportLatest(c, initial)
c.Assert(unit.RelationState(), jc.DeepEquals, initial.RelationState())
}

func (s *UnitSerializationSuite) TestUniterState(c *gc.C) {
initial := minimalUnit()
initial.SetUniterState("my new uniter state")

unit := s.exportImportLatest(c, initial)
c.Assert(unit.UniterState(), jc.DeepEquals, initial.UniterState())
}

func (s *UnitSerializationSuite) TestStorageState(c *gc.C) {
initial := minimalUnit()
initial.SetStorageState("my new storage state")

unit := s.exportImportLatest(c, initial)
c.Assert(unit.StorageState(), jc.DeepEquals, initial.StorageState())
}

func (s *UnitSerializationSuite) TestMeterStatusState(c *gc.C) {
initial := minimalUnit()
initial.SetMeterStatusState("my new meter status state")

unit := s.exportImportLatest(c, initial)
c.Assert(unit.MeterStatusState(), jc.DeepEquals, initial.MeterStatusState())
}

func (s *UnitSerializationSuite) TestCAASUnitNoTools(c *gc.C) {
Expand Down