Skip to content

Commit

Permalink
feat: implement dynamic defaulting feature
Browse files Browse the repository at this point in the history
- file/codegen/main.go and corresponding schema change
  This is to relax the schema of kong entities that are used as input
  for defaults. The relaxation is necessary as otherwise the user will
  have to specify a name in default values. This doesn't have any
  side-effects because the *kong.Type is not used anywhere else in
  Content.
- file/builder.go file/reader.go
  - defaulter instantiation has been moved inside builder, this feels
    natural, defaulter being instantiated outside the builder seems odd
    and the only explanation is that I simply didn't think through when
    implementing defaulter
  - other changes override the default values that are registered

Fix #89
  • Loading branch information
hbagdi committed May 27, 2021
1 parent 84035a6 commit cdf4097
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 66 deletions.
10 changes: 10 additions & 0 deletions file/builder.go
Expand Up @@ -47,6 +47,16 @@ func (b *stateBuilder) build() (*utils.KongRawState, *utils.KonnectRawState, err
return nil, nil, err
}

// defaulter
var kongDefaults KongDefaults
if b.targetContent.Info != nil {
kongDefaults = b.targetContent.Info.Defaults
}
b.defaulter, err = defaulter(kongDefaults)
if err != nil {
return nil, nil, fmt.Errorf("creating defaulter: %w", err)
}

// build
b.certificates()
b.caCertificates()
Expand Down
309 changes: 306 additions & 3 deletions file/builder_test.go
Expand Up @@ -372,8 +372,6 @@ func Test_stateBuilder_services(t *testing.T) {
targetContent: tt.fields.targetContent,
currentState: tt.fields.currentState,
}
d, _ := utils.GetKongDefaulter()
b.defaulter = d
b.build()
assert.Equal(tt.want, b.rawState)
})
Expand Down Expand Up @@ -1845,7 +1843,6 @@ func Test_stateBuilder_documents(t *testing.T) {

func Test_stateBuilder(t *testing.T) {
assert := assert.New(t)
rand.Seed(42)
type fields struct {
targetContent *Content
currentState *state.KongState
Expand Down Expand Up @@ -2091,9 +2088,315 @@ func Test_stateBuilder(t *testing.T) {
},
},
},
{
name: "entities with configurable defaults",
fields: fields{
targetContent: &Content{
Info: &Info{
SelectorTags: []string{"tag1"},
Defaults: KongDefaults{
Route: &kong.Route{
PathHandling: kong.String("v0"),
PreserveHost: kong.Bool(false),
RegexPriority: kong.Int(0),
StripPath: kong.Bool(false),
Protocols: kong.StringSlice("http", "https"),
RequestBuffering: kong.Bool(false),
},
Service: &kong.Service{
Port: kong.Int(443),
Protocol: kong.String("https"),
ConnectTimeout: kong.Int(5000),
WriteTimeout: kong.Int(5000),
ReadTimeout: kong.Int(5000),
},
Upstream: &kong.Upstream{
Slots: kong.Int(100),
Healthchecks: &kong.Healthcheck{
Active: &kong.ActiveHealthcheck{
Concurrency: kong.Int(5),
Healthy: &kong.Healthy{
HTTPStatuses: []int{200, 302},
Interval: kong.Int(0),
Successes: kong.Int(0),
},
HTTPPath: kong.String("/"),
Type: kong.String("http"),
Timeout: kong.Int(1),
Unhealthy: &kong.Unhealthy{
HTTPFailures: kong.Int(0),
TCPFailures: kong.Int(0),
Timeouts: kong.Int(0),
Interval: kong.Int(0),
HTTPStatuses: []int{429, 404, 500, 501, 502, 503, 504, 505},
},
},
Passive: &kong.PassiveHealthcheck{
Healthy: &kong.Healthy{
HTTPStatuses: []int{
200, 201, 202, 203, 204, 205,
206, 207, 208, 226, 300, 301, 302, 303, 304, 305,
306, 307, 308,
},
Successes: kong.Int(0),
},
Unhealthy: &kong.Unhealthy{
HTTPFailures: kong.Int(0),
TCPFailures: kong.Int(0),
Timeouts: kong.Int(0),
HTTPStatuses: []int{429, 500, 503},
},
},
},
HashOn: kong.String("none"),
HashFallback: kong.String("none"),
HashOnCookiePath: kong.String("/"),
},
},
},
Services: []FService{
{
Service: kong.Service{
Name: kong.String("foo-service"),
},
Routes: []*FRoute{
{
Route: kong.Route{
Name: kong.String("foo-route1"),
},
},
{
Route: kong.Route{
ID: kong.String("d125e79a-297c-414b-bc00-ad3a87be6c2b"),
Name: kong.String("foo-route2"),
},
},
},
},
{
Service: kong.Service{
Name: kong.String("bar-service"),
},
Routes: []*FRoute{
{
Route: kong.Route{
Name: kong.String("bar-route1"),
},
},
{
Route: kong.Route{
Name: kong.String("bar-route2"),
},
},
},
},
{
Service: kong.Service{
Name: kong.String("large-payload-service"),
},
Routes: []*FRoute{
{
Route: kong.Route{
Name: kong.String("dont-buffer-these"),
RequestBuffering: kong.Bool(false),
ResponseBuffering: kong.Bool(false),
},
},
{
Route: kong.Route{
Name: kong.String("buffer-these"),
RequestBuffering: kong.Bool(true),
ResponseBuffering: kong.Bool(true),
},
},
},
},
},
Upstreams: []FUpstream{
{
Upstream: kong.Upstream{
Name: kong.String("foo"),
Slots: kong.Int(42),
},
},
},
},
currentState: existingServiceState(),
},
want: &utils.KongRawState{
Services: []*kong.Service{
{
ID: kong.String("538c7f96-b164-4f1b-97bb-9f4bb472e89f"),
Name: kong.String("foo-service"),
Port: kong.Int(443),
Protocol: kong.String("https"),
ConnectTimeout: kong.Int(5000),
WriteTimeout: kong.Int(5000),
ReadTimeout: kong.Int(5000),
Tags: kong.StringSlice("tag1"),
},
{
ID: kong.String("dfd79b4d-7642-4b61-ba0c-9f9f0d3ba55b"),
Name: kong.String("bar-service"),
Port: kong.Int(443),
Protocol: kong.String("https"),
ConnectTimeout: kong.Int(5000),
WriteTimeout: kong.Int(5000),
ReadTimeout: kong.Int(5000),
Tags: kong.StringSlice("tag1"),
},
{
ID: kong.String("9e6f82e5-4e74-4e81-a79e-4bbd6fe34cdc"),
Name: kong.String("large-payload-service"),
Port: kong.Int(443),
Protocol: kong.String("https"),
ConnectTimeout: kong.Int(5000),
WriteTimeout: kong.Int(5000),
ReadTimeout: kong.Int(5000),
Tags: kong.StringSlice("tag1"),
},
},
Routes: []*kong.Route{
{
ID: kong.String("5b1484f2-5209-49d9-b43e-92ba09dd9d52"),
Name: kong.String("foo-route1"),
PreserveHost: kong.Bool(false),
RegexPriority: kong.Int(0),
StripPath: kong.Bool(false),
Protocols: kong.StringSlice("http", "https"),
RequestBuffering: kong.Bool(false),
PathHandling: kong.String("v0"),
Service: &kong.Service{
ID: kong.String("538c7f96-b164-4f1b-97bb-9f4bb472e89f"),
},
Tags: kong.StringSlice("tag1"),
},
{
ID: kong.String("d125e79a-297c-414b-bc00-ad3a87be6c2b"),
Name: kong.String("foo-route2"),
PreserveHost: kong.Bool(false),
RegexPriority: kong.Int(0),
StripPath: kong.Bool(false),
Protocols: kong.StringSlice("http", "https"),
RequestBuffering: kong.Bool(false),
PathHandling: kong.String("v0"),
Service: &kong.Service{
ID: kong.String("538c7f96-b164-4f1b-97bb-9f4bb472e89f"),
},
Tags: kong.StringSlice("tag1"),
},
{
ID: kong.String("0cc0d614-4c88-4535-841a-cbe0709b0758"),
Name: kong.String("bar-route1"),
PreserveHost: kong.Bool(false),
RegexPriority: kong.Int(0),
StripPath: kong.Bool(false),
Protocols: kong.StringSlice("http", "https"),
RequestBuffering: kong.Bool(false),
PathHandling: kong.String("v0"),
Service: &kong.Service{
ID: kong.String("dfd79b4d-7642-4b61-ba0c-9f9f0d3ba55b"),
},
Tags: kong.StringSlice("tag1"),
},
{
ID: kong.String("083f61d3-75bc-42b4-9df4-f91929e18fda"),
Name: kong.String("bar-route2"),
PreserveHost: kong.Bool(false),
RegexPriority: kong.Int(0),
StripPath: kong.Bool(false),
Protocols: kong.StringSlice("http", "https"),
RequestBuffering: kong.Bool(false),
PathHandling: kong.String("v0"),
Service: &kong.Service{
ID: kong.String("dfd79b4d-7642-4b61-ba0c-9f9f0d3ba55b"),
},
Tags: kong.StringSlice("tag1"),
},
{
ID: kong.String("ba843ee8-d63e-4c4f-be1c-ebea546d8fac"),
Name: kong.String("dont-buffer-these"),
PreserveHost: kong.Bool(false),
RegexPriority: kong.Int(0),
StripPath: kong.Bool(false),
Protocols: kong.StringSlice("http", "https"),
PathHandling: kong.String("v0"),
Service: &kong.Service{
ID: kong.String("9e6f82e5-4e74-4e81-a79e-4bbd6fe34cdc"),
},
Tags: kong.StringSlice("tag1"),
RequestBuffering: kong.Bool(false),
ResponseBuffering: kong.Bool(false),
},
{
ID: kong.String("13dd1aac-04ce-4ea2-877c-5579cfa2c78e"),
Name: kong.String("buffer-these"),
PreserveHost: kong.Bool(false),
RegexPriority: kong.Int(0),
StripPath: kong.Bool(false),
Protocols: kong.StringSlice("http", "https"),
PathHandling: kong.String("v0"),
Service: &kong.Service{
ID: kong.String("9e6f82e5-4e74-4e81-a79e-4bbd6fe34cdc"),
},
Tags: kong.StringSlice("tag1"),
RequestBuffering: kong.Bool(true),
ResponseBuffering: kong.Bool(true),
},
},
Upstreams: []*kong.Upstream{
{
ID: kong.String("1b0bafae-881b-42a7-9110-8a42ed3c903c"),
Name: kong.String("foo"),
Slots: kong.Int(42),
Healthchecks: &kong.Healthcheck{
Active: &kong.ActiveHealthcheck{
Concurrency: kong.Int(5),
Healthy: &kong.Healthy{
HTTPStatuses: []int{200, 302},
Interval: kong.Int(0),
Successes: kong.Int(0),
},
HTTPPath: kong.String("/"),
Type: kong.String("http"),
Timeout: kong.Int(1),
Unhealthy: &kong.Unhealthy{
HTTPFailures: kong.Int(0),
TCPFailures: kong.Int(0),
Timeouts: kong.Int(0),
Interval: kong.Int(0),
HTTPStatuses: []int{429, 404, 500, 501, 502, 503, 504, 505},
},
},
Passive: &kong.PassiveHealthcheck{
Healthy: &kong.Healthy{
HTTPStatuses: []int{
200, 201, 202, 203, 204, 205,
206, 207, 208, 226, 300, 301, 302, 303, 304, 305,
306, 307, 308,
},
Successes: kong.Int(0),
},
Unhealthy: &kong.Unhealthy{
HTTPFailures: kong.Int(0),
TCPFailures: kong.Int(0),
Timeouts: kong.Int(0),
HTTPStatuses: []int{429, 500, 503},
},
},
},
HashOn: kong.String("none"),
HashFallback: kong.String("none"),
HashOnCookiePath: kong.String("/"),
Tags: kong.StringSlice("tag1"),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rand.Seed(42)
b := &stateBuilder{
targetContent: tt.fields.targetContent,
currentState: tt.fields.currentState,
Expand Down
8 changes: 4 additions & 4 deletions file/codegen/main.go
Expand Up @@ -58,16 +58,16 @@ func main() {
return nil
}
schema := reflector.Reflect(file.Content{})
schema.Definitions["Service"].AnyOf = anyOfNameOrID
// schema.Definitions["Service"].AnyOf = anyOfNameOrID
schema.Definitions["FService"].AnyOf = anyOfNameOrID

schema.Definitions["Route"].AnyOf = anyOfNameOrID
// schema.Definitions["Route"].AnyOf = anyOfNameOrID
schema.Definitions["FRoute"].AnyOf = anyOfNameOrID

schema.Definitions["Consumer"].AnyOf = anyOfUsernameOrID
// schema.Definitions["Consumer"].AnyOf = anyOfUsernameOrID
schema.Definitions["FConsumer"].AnyOf = anyOfUsernameOrID

schema.Definitions["Upstream"].Required = []string{"name"}
// schema.Definitions["Upstream"].Required = []string{"name"}
schema.Definitions["FUpstream"].Required = []string{"name"}

schema.Definitions["FTarget"].Required = []string{"target"}
Expand Down

0 comments on commit cdf4097

Please sign in to comment.