Skip to content

Commit

Permalink
feat: add GetFullSchema and Fill*Defaults to core entities
Browse files Browse the repository at this point in the history
Mirroring what's been previously done for Plugins,
this adds GetFullSchema to Routes, Upstreams, Targets and Services.
This also adds utils function to fill core entities with
defaults comings from their schema.
  • Loading branch information
GGabriele committed Jan 24, 2022
1 parent e965de2 commit df63973
Show file tree
Hide file tree
Showing 12 changed files with 647 additions and 1 deletion.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/google/go-cmp v0.5.7
github.com/google/go-querystring v1.1.0
github.com/google/uuid v1.3.0
github.com/imdario/mergo v0.3.12
github.com/mitchellh/mapstructure v1.4.3
github.com/stretchr/testify v1.7.0
github.com/tidwall/gjson v1.13.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
Expand Down
2 changes: 1 addition & 1 deletion kong/plugin_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ func TestPluginListAllForEntityEndpoint(T *testing.T) {
assert.Nil(client.Services.Delete(defaultCtx, createdService.ID))
}

func TestGetFullSchema(T *testing.T) {
func TestPluginGetFullSchema(T *testing.T) {
assert := assert.New(T)

client, err := NewTestClient(nil, nil)
Expand Down
16 changes: 16 additions & 0 deletions kong/route_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,27 @@ type AbstractRouteService interface {
ListAll(ctx context.Context) ([]*Route, error)
// ListForService fetches a list of Routes in Kong associated with a service.
ListForService(ctx context.Context, serviceNameOrID *string, opt *ListOpt) ([]*Route, *ListOpt, error)
// GetFullSchema retrieves the full schema of routes.
GetFullSchema(ctx context.Context) (map[string]interface{}, error)
}

// RouteService handles routes in Kong.
type RouteService service

// GetFullSchema retrieves the full schema of routes.
func (s *RouteService) GetFullSchema(ctx context.Context) (map[string]interface{}, error) {
req, err := s.client.NewRequest("GET", "/schemas/routes", nil, nil)
if err != nil {
return nil, err
}
var schema map[string]interface{}
_, err = s.client.Do(ctx, req, &schema)
if err != nil {
return nil, err
}
return schema, nil
}

// Create creates a Route in Kong
// If an ID is specified, it will be used to
// create a route in Kong, otherwise an ID
Expand Down
104 changes: 104 additions & 0 deletions kong/route_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package kong
import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -286,3 +288,105 @@ func TestRouteWithHeaders(T *testing.T) {
err = client.Routes.Delete(defaultCtx, createdRoute.ID)
assert.Nil(err)
}

func TestRouteGetFullSchema(T *testing.T) {
assert := assert.New(T)

client, err := NewTestClient(nil, nil)
assert.Nil(err)
assert.NotNil(client)

schema, err := client.Routes.GetFullSchema(defaultCtx)
_, ok := schema["fields"]
assert.True(ok)
assert.Nil(err)
}

func TestFillRoutesDefaults(T *testing.T) {
assert := assert.New(T)

client, err := NewTestClient(nil, nil)
assert.Nil(err)
assert.NotNil(client)

tests := []struct {
name string
route *Route
expected *Route
}{
{
name: "name paths",
route: &Route{
Name: String("r1"),
Paths: []*string{String("/r1")},
},
expected: &Route{
Name: String("r1"),
Paths: []*string{String("/r1")},
PathHandling: String("v0"),
PreserveHost: Bool(false),
Protocols: []*string{String("http"), String("https")},
RegexPriority: Int(0),
StripPath: Bool(true),
HTTPSRedirectStatusCode: Int(426),
},
},
{
name: "name paths protocols",
route: &Route{
Name: String("r1"),
Paths: []*string{String("/r1")},
Protocols: []*string{String("grpc")},
},
expected: &Route{
Name: String("r1"),
Paths: []*string{String("/r1")},
PathHandling: String("v0"),
PreserveHost: Bool(false),
Protocols: []*string{String("grpc")},
RegexPriority: Int(0),
StripPath: Bool(true),
HTTPSRedirectStatusCode: Int(426),
},
},
// TODO: re-add once upstream patch has been merged.
// https://github.com/imdario/mergo/pull/203
// {
// name: "set opposite bools",
// route: &Route{
// Name: String("r1"),
// Paths: []*string{String("/r1")},
// Protocols: []*string{String("grpc")},
// StripPath: Bool(false),
// PreserveHost: Bool(true),
// },
// expected: &Route{
// Name: String("r1"),
// Paths: []*string{String("/r1")},
// PathHandling: String("v0"),
// PreserveHost: Bool(true),
// Protocols: []*string{String("grpc")},
// RegexPriority: Int(0),
// StripPath: Bool(false),
// HTTPSRedirectStatusCode: Int(426),
// },
// },
}

for _, tc := range tests {
T.Run(tc.name, func(t *testing.T) {
r := tc.route
fullSchema, err := client.Routes.GetFullSchema(defaultCtx)
assert.Nil(err)
assert.NotNil(fullSchema)
if err = FillRoutesDefaults(r, fullSchema); err != nil {
t.Errorf(err.Error())
}
// Ignore fields to make tests pass despite small differences across releases.
opts := cmpopts.IgnoreFields(Route{}, "RequestBuffering", "ResponseBuffering")
if diff := cmp.Diff(r, tc.expected, opts); diff != "" {
t.Errorf(diff)
}
})
}
}
16 changes: 16 additions & 0 deletions kong/service_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,27 @@ type AbstractSvcService interface {
List(ctx context.Context, opt *ListOpt) ([]*Service, *ListOpt, error)
// ListAll fetches all Services in Kong.
ListAll(ctx context.Context) ([]*Service, error)
// GetFullSchema retrieves the full schema of services.
GetFullSchema(ctx context.Context) (map[string]interface{}, error)
}

// Svcservice handles services in Kong.
type Svcservice service

// GetFullSchema retrieves the full schema of services.
func (s *Svcservice) GetFullSchema(ctx context.Context) (map[string]interface{}, error) {
req, err := s.client.NewRequest("GET", "/schemas/services", nil, nil)
if err != nil {
return nil, err
}
var schema map[string]interface{}
_, err = s.client.Do(ctx, req, &schema)
if err != nil {
return nil, err
}
return schema, nil
}

// Create creates an Service in Kong
// If an ID is specified, it will be used to
// create a service in Kong, otherwise an ID
Expand Down
99 changes: 99 additions & 0 deletions kong/service_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package kong
import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -222,3 +223,101 @@ func TestServiceWithClientCert(T *testing.T) {
err = client.Certificates.Delete(defaultCtx, createdCertificate.ID)
assert.Nil(err)
}

func TestServiceGetFullSchema(T *testing.T) {
assert := assert.New(T)

client, err := NewTestClient(nil, nil)
assert.Nil(err)
assert.NotNil(client)

schema, err := client.Services.GetFullSchema(defaultCtx)
_, ok := schema["fields"]
assert.True(ok)
assert.Nil(err)
}

func TestFillServiceDefaults(T *testing.T) {
assert := assert.New(T)

client, err := NewTestClient(nil, nil)
assert.Nil(err)
assert.NotNil(client)

tests := []struct {
name string
service *Service
expected *Service
}{
{
name: "name and host only",
service: &Service{
Name: String("svc1"),
Host: String("mockbin.org"),
},
expected: &Service{
Name: String("svc1"),
Host: String("mockbin.org"),
Port: Int(80),
Protocol: String("http"),
ConnectTimeout: Int(60000),
ReadTimeout: Int(60000),
Retries: Int(5),
WriteTimeout: Int(60000),
},
},
{
name: "name host and port",
service: &Service{
Name: String("svc1"),
Host: String("mockbin.org"),
Port: Int(8080),
},
expected: &Service{
Name: String("svc1"),
Host: String("mockbin.org"),
Port: Int(8080),
Protocol: String("http"),
ConnectTimeout: Int(60000),
ReadTimeout: Int(60000),
Retries: Int(5),
WriteTimeout: Int(60000),
},
},
{
name: "name host port and tags",
service: &Service{
Name: String("svc1"),
Host: String("mockbin.org"),
Port: Int(8080),
Tags: []*string{String("tag1"), String("tag2")},
},
expected: &Service{
Name: String("svc1"),
Host: String("mockbin.org"),
Port: Int(8080),
Protocol: String("http"),
ConnectTimeout: Int(60000),
ReadTimeout: Int(60000),
Retries: Int(5),
WriteTimeout: Int(60000),
Tags: []*string{String("tag1"), String("tag2")},
},
},
}

for _, tc := range tests {
T.Run(tc.name, func(t *testing.T) {
s := tc.service
fullSchema, err := client.Services.GetFullSchema(defaultCtx)
assert.Nil(err)
assert.NotNil(fullSchema)
if err := FillServicesDefaults(s, fullSchema); err != nil {
t.Errorf(err.Error())
}
if diff := cmp.Diff(s, tc.expected); diff != "" {
t.Errorf(diff)
}
})
}
}
16 changes: 16 additions & 0 deletions kong/target_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,27 @@ type AbstractTargetService interface {
// MarkUnhealthy marks target belonging to upstreamNameOrID as unhealthy in
// Kong's load balancer.
MarkUnhealthy(ctx context.Context, upstreamNameOrID *string, target *Target) error
// GetFullSchema retrieves the full schema of targets.
GetFullSchema(ctx context.Context) (map[string]interface{}, error)
}

// TargetService handles Targets in Kong.
type TargetService service

// GetFullSchema retrieves the full schema of targets.
func (s *TargetService) GetFullSchema(ctx context.Context) (map[string]interface{}, error) {
req, err := s.client.NewRequest("GET", "/schemas/targets", nil, nil)
if err != nil {
return nil, err
}
var schema map[string]interface{}
_, err = s.client.Do(ctx, req, &schema)
if err != nil {
return nil, err
}
return schema, nil
}

// TODO foreign key can be read directly from the embedded key itself
// upstreamNameOrID need not be an explicit parameter.

Expand Down
51 changes: 51 additions & 0 deletions kong/target_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package kong
import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -284,3 +285,53 @@ func TestTargetMarkUnhealthy(T *testing.T) {

assert.Nil(client.Upstreams.Delete(defaultCtx, createdUpstream.ID))
}

func TestTargetGetFullSchema(T *testing.T) {
assert := assert.New(T)

client, err := NewTestClient(nil, nil)
assert.Nil(err)
assert.NotNil(client)

schema, err := client.Targets.GetFullSchema(defaultCtx)
_, ok := schema["fields"]
assert.True(ok)
assert.Nil(err)
}

func TestFillTargetDefaults(T *testing.T) {
assert := assert.New(T)

client, err := NewTestClient(nil, nil)
assert.Nil(err)
assert.NotNil(client)

tests := []struct {
name string
target *Target
expected *Target
}{
{
name: "empty",
target: &Target{},
expected: &Target{
Weight: Int(100),
},
},
}

for _, tc := range tests {
T.Run(tc.name, func(t *testing.T) {
target := tc.target
fullSchema, err := client.Targets.GetFullSchema(defaultCtx)
assert.Nil(err)
assert.NotNil(fullSchema)
if err := FillTargetsDefaults(target, fullSchema); err != nil {
t.Errorf(err.Error())
}
if diff := cmp.Diff(target, tc.expected); diff != "" {
t.Errorf(diff)
}
})
}
}
Loading

0 comments on commit df63973

Please sign in to comment.