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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PROJECT := github.com/juju/description/v5
PROJECT := github.com/juju/description/v6

.PHONY: check-licence check-go check

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/juju/description/v5
module github.com/juju/description/v6

go 1.21

Expand Down
3 changes: 3 additions & 0 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type AgentTools interface {
// Space represents a network space, which is a named collection of subnets.
type Space interface {
Id() string
UUID() string
Name() string
Public() bool
ProviderID() string
Expand Down Expand Up @@ -204,6 +205,7 @@ type StorageInstanceConstraints struct {
// Subnet represents a network subnet.
type Subnet interface {
ID() string
UUID() string
ProviderId() string
ProviderNetworkId() string
ProviderSpaceId() string
Expand All @@ -212,6 +214,7 @@ type Subnet interface {
AvailabilityZones() []string
IsPublic() bool
SpaceID() string
SpaceUUID() string
SpaceName() string
FanLocalUnderlay() string
FanOverlay() string
Expand Down
12 changes: 6 additions & 6 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ func (m *model) AddSpace(args SpaceArgs) Space {

func (m *model) setSpaces(spaceList []*space) {
m.Spaces_ = spaces{
Version: 2,
Version: 3,
Spaces_: spaceList,
}
}
Expand Down Expand Up @@ -1234,18 +1234,18 @@ func (m *model) validateStorage(validationCtx *validationContext) error {

// validateSubnets makes sure that any spaces referenced by subnets exist.
func (m *model) validateSubnets() error {
spaceIDs := set.NewStrings()
spaceUUIDs := set.NewStrings()
for _, space := range m.Spaces_.Spaces_ {
spaceIDs.Add(space.Id())
spaceUUIDs.Add(space.UUID())
}
for _, subnet := range m.Subnets_.Subnets_ {
// space "0" is the new, in juju 2.7, default space,
// created with each new model.
if subnet.SpaceID() == "" || subnet.SpaceID() == "0" {
if subnet.SpaceUUID() == "" || subnet.SpaceUUID() == "0" {
continue
}
if !spaceIDs.Contains(subnet.SpaceID()) {
return errors.Errorf("subnet %q references non-existent space %q", subnet.CIDR(), subnet.SpaceID())
if !spaceUUIDs.Contains(subnet.SpaceUUID()) {
return errors.Errorf("subnet %q references non-existent space %q", subnet.CIDR(), subnet.SpaceUUID())
}
}

Expand Down
12 changes: 6 additions & 6 deletions model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (s *ModelSerializationSuite) TestVersions(c *gc.C) {
c.Assert(initial.Relations_.Version, gc.Equals, len(relationFieldsFuncs))
c.Assert(initial.RemoteEntities_.Version, gc.Equals, len(remoteEntityFieldsFuncs))
c.Assert(initial.RemoteApplications_.Version, gc.Equals, len(remoteApplicationFieldsFuncs))
c.Assert(initial.Spaces_.Version, gc.Equals, len(spaceDeserializationFuncs))
c.Assert(initial.Spaces_.Version, gc.Equals, len(spaceFieldsFuncs))
c.Assert(initial.Volumes_.Version, gc.Equals, len(volumeDeserializationFuncs))
c.Assert(initial.FirewallRules_.Version, gc.Equals, len(firewallRuleFieldsFuncs))
c.Assert(initial.OfferConnections_.Version, gc.Equals, len(offerConnectionDeserializationFuncs))
Expand Down Expand Up @@ -594,11 +594,11 @@ func (s *ModelSerializationSuite) TestModelSerializationWithRelationNetworks(c *

func (s *ModelSerializationSuite) TestModelValidationChecksSubnets(c *gc.C) {
model := s.newModel(ModelArgs{Owner: names.NewUserTag("owner")})
model.AddSubnet(SubnetArgs{CIDR: "10.0.0.0/24", SpaceID: "3"})
model.AddSubnet(SubnetArgs{CIDR: "10.0.0.0/24", SpaceUUID: "deadbeef-1bad-500d-9000-4b1d0d06f00d"})
model.AddSubnet(SubnetArgs{CIDR: "10.0.1.0/24"})
err := model.Validate()
c.Assert(err, gc.ErrorMatches, `subnet "10.0.0.0/24" references non-existent space "3"`)
model.AddSpace(SpaceArgs{Id: "3"})
c.Assert(err, gc.ErrorMatches, `subnet "10.0.0.0/24" references non-existent space "deadbeef-1bad-500d-9000-4b1d0d06f00d"`)
model.AddSpace(SpaceArgs{UUID: "deadbeef-1bad-500d-9000-4b1d0d06f00d"})
err = model.Validate()
c.Assert(err, jc.ErrorIsNil)
}
Expand Down Expand Up @@ -1206,9 +1206,9 @@ func (s *ModelSerializationSuite) TestVersion1IgnoresRemoteApplications(c *gc.C)

func (s *ModelSerializationSuite) TestSpaces(c *gc.C) {
initial := s.newModel(ModelArgs{Owner: names.NewUserTag("owner")})
space := initial.AddSpace(SpaceArgs{Id: "1", Name: "special"})
space := initial.AddSpace(SpaceArgs{UUID: "deadbeef-1bad-500d-9000-4b1d0d06f00d", Name: "special"})
c.Assert(space.Name(), gc.Equals, "special")
c.Assert(space.Id(), gc.Equals, "1")
c.Assert(space.UUID(), gc.Equals, "deadbeef-1bad-500d-9000-4b1d0d06f00d")

spaces := initial.Spaces()
c.Assert(spaces, gc.HasLen, 1)
Expand Down
2 changes: 1 addition & 1 deletion package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var _ = gc.Suite(&ImportTest{})

func (*ImportTest) TestImports(c *gc.C) {
imps, err := jtesting.FindImports(
"github.com/juju/description/v5",
"github.com/juju/description/v6",
"github.com/juju/juju/")
c.Assert(err, jc.ErrorIsNil)
// This package brings in nothing else from juju/juju
Expand Down
2 changes: 2 additions & 0 deletions remoteapplication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ func minimalRemoteApplicationMapWithoutStatus() map[interface{}]interface{} {
"subnets": []interface{}{map[interface{}]interface{}{
"cidr": "2.3.4.0/24",
"subnet-id": "",
"uuid": "",
"is-public": false,
"space-id": "",
"space-uuid": "",
"space-name": "",
"vlan-tag": 0,
"provider-id": "juju-subnet-1",
Expand Down
2 changes: 2 additions & 0 deletions remotespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ func minimalRemoteSpaceMap() map[interface{}]interface{} {
"subnets": []interface{}{map[interface{}]interface{}{
"cidr": "2.3.4.0/24",
"subnet-id": "",
"uuid": "",
"is-public": false,
"space-id": "",
"space-uuid": "",
"space-name": "a-space",
"vlan-tag": 64,
"provider-id": "juju-subnet-1",
Expand Down
92 changes: 48 additions & 44 deletions space.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type spaces struct {

type space struct {
Id_ string `yaml:"id"`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the addition of uuid, can't we do away with Id now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

answered below

UUID_ string `yaml:"uuid"`
Name_ string `yaml:"name"`
Public_ bool `yaml:"public"`
ProviderID_ string `yaml:"provider-id,omitempty"`
Expand All @@ -24,6 +25,7 @@ type space struct {
// type that supports the Space interface.
type SpaceArgs struct {
Id string
UUID string
Name string
Public bool
ProviderID string
Expand All @@ -32,6 +34,7 @@ type SpaceArgs struct {
func newSpace(args SpaceArgs) *space {
return &space{
Id_: args.Id,
UUID_: args.UUID,
Name_: args.Name,
Public_: args.Public,
ProviderID_: args.ProviderID,
Expand All @@ -43,6 +46,11 @@ func (s *space) Id() string {
return s.Id_
}

// UUID implements Space.
func (s *space) UUID() string {
return s.UUID_
}

// Name implements Space.
func (s *space) Name() string {
return s.Name_
Expand All @@ -67,22 +75,28 @@ func importSpaces(source map[string]interface{}) ([]*space, error) {
valid := coerced.(map[string]interface{})

version := int(valid["version"].(int64))
importFunc, ok := spaceDeserializationFuncs[version]
getFields, ok := spaceFieldsFuncs[version]
if !ok {
return nil, errors.NotValidf("version %d", version)
}
sourceList := valid["spaces"].([]interface{})
return importSpaceList(sourceList, importFunc)
return importSpaceList(sourceList, schema.FieldMap(getFields()), version)
}

func importSpaceList(sourceList []interface{}, importFunc spaceDeserializationFunc) ([]*space, error) {
func importSpaceList(sourceList []interface{}, checker schema.Checker, version int) ([]*space, error) {
result := make([]*space, 0, len(sourceList))
for i, value := range sourceList {
source, ok := value.(map[string]interface{})
if !ok {
return nil, errors.Errorf("unexpected value for space %d, %T", i, value)
}
space, err := importFunc(source)
coerced, err := checker.Coerce(source, nil)

if err != nil {
return nil, errors.Annotatef(err, "space %d v%d schema check failed", i, version)
}
valid := coerced.(map[string]interface{})
space, err := newSpaceFromValid(valid, version)
if err != nil {
return nil, errors.Annotatef(err, "space %d", i)
}
Expand All @@ -91,51 +105,26 @@ func importSpaceList(sourceList []interface{}, importFunc spaceDeserializationFu
return result, nil
}

type spaceDeserializationFunc func(map[string]interface{}) (*space, error)

var spaceDeserializationFuncs = map[int]spaceDeserializationFunc{
1: importSpaceV1,
2: importSpaceV2,
}

func importSpaceV1(source map[string]interface{}) (*space, error) {
fields, defaults := spaceV1Fields()
checker := schema.FieldMap(fields, defaults)

coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "space v1 schema check failed")
}
valid := coerced.(map[string]interface{})
// From here we know that the map returned from the schema coercion
// contains fields of the right type.

return &space{
func newSpaceFromValid(valid map[string]interface{}, version int) (*space, error) {
result := space{
Name_: valid["name"].(string),
Public_: valid["public"].(bool),
ProviderID_: valid["provider-id"].(string),
}, nil
}

func importSpaceV2(source map[string]interface{}) (*space, error) {
fields, defaults := spaceV1Fields()
fields["id"] = schema.String()
checker := schema.FieldMap(fields, defaults)

coerced, err := checker.Coerce(source, nil)
if err != nil {
return nil, errors.Annotatef(err, "space v2 schema check failed")
}
valid := coerced.(map[string]interface{})
// From here we know that the map returned from the schema coercion
// contains fields of the right type.
// id was added in V2 and removed in V3.
if version == 2 {
result.Id_ = valid["id"].(string)
}
if version >= 3 {
result.UUID_ = valid["uuid"].(string)
}
return &result, nil
}

return &space{
Id_: valid["id"].(string),
Name_: valid["name"].(string),
Public_: valid["public"].(bool),
ProviderID_: valid["provider-id"].(string),
}, nil
var spaceFieldsFuncs = map[int]fieldsFunc{
1: spaceV1Fields,
2: spaceV2Fields,
3: spaceV3Fields,
}

func spaceV1Fields() (schema.Fields, schema.Defaults) {
Expand All @@ -150,3 +139,18 @@ func spaceV1Fields() (schema.Fields, schema.Defaults) {
}
return fields, defaults
}

func spaceV2Fields() (schema.Fields, schema.Defaults) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this method is added.
But we still don't use it, eg we still have this code in import v1

	fields, defaults := spaceV1Fields()
	fields["id"] = schema.String()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, added V3 and used this func

fields, defaults := spaceV1Fields()
fields["id"] = schema.String()

return fields, defaults
}

func spaceV3Fields() (schema.Fields, schema.Defaults) {
fields, defaults := spaceV2Fields()
fields["uuid"] = schema.String()
delete(fields, "id")

return fields, defaults
}
28 changes: 28 additions & 0 deletions space_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func (s *SpaceSerializationSuite) TestNewSpace(c *gc.C) {
}
space := newSpace(args)
c.Assert(space.Id(), gc.Equals, "")
c.Assert(space.UUID(), gc.Equals, "")
c.Assert(space.Name(), gc.Equals, args.Name)
c.Assert(space.Public(), gc.Equals, args.Public)
c.Assert(space.ProviderID(), gc.Equals, args.ProviderID)
Expand Down Expand Up @@ -92,3 +93,30 @@ func (s *SpaceSerializationSuite) TestParsingSerializedDataV2(c *gc.C) {

c.Assert(spaces, jc.DeepEquals, initial.Spaces_)
}

func (s *SpaceSerializationSuite) TestParsingSerializedDataV3(c *gc.C) {
initial := spaces{
Version: 3,
Spaces_: []*space{
newSpace(SpaceArgs{
UUID: "018ea48e-c6a6-7d51-ae76-9bfee4a6b6dd",
Name: "special",
Public: true,
ProviderID: "magic",
}),
newSpace(SpaceArgs{Name: "foo"}),
},
}

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)

spaces, err := importSpaces(source)
c.Assert(err, jc.ErrorIsNil)

c.Assert(spaces, jc.DeepEquals, initial.Spaces_)
}
Loading