diff --git a/pkg/metadata/metadata.go b/pkg/metadata/metadata.go index 50c473bbc..1d553ffae 100644 --- a/pkg/metadata/metadata.go +++ b/pkg/metadata/metadata.go @@ -1,5 +1,15 @@ package metadata +import ( + "encoding/json" + "errors" + "fmt" + + "go.infratographer.com/x/gidx" + + "go.infratographer.com/load-balancer-api/pkg/client" +) + // LoadBalancerState is the state of a load balancer type LoadBalancerState string @@ -11,3 +21,53 @@ const ( LoadBalancerStateDeleted LoadBalancerState = "deleted" LoadBalancerStateUpdating LoadBalancerState = "updating" ) + +var ( + // ErrStatusNotFound is returned when a status is not found in the payload + ErrStatusNotFound = errors.New("status not found") + + // ErrInvalidStatusData is returned when the status json data is invalid + ErrInvalidStatusData = errors.New("invalid status json data") +) + +// ErrUnknownLoadBalancerState is returned when the load balancer state is unknown +type ErrUnknownLoadBalancerState struct { + State string +} + +func (e ErrUnknownLoadBalancerState) Error() string { + return "unknown load balancer state: " + e.State +} + +// GetLoadbalancerState returns the status of a load balancer +func GetLoadbalancerState(metadataStatuses client.MetadataStatuses, statusNamespaceID gidx.PrefixedID) (LoadBalancerState, error) { + for _, status := range metadataStatuses.Edges { + if status.Node.StatusNamespaceID == statusNamespaceID.String() { + // we've found the loadbalancer status namespace we are looking fah + data := map[string]string{} + if err := json.Unmarshal(status.Node.Data, &data); err != nil { + // bad data stored in metadata-api, drop the message and move on + return "", fmt.Errorf("%w: %s", ErrInvalidStatusData, err) + } + + if state, ok := data["state"]; ok { + switch state { + case string(LoadBalancerStateCreating): + fallthrough + case string(LoadBalancerStateTerminating): + fallthrough + case string(LoadBalancerStateActive): + fallthrough + case string(LoadBalancerStateDeleted): + fallthrough + case string(LoadBalancerStateUpdating): + return LoadBalancerState(state), nil + default: + return "", ErrUnknownLoadBalancerState{State: state} + } + } + } + } + + return "", ErrStatusNotFound +} diff --git a/pkg/metadata/metadata_test.go b/pkg/metadata/metadata_test.go new file mode 100644 index 000000000..ac9648ad3 --- /dev/null +++ b/pkg/metadata/metadata_test.go @@ -0,0 +1,82 @@ +package metadata + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + + "go.infratographer.com/load-balancer-api/pkg/client" +) + +func TestGetLoadbalancerState(t *testing.T) { + t.Run("valid status", func(t *testing.T) { + statuses := client.MetadataStatuses{ + Edges: []client.MetadataStatusEdges{ + { + Node: client.MetadataStatusNode{ + StatusNamespaceID: "metasns-loadbalancer-status", + Data: json.RawMessage(`{"state": "active"}`), + }, + }, + { + Node: client.MetadataStatusNode{ + StatusNamespaceID: "metasns-some-other-namespace", + Data: json.RawMessage(`{"key": "value"}`), + }, + }, + }, + } + + state, err := GetLoadbalancerState(statuses, "metasns-loadbalancer-status") + assert.Nil(t, err) + assert.Equal(t, LoadBalancerStateActive, state) + }) + + t.Run("bad json data", func(t *testing.T) { + statuses := client.MetadataStatuses{ + Edges: []client.MetadataStatusEdges{ + { + Node: client.MetadataStatusNode{ + StatusNamespaceID: "metasns-loadbalancer-status", + Data: json.RawMessage(`{"state"}`), + }, + }, + }, + } + + state, err := GetLoadbalancerState(statuses, "metasns-loadbalancer-status") + assert.NotNil(t, err) + assert.Empty(t, state) + assert.ErrorIs(t, err, ErrInvalidStatusData) + }) + + t.Run("status not found", func(t *testing.T) { + statuses := client.MetadataStatuses{ + Edges: []client.MetadataStatusEdges{}, + } + + state, err := GetLoadbalancerState(statuses, "metasns-loadbalancer-status") + assert.NotNil(t, err) + assert.Empty(t, state) + assert.ErrorIs(t, err, ErrStatusNotFound) + }) + + t.Run("unknown state", func(t *testing.T) { + statuses := client.MetadataStatuses{ + Edges: []client.MetadataStatusEdges{ + { + Node: client.MetadataStatusNode{ + StatusNamespaceID: "metasns-loadbalancer-status", + Data: json.RawMessage(`{"state": "unknown"}`), + }, + }, + }, + } + + state, err := GetLoadbalancerState(statuses, "metasns-loadbalancer-status") + assert.NotNil(t, err) + assert.Empty(t, state) + assert.ErrorIs(t, err, ErrUnknownLoadBalancerState{State: "unknown"}) + }) +}