diff --git a/file/types.go b/file/types.go index 2d7ef79a2..4c86aa778 100644 --- a/file/types.go +++ b/file/types.go @@ -14,8 +14,8 @@ import ( // Format is a file format for Kong's configuration. type Format string -type id interface { - id() string +type sortable interface { + sortKey() string } const ( @@ -36,14 +36,14 @@ type FService struct { URL *string `json:"url,omitempty" yaml:",omitempty"` } -// id is used for sorting. -func (s FService) id() string { - if s.ID != nil { - return *s.ID - } +// sortKey is used for sorting. +func (s FService) sortKey() string { if s.Name != nil { return *s.Name } + if s.ID != nil { + return *s.ID + } return "" } @@ -216,14 +216,14 @@ type FRoute struct { Plugins []*FPlugin `json:"plugins,omitempty" yaml:",omitempty"` } -// id is used for sorting. -func (r FRoute) id() string { - if r.ID != nil { - return *r.ID - } +// sortKey is used for sorting. +func (r FRoute) sortKey() string { if r.Name != nil { return *r.Name } + if r.ID != nil { + return *r.ID + } return "" } @@ -234,14 +234,14 @@ type FUpstream struct { Targets []*FTarget `json:"targets,omitempty" yaml:",omitempty"` } -// id is used for sorting. -func (u FUpstream) id() string { - if u.ID != nil { - return *u.ID - } +// sortKey is used for sorting. +func (u FUpstream) sortKey() string { if u.Name != nil { return *u.Name } + if u.ID != nil { + return *u.ID + } return "" } @@ -251,14 +251,14 @@ type FTarget struct { kong.Target `yaml:",inline,omitempty"` } -// id is used for sorting. -func (t FTarget) id() string { - if t.ID != nil { - return *t.ID - } +// sortKey is used for sorting. +func (t FTarget) sortKey() string { if t.Target.Target != nil { return *t.Target.Target } + if t.ID != nil { + return *t.ID + } return "" } @@ -273,14 +273,14 @@ type FCertificate struct { SNIs []kong.SNI `json:"snis,omitempty" yaml:"snis,omitempty"` } -// id is used for sorting. -func (c FCertificate) id() string { - if c.ID != nil { - return *c.ID - } +// sortKey is used for sorting. +func (c FCertificate) sortKey() string { if c.Cert != nil { return *c.Cert } + if c.ID != nil { + return *c.ID + } return "" } @@ -290,14 +290,14 @@ type FCACertificate struct { kong.CACertificate `yaml:",inline,omitempty"` } -// id is used for sorting. -func (c FCACertificate) id() string { - if c.ID != nil { - return *c.ID - } +// sortKey is used for sorting. +func (c FCACertificate) sortKey() string { if c.Cert != nil { return *c.Cert } + if c.ID != nil { + return *c.ID + } return "" } @@ -444,24 +444,27 @@ func (p *FPlugin) UnmarshalJSON(b []byte) error { return nil } -// id is used for sorting. -func (p FPlugin) id() string { - if p.ID != nil { - return *p.ID - } +// sortKey is used for sorting. +func (p FPlugin) sortKey() string { // concat plugin name and foreign relations - key := "" - key = *p.Name - if p.Consumer != nil { - key += *p.Consumer.ID - } - if p.Route != nil { - key += *p.Route.ID + if p.Name != nil { + key := "" + key = *p.Name + if p.Consumer != nil { + key += *p.Consumer.ID + } + if p.Route != nil { + key += *p.Route.ID + } + if p.Service != nil { + key += *p.Service.ID + } + return key } - if p.Service != nil { - key += *p.Service.ID + if p.ID != nil { + return *p.ID } - return key + return "" } // FConsumer represents a consumer in Kong. @@ -478,14 +481,14 @@ type FConsumer struct { MTLSAuths []*kong.MTLSAuth `json:"mtls_auth_credentials,omitempty" yaml:"mtls_auth_credentials,omitempty"` } -// id is used for sorting. -func (c FConsumer) id() string { - if c.ID != nil { - return *c.ID - } +// sortKey is used for sorting. +func (c FConsumer) sortKey() string { if c.Username != nil { return *c.Username } + if c.ID != nil { + return *c.ID + } return "" } @@ -533,14 +536,14 @@ type FServicePackage struct { Versions []FServiceVersion `json:"versions,omitempty" yaml:"versions,omitempty"` } -// id is used for sorting. -func (s FServicePackage) id() string { - if s.ID != nil { - return *s.ID - } +// sortKey is used for sorting. +func (s FServicePackage) sortKey() string { if s.Name != nil { return *s.Name } + if s.ID != nil { + return *s.ID + } return "" } diff --git a/file/types_test.go b/file/types_test.go index 497c4e89d..122adb5eb 100644 --- a/file/types_test.go +++ b/file/types_test.go @@ -39,6 +39,237 @@ protocols: ` ) +func Test_sortKey(t *testing.T) { + tests := []struct { + name string + sortable sortable + expectedKey string + }{ + { + sortable: &FService{ + Service: kong.Service{ + Name: kong.String("my-service"), + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-service", + }, + { + sortable: &FService{ + Service: kong.Service{ + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-id", + }, + { + sortable: FService{}, + expectedKey: "", + }, { + sortable: &FRoute{ + Route: kong.Route{ + Name: kong.String("my-route"), + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-route", + }, + { + sortable: FRoute{ + Route: kong.Route{ + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-id", + }, + { + sortable: FRoute{}, + expectedKey: "", + }, { + sortable: FUpstream{ + Upstream: kong.Upstream{ + Name: kong.String("my-upstream"), + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-upstream", + }, + { + sortable: FUpstream{ + Upstream: kong.Upstream{ + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-id", + }, + { + sortable: FUpstream{}, + expectedKey: "", + }, { + sortable: FTarget{ + Target: kong.Target{ + Target: kong.String("my-target"), + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-target", + }, + { + sortable: FTarget{ + Target: kong.Target{ + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-id", + }, + { + sortable: FTarget{}, + expectedKey: "", + }, { + sortable: FCertificate{ + Cert: kong.String("my-certificate"), + ID: kong.String("my-id"), + }, + expectedKey: "my-certificate", + }, + { + sortable: FCertificate{ + ID: kong.String("my-id"), + }, + expectedKey: "my-id", + }, + { + sortable: FCertificate{}, + expectedKey: "", + }, { + sortable: FCACertificate{ + CACertificate: kong.CACertificate{ + Cert: kong.String("my-ca-certificate"), + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-ca-certificate", + }, + { + sortable: FCACertificate{ + CACertificate: kong.CACertificate{ + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-id", + }, + { + sortable: FCACertificate{}, + expectedKey: "", + }, + { + sortable: FPlugin{ + Plugin: kong.Plugin{ + Name: kong.String("my-plugin"), + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-plugin", + }, + { + sortable: FPlugin{ + Plugin: kong.Plugin{ + Name: kong.String("my-plugin"), + ID: kong.String("my-id"), + Consumer: &kong.Consumer{ + ID: kong.String("my-consumer-id"), + }, + }, + }, + expectedKey: "my-pluginmy-consumer-id", + }, + { + sortable: FPlugin{ + Plugin: kong.Plugin{ + Name: kong.String("my-plugin"), + ID: kong.String("my-id"), + Route: &kong.Route{ + ID: kong.String("my-route-id"), + }, + }, + }, + expectedKey: "my-pluginmy-route-id", + }, + { + sortable: FPlugin{ + Plugin: kong.Plugin{ + Name: kong.String("my-plugin"), + ID: kong.String("my-id"), + Service: &kong.Service{ + ID: kong.String("my-service-id"), + }, + }, + }, + expectedKey: "my-pluginmy-service-id", + }, + + { + sortable: FPlugin{ + Plugin: kong.Plugin{ + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-id", + }, + { + sortable: FPlugin{}, + expectedKey: "", + }, + { + sortable: &FConsumer{ + Consumer: kong.Consumer{ + Username: kong.String("my-consumer"), + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-consumer", + }, + { + sortable: &FConsumer{ + Consumer: kong.Consumer{ + ID: kong.String("my-id"), + }, + }, + expectedKey: "my-id", + }, + { + sortable: FConsumer{}, + expectedKey: "", + }, + { + sortable: &FServicePackage{ + Name: kong.String("my-service-package"), + ID: kong.String("my-id"), + }, + expectedKey: "my-service-package", + }, + { + sortable: &FServicePackage{ + ID: kong.String("my-id"), + }, + expectedKey: "my-id", + }, + { + sortable: FServicePackage{}, + expectedKey: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + key := tt.sortable.sortKey() + if key != tt.expectedKey { + t.Errorf("Expected %v, but is %v", tt.expectedKey, key) + } + }) + } + +} + func TestPluginUnmarshalYAML(t *testing.T) { var p FPlugin assert := assert.New(t) diff --git a/file/writer.go b/file/writer.go index afb5ee49a..8287e40da 100644 --- a/file/writer.go +++ b/file/writer.go @@ -22,8 +22,8 @@ type WriteConfig struct { WithID bool } -func compareID(obj1, obj2 id) bool { - return strings.Compare(obj1.id(), obj2.id()) < 0 +func compareOrder(obj1, obj2 sortable) bool { + return strings.Compare(obj1.sortKey(), obj2.sortKey()) < 0 } // KongStateToFile writes a state object to file with filename. @@ -167,7 +167,7 @@ func populateServicePackages(kongState *state.KongState, file *Content, file.ServicePackages = append(file.ServicePackages, p) } sort.SliceStable(file.ServicePackages, func(i, j int) bool { - return compareID(file.ServicePackages[i], file.ServicePackages[j]) + return compareOrder(file.ServicePackages[i], file.ServicePackages[j]) }) return nil } @@ -186,7 +186,7 @@ func populateServices(kongState *state.KongState, file *Content, file.Services = append(file.Services, *s) } sort.SliceStable(file.Services, func(i, j int) bool { - return compareID(file.Services[i], file.Services[j]) + return compareOrder(file.Services[i], file.Services[j]) }) return nil } @@ -216,7 +216,7 @@ func fetchService(id string, kongState *state.KongState, config WriteConfig) (*F s.Plugins = append(s.Plugins, &FPlugin{Plugin: p.Plugin}) } sort.SliceStable(s.Plugins, func(i, j int) bool { - return compareID(s.Plugins[i], s.Plugins[j]) + return compareOrder(s.Plugins[i], s.Plugins[j]) }) for _, r := range routes { plugins, err := kongState.Plugins.GetAllByRouteID(*r.ID) @@ -239,12 +239,12 @@ func fetchService(id string, kongState *state.KongState, config WriteConfig) (*F route.Plugins = append(route.Plugins, &FPlugin{Plugin: p.Plugin}) } sort.SliceStable(route.Plugins, func(i, j int) bool { - return compareID(route.Plugins[i], route.Plugins[j]) + return compareOrder(route.Plugins[i], route.Plugins[j]) }) s.Routes = append(s.Routes, route) } sort.SliceStable(s.Routes, func(i, j int) bool { - return compareID(s.Routes[i], s.Routes[j]) + return compareOrder(s.Routes[i], s.Routes[j]) }) utils.ZeroOutID(&s, s.Name, config.WithID) utils.ZeroOutTimestamps(&s) @@ -280,12 +280,12 @@ func populateServicelessRoutes(kongState *state.KongState, file *Content, route.Plugins = append(route.Plugins, &FPlugin{Plugin: p.Plugin}) } sort.SliceStable(route.Plugins, func(i, j int) bool { - return compareID(route.Plugins[i], route.Plugins[j]) + return compareOrder(route.Plugins[i], route.Plugins[j]) }) file.Routes = append(file.Routes, *route) } sort.SliceStable(file.Routes, func(i, j int) bool { - return compareID(file.Routes[i], file.Routes[j]) + return compareOrder(file.Routes[i], file.Routes[j]) }) return nil } @@ -343,7 +343,7 @@ func populatePlugins(kongState *state.KongState, file *Content, } } sort.SliceStable(file.Plugins, func(i, j int) bool { - return compareID(file.Plugins[i], file.Plugins[j]) + return compareOrder(file.Plugins[i], file.Plugins[j]) }) return nil } @@ -368,7 +368,7 @@ func populateUpstreams(kongState *state.KongState, file *Content, u.Targets = append(u.Targets, &FTarget{Target: t.Target}) } sort.SliceStable(u.Targets, func(i, j int) bool { - return compareID(u.Targets[i], u.Targets[j]) + return compareOrder(u.Targets[i], u.Targets[j]) }) utils.ZeroOutID(&u, u.Name, config.WithID) utils.ZeroOutTimestamps(&u) @@ -376,7 +376,7 @@ func populateUpstreams(kongState *state.KongState, file *Content, file.Upstreams = append(file.Upstreams, u) } sort.SliceStable(file.Upstreams, func(i, j int) bool { - return compareID(file.Upstreams[i], file.Upstreams[j]) + return compareOrder(file.Upstreams[i], file.Upstreams[j]) }) return nil } @@ -413,7 +413,7 @@ func populateCertificates(kongState *state.KongState, file *Content, file.Certificates = append(file.Certificates, c) } sort.SliceStable(file.Certificates, func(i, j int) bool { - return compareID(file.Certificates[i], file.Certificates[j]) + return compareOrder(file.Certificates[i], file.Certificates[j]) }) return nil } @@ -431,7 +431,7 @@ func populateCACertificates(kongState *state.KongState, file *Content, file.CACertificates = append(file.CACertificates, c) } sort.SliceStable(file.CACertificates, func(i, j int) bool { - return compareID(file.CACertificates[i], file.CACertificates[j]) + return compareOrder(file.CACertificates[i], file.CACertificates[j]) }) return nil } @@ -459,7 +459,7 @@ func populateConsumers(kongState *state.KongState, file *Content, c.Plugins = append(c.Plugins, &FPlugin{Plugin: p.Plugin}) } sort.SliceStable(c.Plugins, func(i, j int) bool { - return compareID(c.Plugins[i], c.Plugins[j]) + return compareOrder(c.Plugins[i], c.Plugins[j]) }) // custom-entities associated with Consumer keyAuths, err := kongState.KeyAuths.GetAllByConsumerID(*c.ID) @@ -557,7 +557,7 @@ func populateConsumers(kongState *state.KongState, file *Content, file.RBACRoles = append(file.RBACRoles, r) } sort.SliceStable(file.Consumers, func(i, j int) bool { - return compareID(file.Consumers[i], file.Consumers[j]) + return compareOrder(file.Consumers[i], file.Consumers[j]) }) return nil } diff --git a/file/writer_test.go b/file/writer_test.go index 0b457c0fb..81153f844 100644 --- a/file/writer_test.go +++ b/file/writer_test.go @@ -42,6 +42,158 @@ func captureOutput(f func()) string { return <-out } +func Test_compareOrder(t *testing.T) { + tests := []struct { + name string + sortable1 sortable + sortable2 sortable + expected bool + }{ + { + sortable1: &FService{ + Service: kong.Service{ + Name: kong.String("my-service-1"), + ID: kong.String("my-id-1"), + }, + }, + sortable2: &FService{ + Service: kong.Service{ + Name: kong.String("my-service-2"), + ID: kong.String("my-id-2"), + }, + }, + expected: true, + }, + + { + sortable1: &FRoute{ + Route: kong.Route{ + Name: kong.String("my-route-1"), + ID: kong.String("my-id-1"), + }, + }, + sortable2: &FRoute{ + Route: kong.Route{ + Name: kong.String("my-route-2"), + ID: kong.String("my-id-2"), + }, + }, + expected: true, + }, + + { + sortable1: FUpstream{ + Upstream: kong.Upstream{ + Name: kong.String("my-upstream-1"), + ID: kong.String("my-id-1"), + }, + }, + sortable2: FUpstream{ + Upstream: kong.Upstream{ + Name: kong.String("my-upstream-2"), + ID: kong.String("my-id-2"), + }, + }, + expected: true, + }, + + { + sortable1: FTarget{ + Target: kong.Target{ + Target: kong.String("my-target-1"), + ID: kong.String("my-id-1"), + }, + }, + sortable2: FTarget{ + Target: kong.Target{ + Target: kong.String("my-target-2"), + ID: kong.String("my-id-2"), + }, + }, + expected: true, + }, + + { + sortable1: FCertificate{ + Cert: kong.String("my-certificate-1"), + ID: kong.String("my-id-1"), + }, + sortable2: FCertificate{ + Cert: kong.String("my-certificate-2"), + ID: kong.String("my-id-2"), + }, + expected: true, + }, + + { + sortable1: FCACertificate{ + CACertificate: kong.CACertificate{ + Cert: kong.String("my-ca-certificate-1"), + ID: kong.String("my-id-1"), + }, + }, + sortable2: FCACertificate{ + CACertificate: kong.CACertificate{ + Cert: kong.String("my-ca-certificate-2"), + ID: kong.String("my-id-2"), + }, + }, + expected: true, + }, + + { + sortable1: FPlugin{ + Plugin: kong.Plugin{ + Name: kong.String("my-plugin-1"), + ID: kong.String("my-id-1"), + }, + }, + sortable2: FPlugin{ + Plugin: kong.Plugin{ + Name: kong.String("my-plugin-2"), + ID: kong.String("my-id-2"), + }, + }, + expected: true, + }, + + { + sortable1: &FConsumer{ + Consumer: kong.Consumer{ + Username: kong.String("my-consumer-1"), + ID: kong.String("my-id-2"), + }, + }, + sortable2: &FConsumer{ + Consumer: kong.Consumer{ + Username: kong.String("my-consumer-2"), + ID: kong.String("my-id-2"), + }, + }, + expected: true, + }, + + { + sortable1: &FServicePackage{ + Name: kong.String("my-service-package-1"), + ID: kong.String("my-id-1"), + }, + sortable2: &FServicePackage{ + Name: kong.String("my-service-package-2"), + ID: kong.String("my-id-2"), + }, + expected: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if compareOrder(tt.sortable1, tt.sortable2) != tt.expected { + t.Errorf("Expected %v, but isn't", tt.expected) + } + }) + } +} + func TestWriteKongStateToStdoutEmptyState(t *testing.T) { var ks, _ = state.NewKongState() var filename = "-"