Skip to content

Commit

Permalink
Refactor and update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Jont828 committed Sep 28, 2022
1 parent 8d7d091 commit 998b3fe
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 208 deletions.
26 changes: 26 additions & 0 deletions internal/contract/bootstrap_test.go
Expand Up @@ -52,4 +52,30 @@ func TestBootstrap(t *testing.T) {
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal("fake-data-secret-name"))
})
t.Run("Manages optional status.failureReason", func(t *testing.T) {
g := NewWithT(t)

g.Expect(Bootstrap().FailureReason().Path()).To(Equal(Path{"status", "failureReason"}))

err := Bootstrap().FailureReason().Set(obj, "fake-reason")
g.Expect(err).ToNot(HaveOccurred())

got, err := Bootstrap().FailureReason().Get(obj)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal("fake-reason"))
})
t.Run("Manages optional status.failureMessage", func(t *testing.T) {
g := NewWithT(t)

g.Expect(Bootstrap().FailureMessage().Path()).To(Equal(Path{"status", "failureMessage"}))

err := Bootstrap().FailureMessage().Set(obj, "fake-message")
g.Expect(err).ToNot(HaveOccurred())

got, err := Bootstrap().FailureMessage().Get(obj)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal("fake-message"))
})
}
4 changes: 2 additions & 2 deletions internal/contract/controlplane.go
Expand Up @@ -66,14 +66,14 @@ func (c *ControlPlaneContract) StatusVersion() *String {
}
}

// Ready provide access to the status.ready field in a ControlPlane object. Applies to implementations using replicas.
// Ready provide access to the status.ready field in a ControlPlane object.
func (c *ControlPlaneContract) Ready() *Bool {
return &Bool{
path: []string{"status", "ready"},
}
}

// Initialized provide access to status.initialized field in a ControlPlane object. Applies to implementations using replicas.
// Initialized provide access to status.initialized field in a ControlPlane object.
func (c *ControlPlaneContract) Initialized() *Bool {
return &Bool{
path: []string{"status", "initialized"},
Expand Down
95 changes: 49 additions & 46 deletions internal/contract/infrastructure_cluster.go
Expand Up @@ -17,10 +17,13 @@ limitations under the License.
package contract

import (
"encoding/json"
"strings"
"sync"

"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)

// InfrastructureClusterContract encodes information about the Cluster API contract for InfrastructureCluster objects
Expand Down Expand Up @@ -82,53 +85,8 @@ func (c *InfrastructureClusterContract) FailureMessage() *String {
}
}

// Option 1 for FailureDomains:
// Pros: Provides granularity for accessing map[string]FailureDomainSpec using intermediate structs, all `path` return
// types are wrappers for primatives instead of custom structs.
// Cons: Accessing the map[string]FailureDomainSpec requires knowing the keys ahead of time.

// FailureDomains provides access to the status.failureDomains field in an InfrastructureCluster object. Note that this field is optional.
func (c *InfrastructureClusterContract) FailureDomains() *InfrastructureClusterFailureDomains {
return &InfrastructureClusterFailureDomains{}
}

// InfrastructureClusterFailureDomains provides a helper struct for working with FailureDomains
// in an InfrastructureCluster object.
type InfrastructureClusterFailureDomains struct{}

// FailureDomainSpec provides access to the status.failureDomains.<name> field in an InfrastructureCluster object. Note that this field is optional.
func (c *InfrastructureClusterFailureDomains) FailureDomainSpec(name string) *InfrastructureClusterFailureDomainSpec {
return &InfrastructureClusterFailureDomainSpec{
Name: name,
}
}

// InfrastructureClusterFailureDomainSpec provides a helper struct for working with FailureDomainSpec
// in an InfrastructureCluster object.
type InfrastructureClusterFailureDomainSpec struct {
Name string
}

// ControlPlane provides access to the status.failureDomains.<name>.controlPlane field in an InfrastructureCluster object. Note that this field is optional.
func (c *InfrastructureClusterFailureDomainSpec) ControlPlane() *Bool {
return &Bool{
path: []string{"status", "failureDomains", c.Name, "controlPlane"},
}
}

// Attributes provides access to the status.failureDomains.<name>.attributes field in an InfrastructureCluster object. Note that this field is optional.
func (c *InfrastructureClusterFailureDomainSpec) Attributes() *StringMap {
return &StringMap{
path: []string{"status", "failureDomains", c.Name, "attributes"},
}
}

// Option 2 for FailureDomains:
// Pros: Directly returns the map[string]FailureDomainSpec, allowing users to determine the keys at runtime.
// Cons: Requires defining a new intermediate struct and doesn't document the subfields within a FailureDomainSpec.

// FailureDomains provides access to the status.failureDomains field in an InfrastructureCluster object. Note that this field is optional.
func (c *InfrastructureClusterContract) FailureDomainsAlt() *FailureDomains {
func (c *InfrastructureClusterContract) FailureDomains() *FailureDomains {
return &FailureDomains{
path: []string{"status", "failureDomains"},
}
Expand Down Expand Up @@ -160,3 +118,48 @@ func (c *InfrastructureClusterContract) IgnorePaths(infrastructureCluster *unstr

return ignorePaths, nil
}

// FailureDomains represents an accessor to a clusterv1.FailureDomains path value.
type FailureDomains struct {
path Path
}

// Path returns the path to the clusterv1.FailureDomains value.
func (d *FailureDomains) Path() Path {
return d.path
}

// Get gets the metav1.MachineAddressList value.
func (d *FailureDomains) Get(obj *unstructured.Unstructured) (*clusterv1.FailureDomains, error) {
domainMap, ok, err := unstructured.NestedMap(obj.UnstructuredContent(), d.path...)
if err != nil {
return nil, errors.Wrapf(err, "failed to get %s from object", "."+strings.Join(d.path, "."))
}
if !ok {
return nil, errors.Wrapf(errNotFound, "path %s", "."+strings.Join(d.path, "."))
}

domains := make(clusterv1.FailureDomains, len(domainMap))
s, err := json.Marshal(domainMap)
if err != nil {
return nil, err
}
json.Unmarshal(s, &domains)

return &domains, nil
}

// Set sets the clusterv1.FailureDomains value in the path.
func (d *FailureDomains) Set(obj *unstructured.Unstructured, values clusterv1.FailureDomains) error {
domains := make(map[string]interface{}, len(values))
s, err := json.Marshal(values)
if err != nil {
return err
}
json.Unmarshal(s, &domains)

if err := unstructured.SetNestedField(obj.UnstructuredContent(), domains, d.path...); err != nil {
return errors.Wrapf(err, "failed to set path %s of object %v", "."+strings.Join(d.path, "."), obj.GroupVersionKind())
}
return nil
}
56 changes: 56 additions & 0 deletions internal/contract/infrastructure_cluster_test.go
Expand Up @@ -21,6 +21,7 @@ import (

. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)

func TestInfrastructureCluster(t *testing.T) {
Expand All @@ -39,6 +40,61 @@ func TestInfrastructureCluster(t *testing.T) {
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal(true))
})
t.Run("Manages optional status.failureReason", func(t *testing.T) {
g := NewWithT(t)

g.Expect(InfrastructureCluster().FailureReason().Path()).To(Equal(Path{"status", "failureReason"}))

err := InfrastructureCluster().FailureReason().Set(obj, "fake-reason")
g.Expect(err).ToNot(HaveOccurred())

got, err := InfrastructureCluster().FailureReason().Get(obj)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal("fake-reason"))
})
t.Run("Manages optional status.failureMessage", func(t *testing.T) {
g := NewWithT(t)

g.Expect(InfrastructureCluster().FailureMessage().Path()).To(Equal(Path{"status", "failureMessage"}))

err := InfrastructureCluster().FailureMessage().Set(obj, "fake-message")
g.Expect(err).ToNot(HaveOccurred())

got, err := InfrastructureCluster().FailureMessage().Get(obj)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal("fake-message"))
})
t.Run("Manages optional status.failureDomains", func(t *testing.T) {
g := NewWithT(t)

failureDomains := clusterv1.FailureDomains{
"domain1": clusterv1.FailureDomainSpec{
ControlPlane: true,
Attributes: map[string]string{
"key1": "value1",
"key2": "value2",
},
},
"domain2": clusterv1.FailureDomainSpec{
ControlPlane: false,
Attributes: map[string]string{
"key3": "value3",
"key4": "value4",
},
},
}
g.Expect(InfrastructureCluster().FailureDomains().Path()).To(Equal(Path{"status", "failureDomains"}))

err := InfrastructureCluster().FailureDomains().Set(obj, failureDomains)
g.Expect(err).ToNot(HaveOccurred())

got, err := InfrastructureCluster().FailureDomains().Get(obj)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal(failureDomains))
})
}

func TestInfrastructureClusterControlPlaneEndpoint(t *testing.T) {
Expand Down
86 changes: 52 additions & 34 deletions internal/contract/infrastructure_machine.go
Expand Up @@ -17,8 +17,13 @@ limitations under the License.
package contract

import (
"fmt"
"encoding/json"
"strings"
"sync"

"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)

// InfrastructureMachineContract encodes information about the Machine API contract for InfrastructureMachine objects
Expand Down Expand Up @@ -58,39 +63,7 @@ func (m *InfrastructureMachineContract) FailureMessage() *String {
}

// Addresses provides access to the status.addresses field in an InfrastructureMachine object. Note that this field is optional.
func (m *InfrastructureMachineContract) Addresses() *InfrastructureMachineAddressesList {
return &InfrastructureMachineAddressesList{}
}

// InfrastructureMachineAddressesList provides a helper struct for working with Addresses in a InfrastructureMachine object.
type InfrastructureMachineAddressesList struct{}

// MachineAddresses provides access to the status.addresses[i] field in an InfrastructureMachine object. Note that this field is optional.
func (m *InfrastructureMachineAddressesList) MachineAddress(index int) *InfrastructureMachineAddress {
return &InfrastructureMachineAddress{}
}

// InfrastructureMachineAddresses provides a helper struct for working with Addresses in a InfrastructureMachine object.
type InfrastructureMachineAddress struct {
Index int
}

// Type provides access to the status.addresses[i].type field in an InfrastructureMachine object. Note that this field is optional.
func (m *InfrastructureMachineAddress) Type() *String {
return &String{
path: []string{"status", fmt.Sprintf("addresses[%d]", m.Index), "type"},
}
}

// Hostname provides access to the status.addresses[i].hostname field in an InfrastructureMachine object. Note that this field is optional.
func (m *InfrastructureMachineAddress) Hostname() *String {
return &String{
path: []string{"status", fmt.Sprintf("addresses[%d]", m.Index), "hostname"},
}
}

// AddressesAlt provides access to the status.addresses field in an InfrastructureMachine object. Note that this field is optional.
func (m *InfrastructureMachineContract) AddressesAlt() *MachineAddresses {
func (m *InfrastructureMachineContract) Addresses() *MachineAddresses {
return &MachineAddresses{
path: []string{"status", "addresses"},
}
Expand All @@ -110,3 +83,48 @@ func (m *InfrastructureMachineContract) FailureDomain() *String {
path: []string{"spec", "failureDomain"},
}
}

// MachineAddresses represents an accessor to a []clusterv1.MachineAddress path value.
type MachineAddresses struct {
path Path
}

// Path returns the path to the []clusterv1.MachineAddress value.
func (m *MachineAddresses) Path() Path {
return m.path
}

// Get gets the metav1.MachineAddressList value.
func (m *MachineAddresses) Get(obj *unstructured.Unstructured) (*[]clusterv1.MachineAddress, error) {
slice, ok, err := unstructured.NestedSlice(obj.UnstructuredContent(), m.path...)
if err != nil {
return nil, errors.Wrapf(err, "failed to get %s from object", "."+strings.Join(m.path, "."))
}
if !ok {
return nil, errors.Wrapf(errNotFound, "path %s", "."+strings.Join(m.path, "."))
}

addresses := make([]clusterv1.MachineAddress, len(slice))
s, err := json.Marshal(slice)
if err != nil {
return nil, err
}
json.Unmarshal(s, &addresses)

return &addresses, nil
}

// Set sets the []clusterv1.MachineAddress value in the path.
func (m *MachineAddresses) Set(obj *unstructured.Unstructured, values []clusterv1.MachineAddress) error {
slice := make([]interface{}, len(values))
s, err := json.Marshal(values)
if err != nil {
return err
}
json.Unmarshal(s, &slice)

if err := unstructured.SetNestedField(obj.UnstructuredContent(), slice, m.path...); err != nil {
return errors.Wrapf(err, "failed to set path %s of object %v", "."+strings.Join(m.path, "."), obj.GroupVersionKind())
}
return nil
}
6 changes: 3 additions & 3 deletions internal/contract/infrastructure_machine_test.go
Expand Up @@ -92,12 +92,12 @@ func TestInfrastructureMachine(t *testing.T) {
Address: "fake-address-2",
},
}
g.Expect(InfrastructureMachine().AddressesAlt().Path()).To(Equal(Path{"status", "addresses"}))
g.Expect(InfrastructureMachine().Addresses().Path()).To(Equal(Path{"status", "addresses"}))

err := InfrastructureMachine().AddressesAlt().Set(obj, addresses)
err := InfrastructureMachine().Addresses().Set(obj, addresses)
g.Expect(err).ToNot(HaveOccurred())

got, err := InfrastructureMachine().AddressesAlt().Get(obj)
got, err := InfrastructureMachine().Addresses().Get(obj)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).ToNot(BeNil())
g.Expect(*got).To(Equal(addresses))
Expand Down

0 comments on commit 998b3fe

Please sign in to comment.