From 23ed3250a56704a262bcd018d8ef870c367d55d4 Mon Sep 17 00:00:00 2001 From: Matt Siwiec Date: Fri, 30 Jun 2023 07:37:04 -0600 Subject: [PATCH] Update lbapi client to query ipam ip addresses for node (#41) * update lbapi client to query ipam ip addresses for node Signed-off-by: Matt Siwiec * more useful lbapi client tests Signed-off-by: Matt Siwiec * correct fragment location Signed-off-by: Matt Siwiec * Add graphql/json tags to make the query happy; Added Owner and Location values to query Signed-off-by: Tyler Auerbeck --------- Signed-off-by: Matt Siwiec Signed-off-by: Tyler Auerbeck Co-authored-by: Tyler Auerbeck --- pkg/lbapi/client_test.go | 109 ++++++++++++++++++++++++-------- pkg/lbapi/internal/mock/doc.go | 2 - pkg/lbapi/internal/mock/mock.go | 14 ---- pkg/lbapi/types.go | 25 ++++++-- 4 files changed, 104 insertions(+), 46 deletions(-) delete mode 100644 pkg/lbapi/internal/mock/doc.go delete mode 100644 pkg/lbapi/internal/mock/mock.go diff --git a/pkg/lbapi/client_test.go b/pkg/lbapi/client_test.go index 53da25eb..1e582d33 100644 --- a/pkg/lbapi/client_test.go +++ b/pkg/lbapi/client_test.go @@ -2,45 +2,102 @@ package lbapi import ( "context" + "io" + "net/http" + "net/http/httptest" "testing" + "github.com/shurcooL/graphql" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "go.infratographer.com/loadbalancer-manager-haproxy/pkg/lbapi/internal/mock" ) -func newGQLClientMock() *mock.GQLClient { - mockCli := &mock.GQLClient{} - mockCli.DoQuery = func(ctx context.Context, q interface{}, variables map[string]interface{}) error { - lb, ok := q.(*GetLoadBalancer) - if ok { - lb.LoadBalancer.ID = "loadbal-test" - lb.LoadBalancer.Name = "test" - lb.LoadBalancer.Owner.ID = "testtnt-test" +func TestGetLoadBalancer(t *testing.T) { + respJSON := `{ + "data": { + "loadBalancer": { + "id": "loadbal-randovalue", + "name": "some lb", + "IPAddresses": [ + { + "id": "ipamipa-randovalue", + "ip": "192.168.1.42", + "reserved": false + }, + { + "id": "ipamipa-randovalue2", + "ip": "192.168.1.1", + "reserved": true + } + ], + "ports": { + "edges": [ + { + "node": { + "name": "porty", + "id": "loadprt-randovalue", + "number": 80 + } + } + ] + } } + } +}` - return nil + cli := Client{ + gqlCli: mustNewGQLTestClient(respJSON), } - return mockCli + t.Run("bad prefix", func(t *testing.T) { + lb, err := cli.GetLoadBalancer(context.Background(), "badprefix-test") + require.Error(t, err) + require.Nil(t, lb) + assert.ErrorContains(t, err, "invalid id") + }) + + t.Run("successful query", func(t *testing.T) { + lb, err := cli.GetLoadBalancer(context.Background(), "loadbal-randovalue") + require.NoError(t, err) + require.NotNil(t, lb) + + assert.Equal(t, "loadbal-randovalue", lb.LoadBalancer.ID) + assert.Equal(t, "some lb", lb.LoadBalancer.Name) + assert.Equal(t, "porty", lb.LoadBalancer.Ports.Edges[0].Node.Name) + assert.Equal(t, int64(80), lb.LoadBalancer.Ports.Edges[0].Node.Number) + assert.Empty(t, lb.LoadBalancer.Ports.Edges[0].Node.Pools) + + require.Len(t, lb.LoadBalancer.IPAddresses, 2) + assert.Equal(t, "ipamipa-randovalue", lb.LoadBalancer.IPAddresses[0].ID) + assert.Equal(t, "192.168.1.42", lb.LoadBalancer.IPAddresses[0].IP) + assert.False(t, lb.LoadBalancer.IPAddresses[0].Reserved) + + assert.Equal(t, "ipamipa-randovalue2", lb.LoadBalancer.IPAddresses[1].ID) + assert.Equal(t, "192.168.1.1", lb.LoadBalancer.IPAddresses[1].IP) + assert.True(t, lb.LoadBalancer.IPAddresses[1].Reserved) + }) } -func TestGetLoadBalancer(t *testing.T) { - cli := Client{ - gqlCli: newGQLClientMock(), - } +func mustNewGQLTestClient(respJSON string) *graphql.Client { + mux := http.NewServeMux() + mux.HandleFunc("/query", func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, err := io.WriteString(w, respJSON) + if err != nil { + panic(err) + } + }) - lb, err := cli.GetLoadBalancer(context.Background(), "badprefix-test") - require.Error(t, err) - require.Nil(t, lb) - assert.ErrorContains(t, err, "invalid id") + return graphql.NewClient("/query", &http.Client{Transport: localRoundTripper{handler: mux}}) +} + +type localRoundTripper struct { + handler http.Handler +} - lb, err = cli.GetLoadBalancer(context.Background(), "loadbal-test") - require.NoError(t, err) - require.NotNil(t, lb) +func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + w := httptest.NewRecorder() + l.handler.ServeHTTP(w, req) - assert.Equal(t, lb.LoadBalancer.ID, "loadbal-test") - assert.Equal(t, lb.LoadBalancer.Name, "test") - assert.Equal(t, lb.LoadBalancer.Owner.ID, "testtnt-test") + return w.Result(), nil } diff --git a/pkg/lbapi/internal/mock/doc.go b/pkg/lbapi/internal/mock/doc.go deleted file mode 100644 index e305b5b9..00000000 --- a/pkg/lbapi/internal/mock/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package mock provides mock implementations of the lb api client -package mock diff --git a/pkg/lbapi/internal/mock/mock.go b/pkg/lbapi/internal/mock/mock.go deleted file mode 100644 index 7f75929b..00000000 --- a/pkg/lbapi/internal/mock/mock.go +++ /dev/null @@ -1,14 +0,0 @@ -package mock - -import ( - "context" -) - -// GQLClient is the mock http client -type GQLClient struct { - DoQuery func(ctx context.Context, q interface{}, variables map[string]interface{}) error -} - -func (c *GQLClient) Query(ctx context.Context, q interface{}, variables map[string]interface{}) error { - return c.DoQuery(ctx, q, variables) -} diff --git a/pkg/lbapi/types.go b/pkg/lbapi/types.go index 4993bb5b..475025c3 100644 --- a/pkg/lbapi/types.go +++ b/pkg/lbapi/types.go @@ -42,23 +42,40 @@ type OwnerNode struct { ID string } +type LocationNode struct { + ID string +} + type LoadBalancer struct { - ID string - Owner OwnerNode - Name string - Ports Ports + ID string + Name string + Owner OwnerNode + Location LocationNode + IPAddressableFragment `graphql:"... on IPAddressable"` + Ports Ports } type GetLoadBalancer struct { LoadBalancer LoadBalancer `graphql:"loadBalancer(id: $id)"` } +type IPAddress struct { + ID string + IP string + Reserved bool +} + +type IPAddressableFragment struct { + IPAddresses []IPAddress `graphql:"IPAddresses" json:"IPAddresses"` +} + // Readable version of the above: // type GetLoadBalancer struct { // LoadBalancer struct { // ID string // Owner string // Name string +// IPAddressableFragment // Ports struct { // Edges []struct { // Node struct {