Skip to content

Commit

Permalink
feat: assign Service, Route and Consumer entities stable IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
czeslavo committed Apr 25, 2023
1 parent eb974e7 commit 30b8c97
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Adding a new version? You'll need three changes:
[#3832](https://github.com/Kong/kubernetes-ingress-controller/pull/3832)
- Added license agent for Konnect-managed instances.
[#3883](https://github.com/Kong/kubernetes-ingress-controller/pull/3883)
- `Service`, `Route` and `Consumer` Kong entities now get assigned deterministic
IDs based on their unique properties (name, username, etc.) instead of random
UUIDs.
[#3933](https://github.com/Kong/kubernetes-ingress-controller/pull/3933)

### Fixed

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.3.0
github.com/kong/deck v1.20.0
github.com/kong/go-kong v0.40.0
github.com/kong/go-kong v0.41.0
github.com/kong/kubernetes-telemetry v0.0.2
github.com/kong/kubernetes-testing-framework v0.30.1
github.com/lithammer/dedent v1.1.0
Expand Down Expand Up @@ -114,7 +114,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand Down
12 changes: 7 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
Expand Down Expand Up @@ -281,8 +282,8 @@ github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v2.20.0+incompatible h1:4Xh3bDzO29j4TWNOI+24ubc0vbVFMg2PMnXKxK54/CA=
github.com/gobuffalo/flect v0.2.4/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
Expand Down Expand Up @@ -467,8 +468,9 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
Expand Down Expand Up @@ -510,8 +512,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kong/deck v1.20.0 h1:q8+8VBnvv0O+9mYjcPdJP5prG3KzbvR4XfePwkTx+Zc=
github.com/kong/deck v1.20.0/go.mod h1:yJWEu6/xnYiaNBg2vP4EsYLtbt33J67Zsolye3JpJmI=
github.com/kong/go-kong v0.40.0 h1:6rd70L4GbPz90j3ey+wjHd4aC21uFgbqsoASJhbcbdU=
github.com/kong/go-kong v0.40.0/go.mod h1:t3siZEEGBB3FA5EQv9CL5EcaiogPTG0A175VQ6KvEHE=
github.com/kong/go-kong v0.41.0 h1:0rn+Haf8wfT0VWFVjQPNETLju5ZuNhfbrHYyjpliDBU=
github.com/kong/go-kong v0.41.0/go.mod h1:S/Mx/ZjgwsREPcpMXgCFt5wX7LBpyFlTKENri7E3KTg=
github.com/kong/kubernetes-telemetry v0.0.2 h1:ZLoctQzvo0onCxbMgFMGsIGu6qAXWaMrd4o5Rv//C68=
github.com/kong/kubernetes-telemetry v0.0.2/go.mod h1:lOeCASSR93hssoiOI2HUHoMFLffo/4lLsk74pIqMcyo=
github.com/kong/kubernetes-testing-framework v0.30.1 h1:FZCThCgf2xOi/pUbSzd5hW1ghUnZYihmvy9a3DHMRAE=
Expand Down Expand Up @@ -689,7 +691,7 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand Down
27 changes: 27 additions & 0 deletions internal/dataplane/kongstate/kongstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,30 @@ func globalPlugins(log logrus.FieldLogger, s store.Storer) ([]Plugin, error) {
func (ks *KongState) FillPlugins(log logrus.FieldLogger, s store.Storer) {
ks.Plugins = buildPlugins(log, s, ks.getPluginRelations())
}

// FillIDs iterates over the KongState and fills in the ID field for each entity
// that supports the FillID method (these are Service, Route and Consumer). It makes
// their IDs deterministic, enabling their correct identification in external systems
// (e.g. Konnect Analytics).
func (ks *KongState) FillIDs(logger logrus.FieldLogger) {
for svcIndex, svc := range ks.Services {
if err := svc.FillID(); err != nil {
logger.WithError(err).Errorf("failed to fill ID for service %s", *svc.Name)
}
ks.Services[svcIndex] = svc

for routeIndex, route := range svc.Routes {
if err := route.FillID(); err != nil {
logger.WithError(err).Errorf("failed to fill ID for route %s", *route.Name)
}
ks.Services[svcIndex].Routes[routeIndex] = route
}
}

for consumerIndex, consumer := range ks.Consumers {
if err := consumer.FillID(); err != nil {
logger.WithError(err).Errorf("failed to fill ID for consumer %s", *consumer.Username)
}
ks.Consumers[consumerIndex] = consumer
}
}
119 changes: 119 additions & 0 deletions internal/dataplane/kongstate/kongstate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/kong/go-kong/kong"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand Down Expand Up @@ -396,3 +397,121 @@ func TestFillConsumersAndCredentials(t *testing.T) {
assert.Equal(t, want.Consumers[0].Oauth2Creds[0].RedirectURIs, state.Consumers[0].Oauth2Creds[0].RedirectURIs)
})
}

func TestKongState_FillIDs(t *testing.T) {
testCases := []struct {
name string
state KongState
expect func(t *testing.T, s KongState)
}{
{
name: "fills service IDs",
state: KongState{
Services: []Service{
{
Service: kong.Service{
Name: kong.String("service.foo"),
},
},
{
Service: kong.Service{
Name: kong.String("service.bar"),
},
},
},
},
expect: func(t *testing.T, s KongState) {
require.NotEmpty(t, s.Services[0].ID)
require.NotEmpty(t, s.Services[1].ID)
},
},
{
name: "fills route IDs",
state: KongState{
Services: []Service{
{
Service: kong.Service{
Name: kong.String("service.foo"),
},
Routes: []Route{
{
Route: kong.Route{
Name: kong.String("route.foo"),
},
},
{
Route: kong.Route{
Name: kong.String("route.bar"),
},
},
},
},
},
},
expect: func(t *testing.T, s KongState) {
require.NotEmpty(t, s.Services[0].ID)
require.NotEmpty(t, s.Services[0].Routes[0].ID)
require.NotEmpty(t, s.Services[0].Routes[1].ID)
},
},
{
name: "fills consumer IDs",
state: KongState{
Consumers: []Consumer{
{
Consumer: kong.Consumer{
Username: kong.String("user.foo"),
},
},
{
Consumer: kong.Consumer{
Username: kong.String("user.bar"),
},
},
},
},
expect: func(t *testing.T, s KongState) {
require.NotEmpty(t, s.Consumers[0].ID)
require.NotEmpty(t, s.Consumers[1].ID)
},
},
{
name: "fills services, routes, and consumer IDs",
state: KongState{
Services: []Service{
{
Service: kong.Service{
Name: kong.String("service.foo"),
},
Routes: []Route{
{
Route: kong.Route{
Name: kong.String("route.bar"),
},
},
},
},
},
Consumers: []Consumer{
{
Consumer: kong.Consumer{
Username: kong.String("user.baz"),
},
},
},
},
expect: func(t *testing.T, s KongState) {
require.NotEmpty(t, s.Services[0].ID)
require.NotEmpty(t, s.Services[0].Routes[0].ID)
require.NotEmpty(t, s.Consumers[0].ID)
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.state.FillIDs(logrus.New())
tc.expect(t, tc.state)
})
}
}
3 changes: 3 additions & 0 deletions internal/dataplane/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ func (p *Parser) Build() (*kongstate.KongState, []failures.ResourceFailure) {
result.Licenses = append(result.Licenses, *p.license)
}

// generate IDs for Kong entities
result.FillIDs(p.logger)

return &result, p.popTranslationFailures()
}

Expand Down
86 changes: 86 additions & 0 deletions internal/dataplane/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4928,6 +4928,92 @@ func TestCertificate(t *testing.T) {
})
}

func TestParser_FillsEntitiesIDs(t *testing.T) {
s, err := store.NewFakeStore(store.FakeObjects{
Services: []*corev1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc.foo",
Namespace: "ns",
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "foo",
Port: 80,
},
},
},
},
},
IngressesV1: []*netv1.Ingress{
{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress.foo",
Namespace: "ns",
Annotations: map[string]string{
annotations.IngressClassKey: annotations.DefaultIngressClass,
},
},
Spec: netv1.IngressSpec{
Rules: []netv1.IngressRule{
{
Host: "foo.com",
IngressRuleValue: netv1.IngressRuleValue{
HTTP: &netv1.HTTPIngressRuleValue{
Paths: []netv1.HTTPIngressPath{
{
Path: "/foo",
Backend: netv1.IngressBackend{
Service: &netv1.IngressServiceBackend{
Name: "svc.foo",
Port: netv1.ServiceBackendPort{
Number: 80,
},
},
},
},
},
},
},
},
},
},
},
},
KongConsumers: []*configurationv1.KongConsumer{
{
ObjectMeta: metav1.ObjectMeta{
Name: "user.foo",
Namespace: "ns",
Annotations: map[string]string{
annotations.IngressClassKey: annotations.DefaultIngressClass,
},
},
Username: "user.foo",
},
},
})
require.NoError(t, err)
p := mustNewParser(t, s)

state, translationFailures := p.Build()
require.Empty(t, translationFailures)
require.NotNil(t, state)

require.Len(t, state.Services, 1)
require.NotNil(t, state.Services[0].ID)
assert.Equal(t, "10157742-edd5-51f6-8141-f21dc8017e87", *state.Services[0].ID, "expected deterministic ID")

require.Len(t, state.Services[0].Routes, 1)
require.NotNil(t, state.Services[0].Routes[0].ID)
assert.Equal(t, "1474bf56-c263-5919-b3e6-e9bc6e4b237f", *state.Services[0].Routes[0].ID, "expected deterministic ID")

require.Len(t, state.Consumers, 1)
require.NotNil(t, state.Consumers[0].ID)
assert.Equal(t, "93c4b796-7cc1-5f86-834c-3bbdf00a806c", *state.Consumers[0].ID, "expected deterministic ID")
}

func mustNewParser(t *testing.T, storer store.Storer) *Parser {
p, err := NewParser(logrus.New(), storer)
require.NoError(t, err)
Expand Down

0 comments on commit 30b8c97

Please sign in to comment.