Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(konnect): add KongKey - KongKeySet binding #663

Merged
merged 5 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions config/samples/konnect_kongkeyset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,64 @@ spec:
name: key-set-1
tags:
- production
---
kind: KongKey
apiVersion: configuration.konghq.com/v1alpha1
metadata:
name: key-1
namespace: default
annotations:
konghq.com/tags: "infra"
spec:
controlPlaneRef:
type: konnectNamespacedRef
konnectNamespacedRef:
name: test1
keySetRef:
type: namespacedRef
namespacedRef:
name: key-set-1
tags:
- production
kid: kid
name: key-1
pem:
private_key: |
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCdLY9b3Pb/Fsxo
cIJAKxZQKEtqTU8k72BOvMBtzcIN9ekGFrUYB8GQgIJsPIUw4+c4XK99YNh0tVBE
/9W8OdyXwopzqNn+nRfrhXpxDu+BVvjQ/AENAHKqg8pJKhNTd4W6dAzxelLO/t7y
rlXbjGX/Ry/3ednPq6PpDcxvqgc+v7Rcmh+5dEKdIoIrppjUc2X38+LXcy9xOuML
FtxNtx+NB+5bNq31eooT9OKk3r7mA0gX4Su2DnIL+SLsdTIb0dnCBIydUpbLdYfd
dew1UGy2XtlWsxux3zoXjGe+RBtndUzPBvyb/k6g2QFAaIEwndPbwQ4fi9y4FrB7
hqjQa+OLAgMBAAECggEAMkWruCydHarLl04BAwgk+19f+7Cdc0lTCuwJOjWY70Er
loR1yKlWamMIFBlpWmFSh67xfE8Y/H8vnNodITZ6jVmuUd78VpklWPHY30dxKHPK
YoFvzppJkqtTbIJWKxir/551s1i2GrnfUkybbnzh9Lvuph9loKwb4YNF06NU7OcA
tgCk78oA/JpVa01PCJYmVy8zI4UERt/2mBzuummk8kJhPl+A7K9gVkNz6KSeQDGM
QUZ6gtiYtyg7nT+kI1H6LfwokxCljQ+MBuB62eehUsie7EmpgmJqbzesqnWfdbFp
IjCDn174R45o0FUD1QpcbQWxa39cdo4f6oP4My8szQKBgQDJT8Z7yfYAXeAyVeRD
tTrOWhXqKzj3DOO65n+Evwen9O4NlWKtbC6LeaogcrlJSuHhYlAShdgrBy6DLWi8
DEwozbK5YvpKbQ8u03rJYnfM6nN57gvm49SgsaoUPO4FlZMt1V3VC6kG2K4YbP8Y
OWy5FCdYPRlOtPp4CsFQ4xzbjQKBgQDH4IIMBT667V+7fWC/YyvUqJoIimuZcVzP
zmxICWVP9u4VKCHw46sbqukCw56bMYD8X7zu16Sbkkc3YzeOP6n4NGcLUzIFkweq
nzKdxZ6wj00x+mHT0/i/B8IZDYSkRFHF7ISV3Z8B9FuJXfsk5xGHVc47jVOTyKPb
XuLzcAlpdwKBgAsij37/X80LZEBEgfjAyHzrfLTUKTV5EAuhfkIwctL2eEhmD+w5
xKVQWHms/tSwAKh/0KAFqTxQDGGTHGzyXTAQmKcqc1+0gpd7eRo0iR3bhgGjiiL+
TR+KVDcEW8IRUO/DEoqbN4E6cP7G4KFNY9ck5zw5PPIejpAfQCwiM9FtAoGAW8Kn
EWurA9gMFiAWNWcK7UNGC9u4UCZqDIDg1yVxHIfpf08AXf23RSludbVm8CqG49Xz
/9aCHGXIShZDoAt8NZWhJOLZ2RNJ9rvFWgcqtjXjo6kmFkB/NvwR0LyTA3LV876E
k+S9pgEPsP2zWZq3QmFTH6XfE76N8x0ZpdbuizsCgYBBDNh8AfKbaEdo90bQi8No
sNqbHFAc12H6qxqnRl/pDvoY34wBVeZP3QEfb/XeOO2BVrcx6tGvIosy2lkOJtrh
ckY/QO1OLvcDtDgMA6qOr1rAROP/aWhuhJg1Aw50vCuy3z96CfUVSJBG+r0v7HvO
ZNgrh9kB0qmomKcjwwJlKQ==
-----END PRIVATE KEY-----
public_key: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnS2PW9z2/xbMaHCCQCsW
UChLak1PJO9gTrzAbc3CDfXpBha1GAfBkICCbDyFMOPnOFyvfWDYdLVQRP/VvDnc
l8KKc6jZ/p0X64V6cQ7vgVb40PwBDQByqoPKSSoTU3eFunQM8XpSzv7e8q5V24xl
/0cv93nZz6uj6Q3Mb6oHPr+0XJofuXRCnSKCK6aY1HNl9/Pi13MvcTrjCxbcTbcf
jQfuWzat9XqKE/TipN6+5gNIF+Ertg5yC/ki7HUyG9HZwgSMnVKWy3WH3XXsNVBs
tl7ZVrMbsd86F4xnvkQbZ3VMzwb8m/5OoNkBQGiBMJ3T28EOH4vcuBawe4ao0Gvj
iwIDAQAB
-----END PUBLIC KEY-----
14 changes: 14 additions & 0 deletions controller/konnect/conditions/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ const (
KongUpstreamRefReasonInvalid = "Invalid"
)

const (
// KeySetRefValidConditionType is the type of the condition that indicates
// whether the KeySet reference is valid and points to an existing
// KeySet.
KeySetRefValidConditionType = "KeySetRefValid"

// KeySetRefReasonValid is the reason used with the KeySetRefValid
// condition type indicating that the KeySet reference is valid.
KeySetRefReasonValid = "Valid"
// KeySetRefReasonInvalid is the reason used with the KeySetRefValid
// condition type indicating that the KeySet reference is invalid.
KeySetRefReasonInvalid = "Invalid"
)

const (
// KongCertificateRefValidConditionType is the type of the condition that indicates
// whether the KongCertificate reference is valid and points to an existing KongCertificate
Expand Down
25 changes: 25 additions & 0 deletions controller/konnect/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,28 @@ type ReferencedKongCertificateDoesNotExist struct {
func (e ReferencedKongCertificateDoesNotExist) Error() string {
return fmt.Sprintf("referenced Kong Certificate %s does not exist: %v", e.Reference, e.Err)
}

// ReferencedKongKeySetDoesNotExist is an error type that is returned when
// a Konnect entity references a KongKeySet which does not exist.
type ReferencedKongKeySetDoesNotExist struct {
Reference types.NamespacedName
Err error
}

// Error implements the error interface.
func (e ReferencedKongKeySetDoesNotExist) Error() string {
return fmt.Sprintf("referenced KongKeySet %s does not exist: %v", e.Reference, e.Err)
}

// ReferencedKongKeySetIsBeingDeleted is an error type that is returned when
// a Konnect entity references a KongKeySet which is being deleted.
type ReferencedKongKeySetIsBeingDeleted struct {
Reference types.NamespacedName
DeletionTimestamp time.Time
}

// Error implements the error interface.
func (e ReferencedKongKeySetIsBeingDeleted) Error() string {
return fmt.Sprintf("referenced KongKeySet %s is being deleted (deletion timestamp: %s)",
e.Reference, e.DeletionTimestamp)
}
56 changes: 56 additions & 0 deletions controller/konnect/index_kongkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package konnect

import (
"sigs.k8s.io/controller-runtime/pkg/client"

configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1"
)

const (
// IndexFieldKongKeyOnKongKeySetReference is the index field for KongKey-> KongKeySet.
IndexFieldKongKeyOnKongKeySetReference = "kongKeySetRef"

// IndexFieldKongKeyOnKonnectGatewayControlPlane is the index field for KongKey -> KonnectGatewayControlPlane.
IndexFieldKongKeyOnKonnectGatewayControlPlane = "kongKeyKonnectGatewayControlPlaneRef"
)

// IndexOptionsForKongKey returns required Index options for KongKey reconclier.
func IndexOptionsForKongKey() []ReconciliationIndexOption {
return []ReconciliationIndexOption{
{
IndexObject: &configurationv1alpha1.KongKey{},
IndexField: IndexFieldKongKeyOnKongKeySetReference,
ExtractValue: kongKeySetRefFromKongKey,
},
{
IndexObject: &configurationv1alpha1.KongKey{},
IndexField: IndexFieldKongKeyOnKonnectGatewayControlPlane,
ExtractValue: konnectGatewayControlPlaneRefFromKongKey,
},
}
}

// kongKeySetRefFromKongKey returns namespace/name of referenced KongKeySet in KongKey spec.
func kongKeySetRefFromKongKey(obj client.Object) []string {
key, ok := obj.(*configurationv1alpha1.KongKey)
if !ok {
return nil
}

if key.Spec.KeySetRef == nil ||
key.Spec.KeySetRef.Type != configurationv1alpha1.KeySetRefNamespacedRef ||
key.Spec.KeySetRef.NamespacedRef == nil {
return nil
}

return []string{key.GetNamespace() + "/" + key.Spec.KeySetRef.NamespacedRef.Name}
}

// kongPluginReferencesFromKongKey returns namespace/name of referenced KonnectGatewayControlPlane in KongKey spec.
func konnectGatewayControlPlaneRefFromKongKey(obj client.Object) []string {
key, ok := obj.(*configurationv1alpha1.KongKey)
if !ok {
return nil
}
return controlPlaneKonnectNamespacedRefAsSlice(key)
}
166 changes: 125 additions & 41 deletions controller/konnect/ops/ops_kongkey_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package ops

import (
"sort"
"testing"

"github.com/google/uuid"
sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components"
"github.com/samber/lo"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -12,55 +13,138 @@ import (
konnectconsts "github.com/kong/gateway-operator/controller/konnect/consts"

configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1"
konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1"
)

func TestKongKeyToKeyInput(t *testing.T) {
key := &configurationv1alpha1.KongKey{
TypeMeta: metav1.TypeMeta{
Kind: "KongKey",
APIVersion: "configuration.konghq.com/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "key-1",
Namespace: "default",
Generation: 2,
UID: k8stypes.UID(uuid.NewString()),
Annotations: map[string]string{
konnectconsts.AnnotationTags: "tag1,tag2,duplicate",
testCases := []struct {
name string
key *configurationv1alpha1.KongKey
expectedOutput sdkkonnectcomp.KeyInput
}{
{
name: "kong key with all fields set without key set",
key: &configurationv1alpha1.KongKey{
TypeMeta: metav1.TypeMeta{
Kind: "KongKey",
APIVersion: "configuration.konghq.com/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "key-1",
Namespace: "default",
Generation: 2,
UID: k8stypes.UID("key-uid"),
Annotations: map[string]string{
konnectconsts.AnnotationTags: "tag1,tag2,duplicate",
},
},
Spec: configurationv1alpha1.KongKeySpec{
KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{
KID: "kid",
Name: lo.ToPtr("name"),
JWK: lo.ToPtr("jwk"),
PEM: &configurationv1alpha1.PEMKeyPair{
PublicKey: "public",
PrivateKey: "private",
},
Tags: []string{"tag3", "tag4", "duplicate"},
},
},
},
expectedOutput: sdkkonnectcomp.KeyInput{
Kid: "kid",
Name: lo.ToPtr("name"),
Jwk: lo.ToPtr("jwk"),
Pem: &sdkkonnectcomp.Pem{
PublicKey: lo.ToPtr("public"),
PrivateKey: lo.ToPtr("private"),
},
Tags: []string{
"duplicate",
"k8s-generation:2",
"k8s-group:configuration.konghq.com",
"k8s-kind:KongKey",
"k8s-name:key-1",
"k8s-namespace:default",
"k8s-uid:key-uid",
"k8s-version:v1alpha1",
"tag1",
"tag2",
"tag3",
"tag4",
},
},
},
Spec: configurationv1alpha1.KongKeySpec{
KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{
KID: "kid",
{
name: "kong key with all fields set with key set",
key: &configurationv1alpha1.KongKey{
TypeMeta: metav1.TypeMeta{
Kind: "KongKey",
APIVersion: "configuration.konghq.com/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "key-1",
Namespace: "default",
Generation: 2,
UID: k8stypes.UID("key-uid"),
Annotations: map[string]string{
konnectconsts.AnnotationTags: "tag1,tag2,duplicate",
},
},
Spec: configurationv1alpha1.KongKeySpec{
KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{
KID: "kid",
Name: lo.ToPtr("name"),
JWK: lo.ToPtr("jwk"),
PEM: &configurationv1alpha1.PEMKeyPair{
PublicKey: "public",
PrivateKey: "private",
},
Tags: []string{"tag3", "tag4", "duplicate"},
},
},
Status: configurationv1alpha1.KongKeyStatus{
Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneAndKeySetRef{
KeySetID: "key-set-id",
},
},
},
expectedOutput: sdkkonnectcomp.KeyInput{
Kid: "kid",
Name: lo.ToPtr("name"),
JWK: lo.ToPtr("jwk"),
PEM: &configurationv1alpha1.PEMKeyPair{
PublicKey: "public",
PrivateKey: "private",
Jwk: lo.ToPtr("jwk"),
Pem: &sdkkonnectcomp.Pem{
PublicKey: lo.ToPtr("public"),
PrivateKey: lo.ToPtr("private"),
},
Set: &sdkkonnectcomp.Set{
ID: lo.ToPtr("key-set-id"),
},
Tags: []string{
"duplicate",
"k8s-generation:2",
"k8s-group:configuration.konghq.com",
"k8s-kind:KongKey",
"k8s-name:key-1",
"k8s-namespace:default",
"k8s-uid:key-uid",
"k8s-version:v1alpha1",
"tag1",
"tag2",
"tag3",
"tag4",
},
Tags: []string{"tag3", "tag4", "duplicate"},
},
},
}
output := kongKeyToKeyInput(key)
expectedTags := []string{
"k8s-generation:2",
"k8s-kind:KongKey",
"k8s-name:key-1",
"k8s-uid:" + string(key.GetUID()),
"k8s-version:v1alpha1",
"k8s-group:configuration.konghq.com",
"k8s-namespace:default",
"tag1",
"tag2",
"tag3",
"tag4",
"duplicate",

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
output := kongKeyToKeyInput(tc.key)

// Tags order is not guaranteed, so we need to sort them before comparing.
pmalek marked this conversation as resolved.
Show resolved Hide resolved
sort.Strings(output.Tags)
require.Equal(t, tc.expectedOutput, output)
})
}
require.ElementsMatch(t, expectedTags, output.Tags)
require.Equal(t, "kid", output.Kid)
require.Equal(t, "name", *output.Name)
require.Equal(t, "jwk", *output.Jwk)
require.Equal(t, "public", *output.Pem.PublicKey)
require.Equal(t, "private", *output.Pem.PrivateKey)
}
Loading
Loading