diff --git a/api/configuration/v1alpha1/key_set_ref.go b/api/configuration/v1alpha1/key_set_ref.go new file mode 100644 index 0000000..42a488c --- /dev/null +++ b/api/configuration/v1alpha1/key_set_ref.go @@ -0,0 +1,47 @@ +package v1alpha1 + +// KeySetRefType is the enum type for the KeySetRef. +// +kubebuilder:validation:Enum=konnectID;namespacedRef +type KeySetRefType string + +const ( + // KeySetRefKonnectID is the type for the KonnectID KeySetRef. + // It is used to reference a KeySet entity by its ID on the Konnect platform. + KeySetRefKonnectID KeySetRefType = "konnectID" + + // KeySetRefNamespacedRef is the type for the KeySetRef. + // It is used to reference a KeySet entity inside the cluster + // using a namespaced reference. + KeySetRefNamespacedRef KeySetRefType = "namespacedRef" +) + +// KeySetRef is the schema for the KeySetRef type. +// It is used to reference a KeySet entity. +// +kubebuilder:validation:XValidation:rule="self.type == 'namespacedRef' ? has(self.namespacedRef) : true", message="when type is namespacedRef, namespacedRef must be set" +// +kubebuilder:validation:XValidation:rule="self.type == 'konnectID' ? has(self.konnectID) : true", message="when type is konnectID, konnectID must be set" +type KeySetRef struct { + // Type defines type of the KeySet object reference. It can be one of: + // - konnectID + // - namespacedRef + Type KeySetRefType `json:"type"` + + // KonnectID is the schema for the KonnectID type. + // This field is required when the Type is konnectID. + // +optional + KonnectID *string `json:"konnectID,omitempty"` + + // NamespacedRef is a reference to a KeySet entity inside the cluster. + // This field is required when the Type is namespacedRef. + // +optional + NamespacedRef *KeySetNamespacedRef `json:"namespacedRef,omitempty"` +} + +// KeySetNamespacedRef is the schema for the KeySetNamespacedRef type. +type KeySetNamespacedRef struct { + // Name is the name of the KeySet object. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + + // TODO: Implement cross namespace references: + // https://github.com/Kong/kubernetes-configuration/issues/36 +} diff --git a/api/configuration/v1alpha1/kongkey_types.go b/api/configuration/v1alpha1/kongkey_types.go new file mode 100644 index 0000000..aba6ea3 --- /dev/null +++ b/api/configuration/v1alpha1/kongkey_types.go @@ -0,0 +1,126 @@ +/* +Copyright 2024 Kong, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// KongKey is the schema for KongKey API which defines a KongKey entity. +// +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Namespaced +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Programmed",description="The Resource is Programmed on Konnect",type=string,JSONPath=`.status.conditions[?(@.type=='Programmed')].status` +// +kubebuilder:validation:XValidation:rule="!has(oldSelf.spec.controlPlaneRef) || has(self.spec.controlPlaneRef)", message="controlPlaneRef is required once set" +// +kubebuilder:validation:XValidation:rule="(!self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True')) ? true : oldSelf.spec.controlPlaneRef == self.spec.controlPlaneRef", message="spec.controlPlaneRef is immutable when an entity is already Programmed" +// +kubebuilder:validation:XValidation:rule="!has(self.spec.controlPlaneRef) ? true : !has(self.spec.controlPlaneRef.konnectNamespacedRef) ? true : !has(self.spec.controlPlaneRef.konnectNamespacedRef.__namespace__)", message="spec.controlPlaneRef cannot specify namespace for namespaced resource - it's not supported yet" +type KongKey struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KongKeySpec `json:"spec"` + + // +kubebuilder:default={conditions: {{type: "Programmed", status: "Unknown", reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}}} + Status KongKeyStatus `json:"status,omitempty"` +} + +// KongKeySpec defines the spec for a KongKey. +// +type KongKeySpec struct { + // ControlPlaneRef is a reference to a Konnect ControlPlane this KongKey is associated with. + // +optional + ControlPlaneRef *ControlPlaneRef `json:"controlPlaneRef,omitempty"` + + // KeySetRef is a reference to a KongKeySet this KongKey is attached to. + // ControlPlane referenced by a KongKeySet must be the same as the ControlPlane referenced by the KongKey. + // +optional + KeySetRef *KeySetRef `json:"keySetRef,omitempty"` + + // KongKeyAPISpec are the attributes of the KongKey itself. + KongKeyAPISpec `json:",inline"` +} + +// KongKeyAPISpec defines the attributes of a Kong Key. +// +kubebuilder:validation:XValidation:rule="has(self.jwk) || has(self.pem)", message="Either 'jwk' or 'pem' must be set" +type KongKeyAPISpec struct { + // KID is a unique identifier for a key. + // When JWK is provided, KID has to match the KID in the JWK. + // +kubebuilder:validation:MinLength=1 + KID string `json:"kid"` + + // Name is an optional name to associate with the given key. + // +optional + Name *string `json:"name,omitempty"` + + // JWK is a JSON Web Key represented as a string. + // The JWK must contain a KID field that matches the KID in the KongKey. + // Either JWK or PEM must be set. + // +optional + JWK *string `json:"jwk,omitempty"` + + // PEM is a keypair in PEM format. + // Either JWK or PEM must be set. + // +optional + PEM *PEMKeyPair `json:"pem,omitempty"` + + // Tags is an optional set of strings associated with the Key for grouping and filtering. + // +optional + Tags []string `json:"tags,omitempty"` +} + +// PEMKeyPair defines a keypair in PEM format. +type PEMKeyPair struct { + // The private key in PEM format. + // +kubebuilder:validation:MinLength=1 + PrivateKey string `json:"private_key"` + + // The public key in PEM format. + // +kubebuilder:validation:MinLength=1 + PublicKey string `json:"public_key"` +} + +// KongKeyStatus defines the status for a KongKey. +type KongKeyStatus struct { + // Konnect contains the Konnect entity status. + // +optional + Konnect *konnectv1alpha1.KonnectEntityStatusWithControlPlaneAndKeySetRef `json:"konnect,omitempty"` + + // Conditions describe the status of the Konnect entity. + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true + +// KongKeyList contains a list of Kong Targets. +type KongKeyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KongKey `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KongKey{}, &KongKeyList{}) +} diff --git a/api/configuration/v1alpha1/zz_generated.deepcopy.go b/api/configuration/v1alpha1/zz_generated.deepcopy.go index f6ecb43..2a6a27f 100644 --- a/api/configuration/v1alpha1/zz_generated.deepcopy.go +++ b/api/configuration/v1alpha1/zz_generated.deepcopy.go @@ -155,6 +155,46 @@ func (in *IngressClassParametersSpec) DeepCopy() *IngressClassParametersSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeySetNamespacedRef) DeepCopyInto(out *KeySetNamespacedRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeySetNamespacedRef. +func (in *KeySetNamespacedRef) DeepCopy() *KeySetNamespacedRef { + if in == nil { + return nil + } + out := new(KeySetNamespacedRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeySetRef) DeepCopyInto(out *KeySetRef) { + *out = *in + if in.KonnectID != nil { + in, out := &in.KonnectID, &out.KonnectID + *out = new(string) + **out = **in + } + if in.NamespacedRef != nil { + in, out := &in.NamespacedRef, &out.NamespacedRef + *out = new(KeySetNamespacedRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeySetRef. +func (in *KeySetRef) DeepCopy() *KeySetRef { + if in == nil { + return nil + } + out := new(KeySetRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KongCACertificate) DeepCopyInto(out *KongCACertificate) { *out = *in @@ -507,6 +547,153 @@ func (in *KongCustomEntityStatus) DeepCopy() *KongCustomEntityStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KongKey) DeepCopyInto(out *KongKey) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KongKey. +func (in *KongKey) DeepCopy() *KongKey { + if in == nil { + return nil + } + out := new(KongKey) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KongKey) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KongKeyAPISpec) DeepCopyInto(out *KongKeyAPISpec) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.JWK != nil { + in, out := &in.JWK, &out.JWK + *out = new(string) + **out = **in + } + if in.PEM != nil { + in, out := &in.PEM, &out.PEM + *out = new(PEMKeyPair) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KongKeyAPISpec. +func (in *KongKeyAPISpec) DeepCopy() *KongKeyAPISpec { + if in == nil { + return nil + } + out := new(KongKeyAPISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KongKeyList) DeepCopyInto(out *KongKeyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KongKey, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KongKeyList. +func (in *KongKeyList) DeepCopy() *KongKeyList { + if in == nil { + return nil + } + out := new(KongKeyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KongKeyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KongKeySpec) DeepCopyInto(out *KongKeySpec) { + *out = *in + if in.ControlPlaneRef != nil { + in, out := &in.ControlPlaneRef, &out.ControlPlaneRef + *out = new(ControlPlaneRef) + (*in).DeepCopyInto(*out) + } + if in.KeySetRef != nil { + in, out := &in.KeySetRef, &out.KeySetRef + *out = new(KeySetRef) + (*in).DeepCopyInto(*out) + } + in.KongKeyAPISpec.DeepCopyInto(&out.KongKeyAPISpec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KongKeySpec. +func (in *KongKeySpec) DeepCopy() *KongKeySpec { + if in == nil { + return nil + } + out := new(KongKeySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KongKeyStatus) DeepCopyInto(out *KongKeyStatus) { + *out = *in + if in.Konnect != nil { + in, out := &in.Konnect, &out.Konnect + *out = new(konnectv1alpha1.KonnectEntityStatusWithControlPlaneAndKeySetRef) + **out = **in + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KongKeyStatus. +func (in *KongKeyStatus) DeepCopy() *KongKeyStatus { + if in == nil { + return nil + } + out := new(KongKeyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KongLicense) DeepCopyInto(out *KongLicense) { *out = *in @@ -1664,6 +1851,21 @@ func (in *ObjectReference) DeepCopy() *ObjectReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PEMKeyPair) DeepCopyInto(out *PEMKeyPair) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PEMKeyPair. +func (in *PEMKeyPair) DeepCopy() *PEMKeyPair { + if in == nil { + return nil + } + out := new(PEMKeyPair) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PluginRef) DeepCopyInto(out *PluginRef) { *out = *in diff --git a/api/configuration/v1alpha1/zz_generated_konnect_funcs.go b/api/configuration/v1alpha1/zz_generated_konnect_funcs.go index 807de5e..4bee7c5 100644 --- a/api/configuration/v1alpha1/zz_generated_konnect_funcs.go +++ b/api/configuration/v1alpha1/zz_generated_konnect_funcs.go @@ -9,6 +9,64 @@ import ( // Code generated by scripts/konnect-funcs/main.go; DO NOT EDIT. +func (k *KongKey) initKonnectStatus() { + k.Status.Konnect = &konnectv1alpha1.KonnectEntityStatusWithControlPlaneAndKeySetRef{} +} + +// GetKonnectStatus returns the Konnect status contained in the KongKey status. +func (k *KongKey) GetKonnectStatus() *konnectv1alpha1.KonnectEntityStatus { + if k.Status.Konnect == nil { + return nil + } + return &k.Status.Konnect.KonnectEntityStatus +} + +// GetKonnectID returns the Konnect ID in the KongKey status. +func (k *KongKey) GetKonnectID() string { + if k.Status.Konnect == nil { + return "" + } + return k.Status.Konnect.ID +} + +// SetKonnectID sets the Konnect ID in the KongKey status. +func (k *KongKey) SetKonnectID(id string) { + if k.Status.Konnect == nil { + k.initKonnectStatus() + } + k.Status.Konnect.ID = id +} + +// GetControlPlaneID returns the ControlPlane ID in the KongKey status. +func (k *KongKey) GetControlPlaneID() string { + if k.Status.Konnect == nil { + return "" + } + return k.Status.Konnect.ControlPlaneID +} + +// SetControlPlaneID sets the ControlPlane ID in the KongKey status. +func (k *KongKey) SetControlPlaneID(id string) { + if k.Status.Konnect == nil { + k.initKonnectStatus() + } + k.Status.Konnect.ControlPlaneID = id +} + +// GetTypeName returns the KongKey Kind name +func (k KongKey) GetTypeName() string { + return "KongKey" +} + +// GetConditions returns the Status Conditions +func (k *KongKey) GetConditions() []metav1.Condition { + return k.Status.Conditions +} + +// SetConditions sets the Status Conditions +func (k *KongKey) SetConditions(conditions []metav1.Condition) { + k.Status.Conditions = conditions +} func (c *KongCredentialBasicAuth) initKonnectStatus() { c.Status.Konnect = &konnectv1alpha1.KonnectEntityStatusWithControlPlaneAndConsumerRefs{} } diff --git a/api/konnect/v1alpha1/konnect_entity_status.go b/api/konnect/v1alpha1/konnect_entity_status.go index fbad789..701b45a 100644 --- a/api/konnect/v1alpha1/konnect_entity_status.go +++ b/api/konnect/v1alpha1/konnect_entity_status.go @@ -127,3 +127,33 @@ type KonnectEntityStatusWithControlPlaneAndUpstreamRefs struct { // UpstreamID is the Konnect ID of the Upstream this entity is associated with. UpstreamID string `json:"upstreamID,omitempty"` } + +type KonnectEntityStatusWithControlPlaneAndKeySetRef struct { + KonnectEntityStatus `json:",inline"` + + // ControlPlaneID is the Konnect ID of the ControlPlane this entity is associated with. + ControlPlaneID string `json:"controlPlaneID,omitempty"` + + // KeySetID is the Konnect ID of the KeySet this entity is associated with. + KeySetID string `json:"keySetID,omitempty"` +} + +// SetControlPlaneID sets the ControlPlane ID of the KonnectEntityStatus struct. +func (in *KonnectEntityStatusWithControlPlaneAndKeySetRef) SetControlPlaneID(id string) { + in.ControlPlaneID = id +} + +// GetControlPlaneID sets the ControlPlane ID of the KonnectEntityStatus struct. +func (in *KonnectEntityStatusWithControlPlaneAndKeySetRef) GetControlPlaneID() string { + return in.ControlPlaneID +} + +// SetKeySetID sets the KeySet ID of the KonnectEntityStatus struct. +func (in *KonnectEntityStatusWithControlPlaneAndKeySetRef) SetKeySetID(id string) { + in.KeySetID = id +} + +// GetKeySetID sets the KeySet ID of the KonnectEntityStatus struct. +func (in *KonnectEntityStatusWithControlPlaneAndKeySetRef) GetKeySetID() string { + return in.KeySetID +} diff --git a/api/konnect/v1alpha1/zz_generated.deepcopy.go b/api/konnect/v1alpha1/zz_generated.deepcopy.go index 9872b01..b03f076 100644 --- a/api/konnect/v1alpha1/zz_generated.deepcopy.go +++ b/api/konnect/v1alpha1/zz_generated.deepcopy.go @@ -173,6 +173,22 @@ func (in *KonnectEntityStatusWithControlPlaneAndConsumerRefs) DeepCopy() *Konnec return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KonnectEntityStatusWithControlPlaneAndKeySetRef) DeepCopyInto(out *KonnectEntityStatusWithControlPlaneAndKeySetRef) { + *out = *in + out.KonnectEntityStatus = in.KonnectEntityStatus +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KonnectEntityStatusWithControlPlaneAndKeySetRef. +func (in *KonnectEntityStatusWithControlPlaneAndKeySetRef) DeepCopy() *KonnectEntityStatusWithControlPlaneAndKeySetRef { + if in == nil { + return nil + } + out := new(KonnectEntityStatusWithControlPlaneAndKeySetRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KonnectEntityStatusWithControlPlaneAndServiceRefs) DeepCopyInto(out *KonnectEntityStatusWithControlPlaneAndServiceRefs) { *out = *in diff --git a/config/crd/bases/configuration.konghq.com_kongkeys.yaml b/config/crd/bases/configuration.konghq.com_kongkeys.yaml new file mode 100644 index 0000000..1568be6 --- /dev/null +++ b/config/crd/bases/configuration.konghq.com_kongkeys.yaml @@ -0,0 +1,290 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: kongkeys.configuration.konghq.com +spec: + group: configuration.konghq.com + names: + kind: KongKey + listKind: KongKeyList + plural: kongkeys + singular: kongkey + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The Resource is Programmed on Konnect + jsonPath: .status.conditions[?(@.type=='Programmed')].status + name: Programmed + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: KongKey is the schema for KongKey API which defines a KongKey + entity. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: KongKeySpec defines the spec for a KongKey. + properties: + controlPlaneRef: + description: ControlPlaneRef is a reference to a Konnect ControlPlane + this KongKey is associated with. + properties: + konnectID: + description: |- + KonnectID is the schema for the KonnectID type. + This field is required when the Type is konnectID. + type: string + konnectNamespacedRef: + description: |- + KonnectNamespacedRef is a reference to a Konnect Control Plane entity inside the cluster. + It contains the name of the Konnect Control Plane. + This field is required when the Type is konnectNamespacedRef. + properties: + name: + description: Name is the name of the Konnect Control Plane. + type: string + namespace: + description: |- + Namespace is the namespace where the Konnect Control Plane is in. + Currently only cluster scoped resources (KongVault) are allowed to set `konnectNamespacedRef.namespace`. + type: string + required: + - name + type: object + type: + description: |- + Type can be one of: + - konnectID + - konnectNamespacedRef + enum: + - konnectID + - konnectNamespacedRef + type: string + type: object + x-kubernetes-validations: + - message: when type is konnectNamespacedRef, konnectNamespacedRef + must be set + rule: 'self.type == ''konnectNamespacedRef'' ? has(self.konnectNamespacedRef) + : true' + - message: when type is konnectID, konnectID must be set + rule: 'self.type == ''konnectID'' ? has(self.konnectID) : true' + jwk: + description: |- + JWK is a JSON Web Key represented as a string. + The JWK must contain a KID field that matches the KID in the KongKey. + Either JWK or PEM must be set. + type: string + keySetRef: + description: |- + KeySetRef is a reference to a KongKeySet this KongKey is attached to. + ControlPlane referenced by a KongKeySet must be the same as the ControlPlane referenced by the KongKey. + properties: + konnectID: + description: |- + KonnectID is the schema for the KonnectID type. + This field is required when the Type is konnectID. + type: string + namespacedRef: + description: |- + NamespacedRef is a reference to a KeySet entity inside the cluster. + This field is required when the Type is namespacedRef. + properties: + name: + description: Name is the name of the KeySet object. + minLength: 1 + type: string + required: + - name + type: object + type: + description: |- + Type defines type of the KeySet object reference. It can be one of: + - konnectID + - namespacedRef + enum: + - konnectID + - namespacedRef + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: when type is namespacedRef, namespacedRef must be set + rule: 'self.type == ''namespacedRef'' ? has(self.namespacedRef) + : true' + - message: when type is konnectID, konnectID must be set + rule: 'self.type == ''konnectID'' ? has(self.konnectID) : true' + kid: + description: |- + KID is a unique identifier for a key. + When JWK is provided, KID has to match the KID in the JWK. + minLength: 1 + type: string + name: + description: Name is an optional name to associate with the given + key. + type: string + pem: + description: |- + PEM is a keypair in PEM format. + Either JWK or PEM must be set. + properties: + private_key: + description: The private key in PEM format. + minLength: 1 + type: string + public_key: + description: The public key in PEM format. + minLength: 1 + type: string + required: + - private_key + - public_key + type: object + tags: + description: Tags is an optional set of strings associated with the + Key for grouping and filtering. + items: + type: string + type: array + required: + - kid + type: object + x-kubernetes-validations: + - message: Either 'jwk' or 'pem' must be set + rule: has(self.jwk) || has(self.pem) + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: KongKeyStatus defines the status for a KongKey. + properties: + conditions: + description: Conditions describe the status of the Konnect entity. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + konnect: + description: Konnect contains the Konnect entity status. + properties: + controlPlaneID: + description: ControlPlaneID is the Konnect ID of the ControlPlane + this entity is associated with. + type: string + id: + description: |- + ID is the unique identifier of the Konnect entity as assigned by Konnect API. + If it's unset (empty string), it means the Konnect entity hasn't been created yet. + type: string + keySetID: + description: KeySetID is the Konnect ID of the KeySet this entity + is associated with. + type: string + organizationID: + description: OrgID is ID of Konnect Org that this entity has been + created in. + type: string + serverURL: + description: ServerURL is the URL of the Konnect server in which + the entity exists. + type: string + type: object + type: object + required: + - spec + type: object + x-kubernetes-validations: + - message: controlPlaneRef is required once set + rule: '!has(oldSelf.spec.controlPlaneRef) || has(self.spec.controlPlaneRef)' + - message: spec.controlPlaneRef is immutable when an entity is already Programmed + rule: '(!self.status.conditions.exists(c, c.type == ''Programmed'' && c.status + == ''True'')) ? true : oldSelf.spec.controlPlaneRef == self.spec.controlPlaneRef' + - message: spec.controlPlaneRef cannot specify namespace for namespaced resource + - it's not supported yet + rule: '!has(self.spec.controlPlaneRef) ? true : !has(self.spec.controlPlaneRef.konnectNamespacedRef) + ? true : !has(self.spec.controlPlaneRef.konnectNamespacedRef.__namespace__)' + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index f376cdd..39e5dac 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -21,6 +21,7 @@ resources: - bases/configuration.konghq.com_kongtargets.yaml - bases/configuration.konghq.com_kongcacertificates.yaml - bases/configuration.konghq.com_kongcredentialbasicauths.yaml +- bases/configuration.konghq.com_kongkeys.yaml - bases/konnect.konghq.com_konnectapiauthconfigurations.yaml - bases/konnect.konghq.com_konnectgatewaycontrolplanes.yaml diff --git a/docs/api-reference.md b/docs/api-reference.md index 8cbe7c7..9f9f5ca 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -332,6 +332,7 @@ Package v1alpha1 contains API Schema definitions for the configuration.konghq.co - [KongCACertificate](#kongcacertificate) - [KongCredentialBasicAuth](#kongcredentialbasicauth) - [KongCustomEntity](#kongcustomentity) +- [KongKey](#kongkey) - [KongLicense](#konglicense) - [KongPluginBinding](#kongpluginbinding) - [KongRoute](#kongroute) @@ -403,6 +404,22 @@ KongCustomEntity defines a "custom" Kong entity that KIC cannot support the enti +### KongKey + + +KongKey is the schema for KongKey API which defines a KongKey entity. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `configuration.konghq.com/v1alpha1` +| `kind` _string_ | `KongKey` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[KongKeySpec](#kongkeyspec)_ | | + + + ### KongLicense @@ -540,6 +557,7 @@ _Appears in:_ - [KongCACertificateSpec](#kongcacertificatespec) - [KongConsumerGroupSpec](#kongconsumergroupspec) - [KongConsumerSpec](#kongconsumerspec) +- [KongKeySpec](#kongkeyspec) - [KongPluginBindingSpec](#kongpluginbindingspec) - [KongServiceSpec](#kongservicespec) - [KongUpstreamSpec](#kongupstreamspec) @@ -592,6 +610,51 @@ _Appears in:_ _Appears in:_ - [IngressClassParameters](#ingressclassparameters) +#### KeySetNamespacedRef + + +KeySetNamespacedRef is the schema for the KeySetNamespacedRef type. + + + +| Field | Description | +| --- | --- | +| `name` _string_ | Name is the name of the KeySet object. | + + +_Appears in:_ +- [KeySetRef](#keysetref) + +#### KeySetRef + + +KeySetRef is the schema for the KeySetRef type. +It is used to reference a KeySet entity. + + + +| Field | Description | +| --- | --- | +| `type` _[KeySetRefType](#keysetreftype)_ | Type defines type of the KeySet object reference. It can be one of: - konnectID - namespacedRef | +| `konnectID` _string_ | KonnectID is the schema for the KonnectID type. This field is required when the Type is konnectID. | +| `namespacedRef` _[KeySetNamespacedRef](#keysetnamespacedref)_ | NamespacedRef is a reference to a KeySet entity inside the cluster. This field is required when the Type is namespacedRef. | + + +_Appears in:_ +- [KongKeySpec](#kongkeyspec) + +#### KeySetRefType +_Underlying type:_ `string` + +KeySetRefType is the enum type for the KeySetRef. + + + + + +_Appears in:_ +- [KeySetRef](#keysetref) + #### Kind _Underlying type:_ `string` @@ -698,6 +761,48 @@ _Appears in:_ +#### KongKeyAPISpec + + +KongKeyAPISpec defines the attributes of a Kong Key. + + + +| Field | Description | +| --- | --- | +| `kid` _string_ | KID is a unique identifier for a key. When JWK is provided, KID has to match the KID in the JWK. | +| `name` _string_ | Name is an optional name to associate with the given key. | +| `jwk` _string_ | JWK is a JSON Web Key represented as a string. The JWK must contain a KID field that matches the KID in the KongKey. Either JWK or PEM must be set. | +| `pem` _[PEMKeyPair](#pemkeypair)_ | PEM is a keypair in PEM format. Either JWK or PEM must be set. | +| `tags` _string array_ | Tags is an optional set of strings associated with the Key for grouping and filtering. | + + +_Appears in:_ +- [KongKeySpec](#kongkeyspec) + +#### KongKeySpec + + +KongKeySpec defines the spec for a KongKey. + + + +| Field | Description | +| --- | --- | +| `controlPlaneRef` _[ControlPlaneRef](#controlplaneref)_ | ControlPlaneRef is a reference to a Konnect ControlPlane this KongKey is associated with. | +| `keySetRef` _[KeySetRef](#keysetref)_ | KeySetRef is a reference to a KongKeySet this KongKey is attached to. ControlPlane referenced by a KongKeySet must be the same as the ControlPlane referenced by the KongKey. | +| `kid` _string_ | KID is a unique identifier for a key. When JWK is provided, KID has to match the KID in the JWK. | +| `name` _string_ | Name is an optional name to associate with the given key. | +| `jwk` _string_ | JWK is a JSON Web Key represented as a string. The JWK must contain a KID field that matches the KID in the KongKey. Either JWK or PEM must be set. | +| `pem` _[PEMKeyPair](#pemkeypair)_ | PEM is a keypair in PEM format. Either JWK or PEM must be set. | +| `tags` _string array_ | Tags is an optional set of strings associated with the Key for grouping and filtering. | + + +_Appears in:_ +- [KongKey](#kongkey) + + + #### KongLicenseControllerStatus @@ -1085,6 +1190,23 @@ ObjectReference defines reference of a kubernetes object. _Appears in:_ - [KongCustomEntitySpec](#kongcustomentityspec) +#### PEMKeyPair + + +PEMKeyPair defines a keypair in PEM format. + + + +| Field | Description | +| --- | --- | +| `private_key` _string_ | The private key in PEM format. | +| `public_key` _string_ | The public key in PEM format. | + + +_Appears in:_ +- [KongKeyAPISpec](#kongkeyapispec) +- [KongKeySpec](#kongkeyspec) + #### PluginRef diff --git a/docs/konnect-api-reference.md b/docs/konnect-api-reference.md index 769765d..a038d78 100644 --- a/docs/konnect-api-reference.md +++ b/docs/konnect-api-reference.md @@ -123,6 +123,7 @@ _Appears in:_ _Appears in:_ - [KonnectEntityStatusWithControlPlaneAndConsumerRefs](#konnectentitystatuswithcontrolplaneandconsumerrefs) +- [KonnectEntityStatusWithControlPlaneAndKeySetRef](#konnectentitystatuswithcontrolplaneandkeysetref) - [KonnectEntityStatusWithControlPlaneAndServiceRefs](#konnectentitystatuswithcontrolplaneandservicerefs) - [KonnectEntityStatusWithControlPlaneAndUpstreamRefs](#konnectentitystatuswithcontrolplaneandupstreamrefs) - [KonnectEntityStatusWithControlPlaneRef](#konnectentitystatuswithcontrolplaneref) @@ -136,6 +137,8 @@ _Appears in:_ + + #### KonnectGatewayControlPlaneSpec diff --git a/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go b/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go index 9a65a69..7b2b11a 100644 --- a/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go +++ b/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go @@ -32,6 +32,7 @@ type ConfigurationV1alpha1Interface interface { KongCACertificatesGetter KongCredentialBasicAuthsGetter KongCustomEntitiesGetter + KongKeysGetter KongLicensesGetter KongPluginBindingsGetter KongRoutesGetter @@ -62,6 +63,10 @@ func (c *ConfigurationV1alpha1Client) KongCustomEntities(namespace string) KongC return newKongCustomEntities(c, namespace) } +func (c *ConfigurationV1alpha1Client) KongKeys(namespace string) KongKeyInterface { + return newKongKeys(c, namespace) +} + func (c *ConfigurationV1alpha1Client) KongLicenses() KongLicenseInterface { return newKongLicenses(c) } diff --git a/pkg/clientset/typed/configuration/v1alpha1/fake/fake_configuration_client.go b/pkg/clientset/typed/configuration/v1alpha1/fake/fake_configuration_client.go index fb24bb1..e192bf6 100644 --- a/pkg/clientset/typed/configuration/v1alpha1/fake/fake_configuration_client.go +++ b/pkg/clientset/typed/configuration/v1alpha1/fake/fake_configuration_client.go @@ -44,6 +44,10 @@ func (c *FakeConfigurationV1alpha1) KongCustomEntities(namespace string) v1alpha return &FakeKongCustomEntities{c, namespace} } +func (c *FakeConfigurationV1alpha1) KongKeys(namespace string) v1alpha1.KongKeyInterface { + return &FakeKongKeys{c, namespace} +} + func (c *FakeConfigurationV1alpha1) KongLicenses() v1alpha1.KongLicenseInterface { return &FakeKongLicenses{c} } diff --git a/pkg/clientset/typed/configuration/v1alpha1/fake/fake_kongkey.go b/pkg/clientset/typed/configuration/v1alpha1/fake/fake_kongkey.go new file mode 100644 index 0000000..bc4dad3 --- /dev/null +++ b/pkg/clientset/typed/configuration/v1alpha1/fake/fake_kongkey.go @@ -0,0 +1,147 @@ +/* +Copyright 2021 Kong, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeKongKeys implements KongKeyInterface +type FakeKongKeys struct { + Fake *FakeConfigurationV1alpha1 + ns string +} + +var kongkeysResource = v1alpha1.SchemeGroupVersion.WithResource("kongkeys") + +var kongkeysKind = v1alpha1.SchemeGroupVersion.WithKind("KongKey") + +// Get takes name of the kongKey, and returns the corresponding kongKey object, and an error if there is any. +func (c *FakeKongKeys) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.KongKey, err error) { + emptyResult := &v1alpha1.KongKey{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(kongkeysResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.KongKey), err +} + +// List takes label and field selectors, and returns the list of KongKeys that match those selectors. +func (c *FakeKongKeys) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.KongKeyList, err error) { + emptyResult := &v1alpha1.KongKeyList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(kongkeysResource, kongkeysKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.KongKeyList{ListMeta: obj.(*v1alpha1.KongKeyList).ListMeta} + for _, item := range obj.(*v1alpha1.KongKeyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested kongKeys. +func (c *FakeKongKeys) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(kongkeysResource, c.ns, opts)) + +} + +// Create takes the representation of a kongKey and creates it. Returns the server's representation of the kongKey, and an error, if there is any. +func (c *FakeKongKeys) Create(ctx context.Context, kongKey *v1alpha1.KongKey, opts v1.CreateOptions) (result *v1alpha1.KongKey, err error) { + emptyResult := &v1alpha1.KongKey{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(kongkeysResource, c.ns, kongKey, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.KongKey), err +} + +// Update takes the representation of a kongKey and updates it. Returns the server's representation of the kongKey, and an error, if there is any. +func (c *FakeKongKeys) Update(ctx context.Context, kongKey *v1alpha1.KongKey, opts v1.UpdateOptions) (result *v1alpha1.KongKey, err error) { + emptyResult := &v1alpha1.KongKey{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(kongkeysResource, c.ns, kongKey, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.KongKey), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeKongKeys) UpdateStatus(ctx context.Context, kongKey *v1alpha1.KongKey, opts v1.UpdateOptions) (result *v1alpha1.KongKey, err error) { + emptyResult := &v1alpha1.KongKey{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(kongkeysResource, "status", c.ns, kongKey, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.KongKey), err +} + +// Delete takes name of the kongKey and deletes it. Returns an error if one occurs. +func (c *FakeKongKeys) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(kongkeysResource, c.ns, name, opts), &v1alpha1.KongKey{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeKongKeys) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(kongkeysResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.KongKeyList{}) + return err +} + +// Patch applies the patch and returns the patched kongKey. +func (c *FakeKongKeys) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.KongKey, err error) { + emptyResult := &v1alpha1.KongKey{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(kongkeysResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.KongKey), err +} diff --git a/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go b/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go index 87fbe16..36938d1 100644 --- a/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go +++ b/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go @@ -26,6 +26,8 @@ type KongCredentialBasicAuthExpansion interface{} type KongCustomEntityExpansion interface{} +type KongKeyExpansion interface{} + type KongLicenseExpansion interface{} type KongPluginBindingExpansion interface{} diff --git a/pkg/clientset/typed/configuration/v1alpha1/kongkey.go b/pkg/clientset/typed/configuration/v1alpha1/kongkey.go new file mode 100644 index 0000000..ebfb5cf --- /dev/null +++ b/pkg/clientset/typed/configuration/v1alpha1/kongkey.go @@ -0,0 +1,69 @@ +/* +Copyright 2021 Kong, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + + v1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + scheme "github.com/kong/kubernetes-configuration/pkg/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// KongKeysGetter has a method to return a KongKeyInterface. +// A group's client should implement this interface. +type KongKeysGetter interface { + KongKeys(namespace string) KongKeyInterface +} + +// KongKeyInterface has methods to work with KongKey resources. +type KongKeyInterface interface { + Create(ctx context.Context, kongKey *v1alpha1.KongKey, opts v1.CreateOptions) (*v1alpha1.KongKey, error) + Update(ctx context.Context, kongKey *v1alpha1.KongKey, opts v1.UpdateOptions) (*v1alpha1.KongKey, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, kongKey *v1alpha1.KongKey, opts v1.UpdateOptions) (*v1alpha1.KongKey, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.KongKey, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.KongKeyList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.KongKey, err error) + KongKeyExpansion +} + +// kongKeys implements KongKeyInterface +type kongKeys struct { + *gentype.ClientWithList[*v1alpha1.KongKey, *v1alpha1.KongKeyList] +} + +// newKongKeys returns a KongKeys +func newKongKeys(c *ConfigurationV1alpha1Client, namespace string) *kongKeys { + return &kongKeys{ + gentype.NewClientWithList[*v1alpha1.KongKey, *v1alpha1.KongKeyList]( + "kongkeys", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.KongKey { return &v1alpha1.KongKey{} }, + func() *v1alpha1.KongKeyList { return &v1alpha1.KongKeyList{} }), + } +} diff --git a/scripts/konnect-funcs/supportedtypes.go b/scripts/konnect-funcs/supportedtypes.go index e72c590..b8c9c46 100644 --- a/scripts/konnect-funcs/supportedtypes.go +++ b/scripts/konnect-funcs/supportedtypes.go @@ -24,6 +24,11 @@ var supportedTypes = []supportedTypesT{ { Package: "v1alpha1", Types: []templateDataT{ + { + Type: "KongKey", + KonnectStatusType: "konnectv1alpha1.KonnectEntityStatusWithControlPlaneAndKeySetRef", + ReceiverName: "k", + }, { Type: "KongCredentialBasicAuth", KonnectStatusType: "konnectv1alpha1.KonnectEntityStatusWithControlPlaneAndConsumerRefs", diff --git a/test/crdsvalidation/kongkey/kongkey_test.go b/test/crdsvalidation/kongkey/kongkey_test.go new file mode 100644 index 0000000..df23c32 --- /dev/null +++ b/test/crdsvalidation/kongkey/kongkey_test.go @@ -0,0 +1,66 @@ +package kongtarget + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + + configurationv1alpha1client "github.com/kong/kubernetes-configuration/pkg/clientset/typed/configuration/v1alpha1" + "github.com/kong/kubernetes-configuration/test/crdsvalidation/kongkey/testcases" +) + +func TestKongKey(t *testing.T) { + ctx := context.Background() + cfg, err := config.GetConfig() + require.NoError(t, err, "error loading Kubernetes config") + cl, err := configurationv1alpha1client.NewForConfig(cfg) + require.NoError(t, err, "error creating configurationv1alpha1 client") + + for _, tcsGroup := range testcases.TestCases { + t.Run(tcsGroup.Name, func(t *testing.T) { + for _, tc := range tcsGroup.TestCases { + t.Run(tc.Name, func(t *testing.T) { + cl := cl.KongKeys(tc.KongKey.Namespace) + entity, err := cl.Create(ctx, &tc.KongKey, metav1.CreateOptions{}) + if err == nil { + t.Cleanup(func() { + assert.NoError(t, client.IgnoreNotFound(cl.Delete(ctx, entity.Name, metav1.DeleteOptions{}))) + }) + } + + if tc.ExpectedErrorMessage == nil { + assert.NoError(t, err) + + // if the status has to be updated, update it. + if tc.KongKeyStatus != nil { + entity.Status = *tc.KongKeyStatus + entity, err = cl.UpdateStatus(ctx, entity, metav1.UpdateOptions{}) + assert.NoError(t, err) + } + + // Update the object and check if the update is allowed. + if tc.Update != nil { + tc.Update(entity) + _, err := cl.Update(ctx, entity, metav1.UpdateOptions{}) + if tc.ExpectedUpdateErrorMessage != nil { + require.Error(t, err) + assert.Contains(t, err.Error(), *tc.ExpectedUpdateErrorMessage) + } else { + assert.NoError(t, err) + } + } + } else { + require.Error(t, err) + require.ErrorContains(t, err, *tc.ExpectedErrorMessage) + } + }) + } + }) + } + +} diff --git a/test/crdsvalidation/kongkey/testcases/common.go b/test/crdsvalidation/kongkey/testcases/common.go new file mode 100644 index 0000000..d314372 --- /dev/null +++ b/test/crdsvalidation/kongkey/testcases/common.go @@ -0,0 +1,34 @@ +package testcases + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" +) + +// testCase is a test case related to KongKey validation. +type testCase struct { + Name string + KongKey configurationv1alpha1.KongKey + KongKeyStatus *configurationv1alpha1.KongKeyStatus + Update func(*configurationv1alpha1.KongKey) + ExpectedErrorMessage *string + ExpectedUpdateErrorMessage *string +} + +type testCasesGroup struct { + Name string + TestCases []testCase +} + +// TestCases is a collection of all test cases groups related to KongKey validation. +var TestCases = []testCasesGroup{} + +func init() { + TestCases = append(TestCases, keySetAPISpec, keySetRef, cpRef) +} + +var commonObjectMeta = metav1.ObjectMeta{ + GenerateName: "test-kongkey-", + Namespace: "default", +} diff --git a/test/crdsvalidation/kongkey/testcases/controlplaneref.go b/test/crdsvalidation/kongkey/testcases/controlplaneref.go new file mode 100644 index 0000000..8062a34 --- /dev/null +++ b/test/crdsvalidation/kongkey/testcases/controlplaneref.go @@ -0,0 +1,144 @@ +package testcases + +import ( + "github.com/samber/lo" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" +) + +var cpRef = testCasesGroup{ + Name: "controlPlaneRef", + TestCases: []testCase{ + { + Name: "konnectNamespacedRef reference is valid", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + }, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}"), + }, + }, + }, + }, + { + Name: "not providing konnectNamespacedRef when type is konnectNamespacedRef yields an error", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}"), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("when type is konnectNamespacedRef, konnectNamespacedRef must be set"), + }, + { + Name: "not providing konnectID when type is konnectID yields an error", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectID, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}")}, + }, + }, + ExpectedErrorMessage: lo.ToPtr("when type is konnectID, konnectID must be set"), + }, + { + Name: "konnectNamespacedRef reference name cannot be changed when an entity is Programmed", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + }, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}")}, + }, + }, + KongKeyStatus: &configurationv1alpha1.KongKeyStatus{ + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + LastTransitionTime: metav1.Now(), + }, + }, + }, + Update: func(ks *configurationv1alpha1.KongKey) { + ks.Spec.ControlPlaneRef.KonnectNamespacedRef.Name = "new-konnect-control-plane" + }, + ExpectedUpdateErrorMessage: lo.ToPtr("spec.controlPlaneRef is immutable when an entity is already Programmed"), + }, + { + Name: "konnectNamespacedRef reference type cannot be changed when an entity is Programmed", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + }, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}")}, + }, + }, + KongKeyStatus: &configurationv1alpha1.KongKeyStatus{ + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + LastTransitionTime: metav1.Now(), + }, + }, + }, + Update: func(ks *configurationv1alpha1.KongKey) { + ks.Spec.ControlPlaneRef.Type = configurationv1alpha1.ControlPlaneRefKonnectID + }, + ExpectedUpdateErrorMessage: lo.ToPtr("spec.controlPlaneRef is immutable when an entity is already Programmed"), + }, + { + Name: "konnectNamespaced reference cannot set namespace as it's not supported yet", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + Namespace: "default", + }, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}")}, + }, + }, + ExpectedErrorMessage: lo.ToPtr("spec.controlPlaneRef cannot specify namespace for namespaced resource - it's not supported yet"), + }, + }, +} diff --git a/test/crdsvalidation/kongkey/testcases/keysetapispec.go b/test/crdsvalidation/kongkey/testcases/keysetapispec.go new file mode 100644 index 0000000..2d4990e --- /dev/null +++ b/test/crdsvalidation/kongkey/testcases/keysetapispec.go @@ -0,0 +1,37 @@ +package testcases + +import ( + "github.com/samber/lo" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" +) + +var keySetAPISpec = testCasesGroup{ + Name: "kongKeyAPISpec", + TestCases: []testCase{ + { + Name: "KID must be set", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + JWK: lo.ToPtr("{}"), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("spec.kid in body should be at least 1 chars long"), + }, + { + Name: "one of JWK or PEM must be set", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("Either 'jwk' or 'pem' must be set"), + }, + }, +} diff --git a/test/crdsvalidation/kongkey/testcases/keysetref.go b/test/crdsvalidation/kongkey/testcases/keysetref.go new file mode 100644 index 0000000..472b1c4 --- /dev/null +++ b/test/crdsvalidation/kongkey/testcases/keysetref.go @@ -0,0 +1,95 @@ +package testcases + +import ( + "github.com/samber/lo" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" +) + +var keySetRef = testCasesGroup{ + Name: "keySetRef", + TestCases: []testCase{ + { + Name: "when type is 'namespacedRef', namespacedRef is required", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + KeySetRef: &configurationv1alpha1.KeySetRef{ + Type: configurationv1alpha1.KeySetRefNamespacedRef, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}"), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("when type is namespacedRef, namespacedRef must be set"), + }, + { + Name: "'namespacedRef' type is accepted", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + KeySetRef: &configurationv1alpha1.KeySetRef{ + Type: configurationv1alpha1.KeySetRefNamespacedRef, + NamespacedRef: &configurationv1alpha1.KeySetNamespacedRef{ + Name: "keyset-1", + }, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}"), + }, + }, + }, + }, + { + Name: "'konnectID' type is accepted", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + KeySetRef: &configurationv1alpha1.KeySetRef{ + Type: configurationv1alpha1.KeySetRefKonnectID, + KonnectID: lo.ToPtr("keyset-1"), + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}"), + }, + }, + }, + }, + { + Name: "when type is 'konnectID', konnectID is required", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + KeySetRef: &configurationv1alpha1.KeySetRef{ + Type: configurationv1alpha1.KeySetRefKonnectID, + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}"), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("when type is konnectID, konnectID must be set"), + }, + { + Name: "unknown type is not accepted", + KongKey: configurationv1alpha1.KongKey{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongKeySpec{ + KeySetRef: &configurationv1alpha1.KeySetRef{ + Type: configurationv1alpha1.KeySetRefType("unknown"), + }, + KongKeyAPISpec: configurationv1alpha1.KongKeyAPISpec{ + KID: "1", + JWK: lo.ToPtr("{}"), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr(`Unsupported value: "unknown": supported values: "konnectID", "namespacedRef"`), + }, + }, +} diff --git a/test/unit/konnect_funcs_test.go b/test/unit/konnect_funcs_test.go index cdbd4de..b418103 100644 --- a/test/unit/konnect_funcs_test.go +++ b/test/unit/konnect_funcs_test.go @@ -61,6 +61,10 @@ func TestKonnectFuncs(t *testing.T) { typeName: "KongCredentialBasicAuth", object: &configurationv1alpha1.KongCredentialBasicAuth{}, }, + { + typeName: "KongKey", + object: &configurationv1alpha1.KongKey{}, + }, } for _, tc := range testcases {