Skip to content

Commit

Permalink
Merge pull request #139 from juju/add-uuid-spaces
Browse files Browse the repository at this point in the history
Add UUID fields into spaces and subnets
  • Loading branch information
nvinuesa authored Apr 15, 2024
2 parents d734bcd + 5488033 commit 8c1d144
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 59 deletions.
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"`
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) {
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

0 comments on commit 8c1d144

Please sign in to comment.