Skip to content

Commit

Permalink
Add ID fields to Resource Objects for easy lookup and validations
Browse files Browse the repository at this point in the history
  • Loading branch information
mgleung committed Jul 24, 2017
1 parent 521dd1c commit c048bde
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/api/bgppeer.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func (t BGPPeer) GetResourceMetadata() unversioned.ResourceMetadata {
return t.Metadata
}

func (t BGPPeer) GetResourceID() string {
return t.Metadata.PeerIP.IP.String() + "_" + string(t.Metadata.Scope) + "_" + t.Metadata.Node
}

// BGPPeerMetadata contains the metadata for a BGPPeer resource.
type BGPPeerMetadata struct {
unversioned.ObjectMetadata
Expand Down
4 changes: 4 additions & 0 deletions lib/api/hostendpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ func (t HostEndpoint) GetResourceMetadata() unversioned.ResourceMetadata {
return t.Metadata
}

func (t HostEndpoint) GetResourceID() string {
return t.Metadata.Node + "_" + t.Metadata.Name
}

// HostEndpointMetadata contains the Metadata for a HostEndpoint resource.
type HostEndpointMetadata struct {
unversioned.ObjectMetadata
Expand Down
4 changes: 4 additions & 0 deletions lib/api/ippool.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func (t IPPool) GetResourceMetadata() unversioned.ResourceMetadata {
return t.Metadata
}

func (t IPPool) GetResourceID() string {
return t.Metadata.CIDR.String()
}

// IPPoolMetadata contains the metadata for an IP pool resource.
type IPPoolMetadata struct {
unversioned.ObjectMetadata
Expand Down
4 changes: 4 additions & 0 deletions lib/api/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func (t Node) GetResourceMetadata() unversioned.ResourceMetadata {
return t.Metadata
}

func (t Node) GetResourceID() string {
return t.Metadata.Name
}

// NodeMetadata contains the metadata for a Calico Node resource.
type NodeMetadata struct {
unversioned.ObjectMetadata
Expand Down
4 changes: 4 additions & 0 deletions lib/api/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func (t Policy) GetResourceMetadata() unversioned.ResourceMetadata {
return t.Metadata
}

func (t Policy) GetResourceID() string {
return t.Metadata.Name
}

// PolicyMetadata contains the metadata for a selector-based security Policy resource.
type PolicyMetadata struct {
unversioned.ObjectMetadata
Expand Down
4 changes: 4 additions & 0 deletions lib/api/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func (t Profile) GetResourceMetadata() unversioned.ResourceMetadata {
return t.Metadata
}

func (t Profile) GetResourceID() string {
return t.Metadata.Name
}

// ProfileMetadata contains the metadata for a security Profile resource.
type ProfileMetadata struct {
unversioned.ObjectMetadata
Expand Down
1 change: 1 addition & 0 deletions lib/api/unversioned/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Resource interface {
type ResourceObject interface {
Resource
GetResourceMetadata() ResourceMetadata
GetResourceID() string
}

// Define available versions.
Expand Down
4 changes: 4 additions & 0 deletions lib/api/workloadendpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ func (t WorkloadEndpoint) GetResourceMetadata() unversioned.ResourceMetadata {
return t.Metadata
}

func (t WorkloadEndpoint) GetResourceID() string {
return t.Metadata.Node + "_" + t.Metadata.Orchestrator + "_" + t.Metadata.Workload + "_" + t.Metadata.Name + "_" + t.Metadata.ActiveInstanceID
}

// WorkloadEndpointMetadata contains the Metadata for a WorkloadEndpoint resource.
type WorkloadEndpointMetadata struct {
unversioned.ObjectMetadata
Expand Down
38 changes: 38 additions & 0 deletions lib/validator/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package validator
import (
"fmt"
"log"
"reflect"
"strings"

"github.com/projectcalico/libcalico-go/lib/api"
"github.com/projectcalico/libcalico-go/lib/api/unversioned"
Expand Down Expand Up @@ -80,3 +82,39 @@ func ValidateMetadataIDsAssigned(rm unversioned.ResourceMetadata) error {

return nil
}

// ValidateIDFieldsSame is used to validate that the Resource Objects
// supplied in one slice reference the same objects in the other slice
// by comparing the ID fields.
func ValidateIDFieldsSame(original, changed []unversioned.ResourceObject) error {
toCompare := make(map[string]int)
for i, obj := range original {
toCompare[obj.GetResourceID()] = i
}

for _, obj := range changed {
if _, ok := toCompare[obj.GetResourceID()]; !ok {
return fmt.Errorf("Cannot change metadata fields: %v, resource will not be found", GetIDFields(obj.GetResourceMetadata()))
}
}

return nil
}

// GetIDFields is a helper function that returns the field labels
// on the metadata objects that are ID fields excluding Labels,
// Annotations, and Tags
func GetIDFields(rm unversioned.ResourceMetadata) []string {
labels := make([]string, 0, 4)
val := reflect.ValueOf(rm)
for i := 0; i < val.Type().NumField(); i++ {
label, ok := val.Type().Field(i).Tag.Lookup("json")
// strip omitempty and other modifiers
label = strings.Split(label, ",")[0]
if ok && label != "annotations" && label != "labels" && label != "tags" {
labels = append(labels, label)
}
}

return labels
}
115 changes: 115 additions & 0 deletions lib/validator/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/projectcalico/libcalico-go/lib/api"
"github.com/projectcalico/libcalico-go/lib/api/unversioned"
"github.com/projectcalico/libcalico-go/lib/net"
"github.com/projectcalico/libcalico-go/lib/scope"
)
Expand Down Expand Up @@ -181,3 +182,117 @@ var _ = Describe("Test ValidateMetadataIDsAssigned function", func() {
})
})
})

var _ = Describe("Test getIDFields helper function", func() {
Context("with BGPPeer Metadata", func() {
md := api.BGPPeerMetadata{}
idFields := validator.GetIDFields(md)
It("should return 3 fields", func() {
Expect(len(idFields)).To(Equal(3))
})
It("should contain scope, node, and peerIP", func() {
for _, field := range idFields {
check := field == "scope" || field == "node" || field == "peerIP"
Expect(check).To(Equal(true))
}
})
})

Context("with Host Endpoint Metadata", func() {
md := api.HostEndpointMetadata{}
idFields := validator.GetIDFields(md)
It("should return 2 fields", func() {
Expect(len(idFields)).To(Equal(2))
})
It("should contain node and name", func() {
for _, field := range idFields {
check := field == "name" || field == "node"
Expect(check).To(Equal(true))
}
})
})

Context("with IP Pool Metadata", func() {
md := api.IPPoolMetadata{}
idFields := validator.GetIDFields(md)
It("should return 1 field", func() {
Expect(len(idFields)).To(Equal(1))
})
It("should contain cidr", func() {
Expect(idFields[0]).To(Equal("cidr"))
})
})

Context("with Node Metadata", func() {
md := api.NodeMetadata{}
idFields := validator.GetIDFields(md)
It("should return 1 field", func() {
Expect(len(idFields)).To(Equal(1))
})
It("should contain name", func() {
Expect(idFields[0]).To(Equal("name"))
})
})

Context("with Policy Metadata", func() {
md := api.PolicyMetadata{}
idFields := validator.GetIDFields(md)
It("should return 1 field", func() {
Expect(len(idFields)).To(Equal(1))
})
It("should contain name", func() {
Expect(idFields[0]).To(Equal("name"))
})
})

Context("with Profile Metadata", func() {
md := api.ProfileMetadata{}
idFields := validator.GetIDFields(md)
It("should return 1 field", func() {
Expect(len(idFields)).To(Equal(1))
})
It("should contain name", func() {
Expect(idFields[0]).To(Equal("name"))
})
})

Context("with Workload Endpoint Metadata", func() {
md := api.WorkloadEndpointMetadata{}
idFields := validator.GetIDFields(md)
It("should return 5 fields", func() {
Expect(len(idFields)).To(Equal(5))
})
It("should contain name, workload, orchestrator, node, activeInstanceID", func() {
for _, field := range idFields {
check := field == "name" || field == "workload" || field == "orchestrator" || field == "node" || field == "activeInstanceID"
Expect(check).To(Equal(true))
}
})
})
})

var _ = Describe("Test ValidateIDFieldsSame", func() {
Context("with Profile Metadata", func() {
var profile1 *api.Profile
var profile2 *api.Profile
BeforeEach(func() {
profile1 = api.NewProfile()
profile1.Metadata.Name = "testProfile1"
profile2 = api.NewProfile()
profile2.Metadata.Name = "testProfile1"
})
It("should not error out if the name is not changed", func() {
before := []unversioned.ResourceObject{profile1}
after := []unversioned.ResourceObject{profile2}
err := validator.ValidateIDFieldsSame(before, after)
Expect(err).NotTo(HaveOccurred())
})
It("should return an error if the name is changed", func() {
profile2.Metadata.Name = "testName2"
before := []unversioned.ResourceObject{profile1}
after := []unversioned.ResourceObject{profile2}
err := validator.ValidateIDFieldsSame(before, after)
Expect(err).To(HaveOccurred())
})
})
})

0 comments on commit c048bde

Please sign in to comment.