Skip to content

Commit

Permalink
Move generic client APIs into pkg/apis/
Browse files Browse the repository at this point in the history
This makes much more visible which APIs are supported by the generic client, makes it easier to have generic client supporting APIs not sharing any code with existing old clients and is a place where other big projects place their API definitions.
  • Loading branch information
LittleFox94 committed Nov 26, 2021
1 parent ee63426 commit ca46cdf
Show file tree
Hide file tree
Showing 20 changed files with 356 additions and 232 deletions.
30 changes: 14 additions & 16 deletions pkg/api/api_examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import (
"fmt"
"log"

lbaasv1 "github.com/anexia-it/go-anxcloud/pkg/apis/lbaas/v1"
"github.com/anexia-it/go-anxcloud/pkg/client"
"github.com/anexia-it/go-anxcloud/pkg/lbaas/backend"
lbaasCommon "github.com/anexia-it/go-anxcloud/pkg/lbaas/common"
"github.com/anexia-it/go-anxcloud/pkg/lbaas/loadbalancer"

"github.com/anexia-it/go-anxcloud/pkg/api/types"
)
Expand All @@ -23,7 +21,7 @@ func ExampleNewAPI() {
log.Fatalf("Error creating api instance: %v\n", err)
} else {
// do something with api
lb := loadbalancer.Loadbalancer{Identifier: "bogus identifier"}
lb := lbaasv1.LoadBalancer{Identifier: "bogus identifier"}
if err := api.Get(context.TODO(), &lb); IgnoreNotFound(err) != nil {
fmt.Printf("Error retrieving loadbalancer with identifier '%v'\n", lb.Identifier)
}
Expand All @@ -39,14 +37,14 @@ func Example_usage() {
apiClient := newExampleAPI()

// retrieve and create backend, handling errors along the way.
backend := backend.Backend{Identifier: "bogus identifier 1"}
backend := lbaasv1.Backend{Identifier: "bogus identifier 1"}
if err := apiClient.Get(context.TODO(), &backend); IgnoreNotFound(err) != nil {
fmt.Printf("Fatal error while retrieving backend: %v\n", err)
} else if err != nil {
fmt.Printf("Backend not yet existing, creating ...\n")

backend.Name = "backend-01"
backend.Mode = lbaasCommon.HTTP
backend.Mode = lbaasv1.HTTP
// [...]

if err := apiClient.Create(context.TODO(), &backend); err != nil {
Expand All @@ -70,9 +68,9 @@ func ExampleAPI_create() {
// see example on NewAPI how to implement this function
apiClient := newExampleAPI()

backend := backend.Backend{
backend := lbaasv1.Backend{
Name: "backend-01",
Mode: lbaasCommon.HTTP,
Mode: lbaasv1.HTTP,
// [...]
}

Expand All @@ -89,7 +87,7 @@ func ExampleAPI_destroy() {
// see example on NewAPI how to implement this function
apiClient := newExampleAPI()

backend := backend.Backend{Identifier: "bogus identifier 1"}
backend := lbaasv1.Backend{Identifier: "bogus identifier 1"}
if err := apiClient.Destroy(context.TODO(), &backend); err != nil {
fmt.Printf("Error destroying backend: %v\n", err)
} else {
Expand All @@ -103,7 +101,7 @@ func ExampleAPI_get() {
// see example on NewAPI how to implement this function
apiClient := newExampleAPI()

backend := backend.Backend{Identifier: "bogus identifier 1"}
backend := lbaasv1.Backend{Identifier: "bogus identifier 1"}
if err := apiClient.Get(context.TODO(), &backend); err != nil {
fmt.Printf("Error retrieving backend: %v\n", err)
} else {
Expand All @@ -121,12 +119,12 @@ func ExampleAPI_listPaged() {

// Beware: listing endpoints usually do not return all data for an object, sometimes
// only the identifier is filled. This varies by specific API.
b := backend.Backend{}
b := lbaasv1.Backend{}
var pageIter types.PageInfo
if err := apiClient.List(context.TODO(), &b, Paged(1, 2, &pageIter)); err != nil {
fmt.Printf("Error listing backends: %v\n", err)
} else {
var backends []backend.Backend
var backends []lbaasv1.Backend
for pageIter.Next(&backends) {
fmt.Printf("Listing entries on page %v\n", pageIter.CurrentPage())

Expand Down Expand Up @@ -168,7 +166,7 @@ func ExampleAPI_listChannel() {

// Beware: listing endpoints usually do not return all data for an object, sometimes
// only the identifier is filled. This varies by specific API.
b := backend.Backend{LoadBalancer: loadbalancer.LoadBalancerInfo{Identifier: "bogus identifier 2"}}
b := lbaasv1.Backend{LoadBalancer: lbaasv1.LoadBalancer{Identifier: "bogus identifier 2"}}
if err := apiClient.List(context.TODO(), &b, AsObjectChannel(&channel)); err != nil {
fmt.Printf("Error listing backends: %v\n", err)
} else {
Expand All @@ -192,10 +190,10 @@ func ExampleAPI_update() {
// see example on NewAPI how to implement this function
apiClient := newExampleAPI()

b := backend.Backend{
b := lbaasv1.Backend{
Identifier: "bogus identifier 1",
Name: "Updated backend",
Mode: lbaasCommon.HTTP,
Mode: lbaasv1.HTTP,
// [...]
}

Expand All @@ -204,7 +202,7 @@ func ExampleAPI_update() {
} else {
fmt.Printf("Successfully updated backend\n")

retrieved := backend.Backend{Identifier: "bogus identifier 1"}
retrieved := lbaasv1.Backend{Identifier: "bogus identifier 1"}
if err := apiClient.Get(context.TODO(), &retrieved); err != nil {
fmt.Printf("Error verifying updated backend: %v\n", err)
} else {
Expand Down
42 changes: 23 additions & 19 deletions pkg/api/mockserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"path"
Expand All @@ -10,9 +11,8 @@ import (

"github.com/onsi/gomega/ghttp"

lbaasv1 "github.com/anexia-it/go-anxcloud/pkg/apis/lbaas/v1"
"github.com/anexia-it/go-anxcloud/pkg/lbaas/backend"
lbaasCommon "github.com/anexia-it/go-anxcloud/pkg/lbaas/common"
"github.com/anexia-it/go-anxcloud/pkg/lbaas/loadbalancer"
)

func newMockServer() *ghttp.Server {
Expand Down Expand Up @@ -43,45 +43,44 @@ func newMockServer() *ghttp.Server {

server := ghttp.NewServer()
server.SetAllowUnhandledRequests(true)
server.SetUnhandledRequestStatusCode(500)

backends := []backend.Backend{
backends := []lbaasv1.Backend{
{
Name: "Example-Backend",
Identifier: "bogus identifier 1",
Mode: lbaasCommon.TCP,
LoadBalancer: loadbalancer.LoadBalancerInfo{
Mode: lbaasv1.TCP,
LoadBalancer: lbaasv1.LoadBalancer{
Identifier: "bogus identifier 2",
},
},
{
Name: "backend-01",
Identifier: "bogus identifier 3",
Mode: lbaasCommon.TCP,
Mode: lbaasv1.TCP,
},
{
Name: "test-backend-01",
Identifier: "test identifier 1",
Mode: lbaasCommon.TCP,
Mode: lbaasv1.TCP,
},
{
Name: "test-backend-02",
Identifier: "test identifier 2",
Mode: lbaasCommon.TCP,
LoadBalancer: loadbalancer.LoadBalancerInfo{
Mode: lbaasv1.TCP,
LoadBalancer: lbaasv1.LoadBalancer{
Identifier: "bogus identifier 2",
},
},
{
Name: "test-backend-03",
Identifier: "test identifier 3",
Mode: lbaasCommon.TCP,
Mode: lbaasv1.TCP,
},
{
Name: "test-backend-04",
Identifier: "test identifier 4",
Mode: lbaasCommon.TCP,
LoadBalancer: loadbalancer.LoadBalancerInfo{
Mode: lbaasv1.TCP,
LoadBalancer: lbaasv1.LoadBalancer{
Identifier: "bogus identifier 2",
},
},
Expand Down Expand Up @@ -158,7 +157,7 @@ func newMockServer() *ghttp.Server {
backend.Backend{
Name: "backend-01",
Identifier: "generated identifier " + strconv.Itoa(identifierGenerateCounter),
Mode: lbaasCommon.TCP,
Mode: lbaasv1.TCP,
},
)

Expand All @@ -182,13 +181,17 @@ func newMockServer() *ghttp.Server {

server.RouteToHandler("PUT", singleBackendPath, func(res http.ResponseWriter, req *http.Request) {
if req.Header.Get("content-type") != "application/json; charset=utf-8" {
errorResponse(505, "Content-Type header on request not set", res)
errorResponse(500, "Content-Type header on request not set", res)
return
}

update := backend.Backend{}
update := struct {
lbaasv1.Backend
LoadBalancer string `json:"load_balancer"`
}{}
if err := json.NewDecoder(req.Body).Decode(&update); err != nil {
errorResponse(505, "Invalid request body", res)
fmt.Printf("Invalid request body: %v\n", err)
errorResponse(500, fmt.Sprintf("invalid request body: %v", err), res)
return
}

Expand All @@ -199,7 +202,8 @@ func newMockServer() *ghttp.Server {

for _, b := range backends {
if b.Identifier == identifier {
newBackends = append(newBackends, update)
update.Backend.LoadBalancer.Identifier = update.LoadBalancer
newBackends = append(newBackends, update.Backend)
found = true
} else {
newBackends = append(newBackends, b)
Expand All @@ -212,7 +216,7 @@ func newMockServer() *ghttp.Server {
errorResponse(404, "", res)
} else {
res.Header().Add("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(res).Encode(update)
_ = json.NewEncoder(res).Encode(update.Backend)
}
})

Expand Down
42 changes: 42 additions & 0 deletions pkg/apis/core/v1/resource_genclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package v1

import (
"context"
"net/url"

"github.com/anexia-it/go-anxcloud/pkg/api"
"github.com/anexia-it/go-anxcloud/pkg/api/types"
"github.com/go-logr/logr"
)

func (i Info) EndpointURL(ctx context.Context) (*url.URL, error) {
u, err := url.ParseRequestURI("/api/core/v1/resource.json")
if err != nil {
return nil, err
}

op, err := types.OperationFromContext(ctx)
if err != nil {
return nil, err
}
switch op {
// OperationCreate is not supported because the API does not exist in the engine.
// OperationDestroy and OperationUpdate is not yet implemented
case types.OperationCreate, types.OperationDestroy, types.OperationUpdate:
return nil, api.ErrOperationNotSupported
}

if op == types.OperationList {
query := u.Query()

if len(i.Tags) > 1 {
logr.FromContextOrDiscard(ctx).Info("Listing with multiple tags isn't supported. Only first one used")
}

if len(i.Tags) > 0 {
query.Add("tag_name", i.Tags[0])
}
u.RawQuery = query.Encode()
}
return u, err
}
15 changes: 15 additions & 0 deletions pkg/apis/core/v1/resource_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package v1

// Type is part of info.
type Type struct {
Identifier string `json:"identifier"`
Name string `json:"name"`
}

// Info contains all information about a resource.
type Info struct {
Identifier string `json:"identifier" anxcloud:"identifier"`
Name string `json:"name"`
Type Type `json:"resource_type"`
Tags []string `json:"tags"`
}
72 changes: 72 additions & 0 deletions pkg/apis/lbaas/v1/backend_genclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package v1

import (
"context"
"net/url"

"github.com/anexia-it/go-anxcloud/pkg/api/types"
)

// EndpointURL returns the URL where to retrieve objects of type Backend and the identifier of the given Backend.
// It implements the api.Object interface on *Backend, making it usable with the generic API client.
func (b *Backend) EndpointURL(ctx context.Context) (*url.URL, error) {
op, err := types.OperationFromContext(ctx)
if err != nil {
return nil, err
}

u, err := url.Parse("/api/LBaaS/v1/backend.json")
if err != nil {
return nil, err
}

if op == types.OperationList {
filters := make(url.Values)

if b.LoadBalancer.Identifier != "" {
filters.Add("load_balancer", b.LoadBalancer.Identifier)
}

if b.Mode != "" {
filters.Add("mode", string(b.Mode))
}

query := u.Query()
query.Add("filters", filters.Encode())
u.RawQuery = query.Encode()
}

return u, err
}

// FilterAPIRequestBody generates the request body for creating a new Backend, which differs from the Backend object.
func (b *Backend) FilterAPIRequestBody(ctx context.Context) (interface{}, error) {
op, err := types.OperationFromContext(ctx)
if err != nil {
return nil, err
}

if op == types.OperationCreate {
return struct {
Name string `json:"name"`
LoadBalancer string `json:"load_balancer"`
Mode Mode `json:"mode"`
State State `json:"state"`
}{
Name: b.Name,
Mode: b.Mode,
LoadBalancer: b.LoadBalancer.Identifier,
State: NewlyCreated,
}, nil
} else if op == types.OperationUpdate {
return struct {
Backend
LoadBalancer string `json:"load_balancer"`
}{
Backend: *b,
LoadBalancer: b.LoadBalancer.Identifier,
}, nil
}

return b, nil
}
15 changes: 15 additions & 0 deletions pkg/apis/lbaas/v1/backend_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package v1

// The Backend resource configures settings common for all specific backend Server resources linked to it.
type Backend struct {
CustomerIdentifier string `json:"customer_identifier"`
ResellerIdentifier string `json:"reseller_identifier"`
Identifier string `json:"identifier" anxcloud:"identifier"`
Name string `json:"name"`
HealthCheck string `json:"health_check"`
Mode Mode `json:"mode"`
ServerTimeout int `json:"server_timeout"`

// Only the name and identifier fields are used and returned.
LoadBalancer LoadBalancer `json:"load_balancer"`
}

0 comments on commit ca46cdf

Please sign in to comment.