From be3e20e440afcbbfd68107c1f11cd2cc7d89ead5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Ma=C5=82ek?= Date: Wed, 18 Sep 2024 20:43:37 +0200 Subject: [PATCH] feat(crd): add CredentialBasicAuth --- .../v1alpha1/credential_basic_auth_types.go | 159 ++++++++++++++ .../v1alpha1/zz_generated.deepcopy.go | 134 ++++++++++++ ...ation.konghq.com_credentialbasicauths.yaml | 199 ++++++++++++++++++ config/crd/kustomization.yaml | 1 + config/samples/konnect_kongconsumer.yaml | 48 +++++ docs/api-reference.md | 55 +++++ .../v1alpha1/configuration_client.go | 5 + .../v1alpha1/credentialbasicauth.go | 69 ++++++ .../fake/fake_configuration_client.go | 4 + .../v1alpha1/fake/fake_credentialbasicauth.go | 147 +++++++++++++ .../v1alpha1/generated_expansion.go | 2 + .../credentialbasicauth_test.go | 65 ++++++ .../credentialbasicauth/testcases/common.go | 38 ++++ ...nsumerref-update-not-allowed-for-status.go | 115 ++++++++++ .../kongconsumergroup_test.go | 2 +- 15 files changed, 1042 insertions(+), 1 deletion(-) create mode 100644 api/configuration/v1alpha1/credential_basic_auth_types.go create mode 100644 config/crd/bases/configuration.konghq.com_credentialbasicauths.yaml create mode 100644 config/samples/konnect_kongconsumer.yaml create mode 100644 pkg/clientset/typed/configuration/v1alpha1/credentialbasicauth.go create mode 100644 pkg/clientset/typed/configuration/v1alpha1/fake/fake_credentialbasicauth.go create mode 100644 test/crdsvalidation/credentialbasicauth/credentialbasicauth_test.go create mode 100644 test/crdsvalidation/credentialbasicauth/testcases/common.go create mode 100644 test/crdsvalidation/credentialbasicauth/testcases/consumerref-update-not-allowed-for-status.go diff --git a/api/configuration/v1alpha1/credential_basic_auth_types.go b/api/configuration/v1alpha1/credential_basic_auth_types.go new file mode 100644 index 0000000..25f6c5c --- /dev/null +++ b/api/configuration/v1alpha1/credential_basic_auth_types.go @@ -0,0 +1,159 @@ +/* +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 ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" +) + +// CredentialBasicAuth is the schema for BasicAuth credentials API which defines a BasicAuth credential for consumers. +// +// +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.consumerRef) || has(self.spec.consumerRef)", message="consumerRef is required once set" +// +kubebuilder:validation:XValidation:rule="(!self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True')) ? true : oldSelf.spec.consumerRef == self.spec.consumerRef", message="spec.consumerRef is immutable when an entity is already Programmed" +type CredentialBasicAuth struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec contains the BasicAuth credential specification. + Spec CredentialBasicAuthSpec `json:"spec"` + + // Status contains the BasicAuth credential status. + // + // +kubebuilder:default={conditions: {{type: "Programmed", status: "Unknown", reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}}} + Status CredentialBasicAuthStatus `json:"status,omitempty"` +} + +func (s *CredentialBasicAuth) initKonnectStatus() { + s.Status.Konnect = &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{} +} + +// GetKonnectStatus returns the Konnect status contained in the CredentialBasicAuth status. +func (s *CredentialBasicAuth) GetKonnectStatus() *konnectv1alpha1.KonnectEntityStatus { + if s.Status.Konnect == nil { + return nil + } + return &s.Status.Konnect.KonnectEntityStatus +} + +// GetKonnectID returns the Konnect ID in the CredentialBasicAuth status. +func (s *CredentialBasicAuth) GetKonnectID() string { + if s.Status.Konnect == nil { + return "" + } + return s.Status.Konnect.ID +} + +// SetKonnectID sets the Konnect ID in the CredentialBasicAuth status. +func (s *CredentialBasicAuth) SetKonnectID(id string) { + if s.Status.Konnect == nil { + s.initKonnectStatus() + } + s.Status.Konnect.ID = id +} + +// GetControlPlaneID returns the ControlPlane ID in the CredentialBasicAuth status. +func (s *CredentialBasicAuth) GetControlPlaneID() string { + if s.Status.Konnect == nil { + return "" + } + return s.Status.Konnect.ControlPlaneID +} + +// SetControlPlaneID sets the ControlPlane ID in the CredentialBasicAuth status. +func (s *CredentialBasicAuth) SetControlPlaneID(id string) { + if s.Status.Konnect == nil { + s.initKonnectStatus() + } + s.Status.Konnect.ControlPlaneID = id +} + +// GetTypeName returns the CredentialBasicAuth Kind name +func (s CredentialBasicAuth) GetTypeName() string { + return "CredentialBasicAuth" +} + +// GetConditions returns the Status Conditions +func (s *CredentialBasicAuth) GetConditions() []metav1.Condition { + return s.Status.Conditions +} + +// SetConditions sets the Status Conditions +func (s *CredentialBasicAuth) SetConditions(conditions []metav1.Condition) { + s.Status.Conditions = conditions +} + +// CredentialBasicAuthSpec defines specification of a Kong Route. +type CredentialBasicAuthSpec struct { + // ConsumerRef is a reference to a Consumer this CredentialBasicAuth is associated with. + // + // +kubebuilder:validation:Required + ConsumerRef corev1.LocalObjectReference `json:"consumerRef"` + + // SecretRef is a reference to a Secret this CredentialBasicAuth is associated with. + // + // +kubebuilder:validation:Required + SecretRef corev1.LocalObjectReference `json:"secretRef"` + + CredentialBasicAuthAPISpec `json:",inline"` +} + +// CredentialBasicAuthAPISpec defines specification of a BasicAuth credential. +type CredentialBasicAuthAPISpec struct { + // Password is the password for the BasicAuth credential. + Password *string `json:"password,omitempty"` + // Tags is a list of tags for the BasicAuth credential. + Tags []string `json:"tags,omitempty"` + // Username is the username for the BasicAuth credential. + Username *string `json:"username,omitempty"` +} + +// CredentialBasicAuthStatus represents the current status of the BasicAuth credential resource. +type CredentialBasicAuthStatus struct { + // Konnect contains the Konnect entity status. + // +optional + Konnect *konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef `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 + +// CredentialBasicAuthList contains a list of BasicAuth credentials. +type CredentialBasicAuthList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []CredentialBasicAuth `json:"items"` +} + +func init() { + SchemeBuilder.Register(&CredentialBasicAuth{}, &CredentialBasicAuthList{}) +} diff --git a/api/configuration/v1alpha1/zz_generated.deepcopy.go b/api/configuration/v1alpha1/zz_generated.deepcopy.go index 293dd75..019a7d8 100644 --- a/api/configuration/v1alpha1/zz_generated.deepcopy.go +++ b/api/configuration/v1alpha1/zz_generated.deepcopy.go @@ -82,6 +82,140 @@ func (in *ControllerReference) DeepCopy() *ControllerReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CredentialBasicAuth) DeepCopyInto(out *CredentialBasicAuth) { + *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 CredentialBasicAuth. +func (in *CredentialBasicAuth) DeepCopy() *CredentialBasicAuth { + if in == nil { + return nil + } + out := new(CredentialBasicAuth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CredentialBasicAuth) 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 *CredentialBasicAuthAPISpec) DeepCopyInto(out *CredentialBasicAuthAPISpec) { + *out = *in + if in.Password != nil { + in, out := &in.Password, &out.Password + *out = new(string) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Username != nil { + in, out := &in.Username, &out.Username + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialBasicAuthAPISpec. +func (in *CredentialBasicAuthAPISpec) DeepCopy() *CredentialBasicAuthAPISpec { + if in == nil { + return nil + } + out := new(CredentialBasicAuthAPISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CredentialBasicAuthList) DeepCopyInto(out *CredentialBasicAuthList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CredentialBasicAuth, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialBasicAuthList. +func (in *CredentialBasicAuthList) DeepCopy() *CredentialBasicAuthList { + if in == nil { + return nil + } + out := new(CredentialBasicAuthList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CredentialBasicAuthList) 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 *CredentialBasicAuthSpec) DeepCopyInto(out *CredentialBasicAuthSpec) { + *out = *in + out.ConsumerRef = in.ConsumerRef + out.SecretRef = in.SecretRef + in.CredentialBasicAuthAPISpec.DeepCopyInto(&out.CredentialBasicAuthAPISpec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialBasicAuthSpec. +func (in *CredentialBasicAuthSpec) DeepCopy() *CredentialBasicAuthSpec { + if in == nil { + return nil + } + out := new(CredentialBasicAuthSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CredentialBasicAuthStatus) DeepCopyInto(out *CredentialBasicAuthStatus) { + *out = *in + if in.Konnect != nil { + in, out := &in.Konnect, &out.Konnect + *out = new(konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef) + **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 CredentialBasicAuthStatus. +func (in *CredentialBasicAuthStatus) DeepCopy() *CredentialBasicAuthStatus { + if in == nil { + return nil + } + out := new(CredentialBasicAuthStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressClassParameters) DeepCopyInto(out *IngressClassParameters) { *out = *in diff --git a/config/crd/bases/configuration.konghq.com_credentialbasicauths.yaml b/config/crd/bases/configuration.konghq.com_credentialbasicauths.yaml new file mode 100644 index 0000000..7da3c4e --- /dev/null +++ b/config/crd/bases/configuration.konghq.com_credentialbasicauths.yaml @@ -0,0 +1,199 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: credentialbasicauths.configuration.konghq.com +spec: + group: configuration.konghq.com + names: + kind: CredentialBasicAuth + listKind: CredentialBasicAuthList + plural: credentialbasicauths + singular: credentialbasicauth + 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: CredentialBasicAuth is the schema for BasicAuth credentials API + which defines a BasicAuth credential for consumers. + 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: Spec contains the BasicAuth credential specification. + properties: + consumerRef: + description: ConsumerRef is a reference to a Consumer this CredentialBasicAuth + is associated with. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + password: + description: Password is the password for the BasicAuth credential. + type: string + secretRef: + description: SecretRef is a reference to a Secret this CredentialBasicAuth + is associated with. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + tags: + description: Tags is a list of tags for the BasicAuth credential. + items: + type: string + type: array + username: + description: Username is the username for the BasicAuth credential. + type: string + required: + - consumerRef + - secretRef + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: Status contains the BasicAuth credential status. + 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 Route 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 + 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: consumerRef is required once set + rule: '!has(oldSelf.spec.consumerRef) || has(self.spec.consumerRef)' + - message: spec.consumerRef is immutable when an entity is already Programmed + rule: '(!self.status.conditions.exists(c, c.type == ''Programmed'' && c.status + == ''True'')) ? true : oldSelf.spec.consumerRef == self.spec.consumerRef' + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 65f4665..fb72380 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -20,6 +20,7 @@ resources: - bases/configuration.konghq.com_kongupstreams.yaml - bases/configuration.konghq.com_kongtargets.yaml - bases/configuration.konghq.com_kongcacertificates.yaml +- bases/configuration.konghq.com_credentialbasicauths.yaml - bases/konnect.konghq.com_konnectapiauthconfigurations.yaml - bases/konnect.konghq.com_konnectgatewaycontrolplanes.yaml diff --git a/config/samples/konnect_kongconsumer.yaml b/config/samples/konnect_kongconsumer.yaml new file mode 100644 index 0000000..bae5f0d --- /dev/null +++ b/config/samples/konnect_kongconsumer.yaml @@ -0,0 +1,48 @@ +kind: KonnectAPIAuthConfiguration +apiVersion: konnect.konghq.com/v1alpha1 +metadata: + name: konnect-api-auth-1 + namespace: default +spec: + type: token + token: kpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + serverURL: eu.api.konghq.com +--- +kind: KonnectGatewayControlPlane +apiVersion: konnect.konghq.com/v1alpha1 +metadata: + name: test1 + namespace: default +spec: + name: test1 + labels: + app: test1 + key1: test1 + konnect: + authRef: + name: konnect-api-auth-dev-1 +--- +apiVersion: v1 +kind: Secret +metadata: + name: consumer1-basic-auth + namespace: default + labels: + konghq.com/credential: basic-auth +type: Opaque +stringData: + username: alex + password: secret123 +--- +apiVersion: configuration.konghq.com/v1 +kind: KongConsumer +metadata: + name: consumer1 + namespace: default +credentials: +- consumer1-basic-auth +spec: + controlPlaneRef: + type: konnectNamespacedRef + konnectNamespacedRef: + name: test1 diff --git a/docs/api-reference.md b/docs/api-reference.md index ac5c816..45b3188 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -328,6 +328,7 @@ _Appears in:_ Package v1alpha1 contains API Schema definitions for the configuration.konghq.com v1alpha1 API group. +- [CredentialBasicAuth](#credentialbasicauth) - [IngressClassParameters](#ingressclassparameters) - [KongCACertificate](#kongcacertificate) - [KongCustomEntity](#kongcustomentity) @@ -338,6 +339,22 @@ Package v1alpha1 contains API Schema definitions for the configuration.konghq.co - [KongTarget](#kongtarget) - [KongUpstream](#kongupstream) - [KongVault](#kongvault) +### CredentialBasicAuth + + +CredentialBasicAuth is the schema for BasicAuth credentials API which defines a BasicAuth credential for consumers. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `configuration.konghq.com/v1alpha1` +| `kind` _string_ | `CredentialBasicAuth` +| `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` _[CredentialBasicAuthSpec](#credentialbasicauthspec)_ | Spec contains the BasicAuth credential specification. | + + + ### IngressClassParameters @@ -546,6 +563,44 @@ _Appears in:_ _Appears in:_ - [KongLicenseControllerStatus](#konglicensecontrollerstatus) +#### CredentialBasicAuthAPISpec + + +CredentialBasicAuthAPISpec defines specification of a BasicAuth credential. + + + +| Field | Description | +| --- | --- | +| `password` _string_ | Password is the password for the BasicAuth credential. | +| `tags` _string array_ | Tags is a list of tags for the BasicAuth credential. | +| `username` _string_ | Username is the username for the BasicAuth credential. | + + +_Appears in:_ +- [CredentialBasicAuthSpec](#credentialbasicauthspec) + +#### CredentialBasicAuthSpec + + +CredentialBasicAuthSpec defines specification of a Kong Route. + + + +| Field | Description | +| --- | --- | +| `consumerRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#localobjectreference-v1-core)_ | ConsumerRef is a reference to a Consumer this CredentialBasicAuth is associated with. | +| `secretRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#localobjectreference-v1-core)_ | SecretRef is a reference to a Secret this CredentialBasicAuth is associated with. | +| `password` _string_ | Password is the password for the BasicAuth credential. | +| `tags` _string array_ | Tags is a list of tags for the BasicAuth credential. | +| `username` _string_ | Username is the username for the BasicAuth credential. | + + +_Appears in:_ +- [CredentialBasicAuth](#credentialbasicauth) + + + #### Group _Underlying type:_ `string` diff --git a/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go b/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go index 91a8085..dbe5057 100644 --- a/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go +++ b/pkg/clientset/typed/configuration/v1alpha1/configuration_client.go @@ -28,6 +28,7 @@ import ( type ConfigurationV1alpha1Interface interface { RESTClient() rest.Interface + CredentialBasicAuthsGetter IngressClassParametersesGetter KongCACertificatesGetter KongCustomEntitiesGetter @@ -45,6 +46,10 @@ type ConfigurationV1alpha1Client struct { restClient rest.Interface } +func (c *ConfigurationV1alpha1Client) CredentialBasicAuths(namespace string) CredentialBasicAuthInterface { + return newCredentialBasicAuths(c, namespace) +} + func (c *ConfigurationV1alpha1Client) IngressClassParameterses(namespace string) IngressClassParametersInterface { return newIngressClassParameterses(c, namespace) } diff --git a/pkg/clientset/typed/configuration/v1alpha1/credentialbasicauth.go b/pkg/clientset/typed/configuration/v1alpha1/credentialbasicauth.go new file mode 100644 index 0000000..ccf0d17 --- /dev/null +++ b/pkg/clientset/typed/configuration/v1alpha1/credentialbasicauth.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" +) + +// CredentialBasicAuthsGetter has a method to return a CredentialBasicAuthInterface. +// A group's client should implement this interface. +type CredentialBasicAuthsGetter interface { + CredentialBasicAuths(namespace string) CredentialBasicAuthInterface +} + +// CredentialBasicAuthInterface has methods to work with CredentialBasicAuth resources. +type CredentialBasicAuthInterface interface { + Create(ctx context.Context, credentialBasicAuth *v1alpha1.CredentialBasicAuth, opts v1.CreateOptions) (*v1alpha1.CredentialBasicAuth, error) + Update(ctx context.Context, credentialBasicAuth *v1alpha1.CredentialBasicAuth, opts v1.UpdateOptions) (*v1alpha1.CredentialBasicAuth, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, credentialBasicAuth *v1alpha1.CredentialBasicAuth, opts v1.UpdateOptions) (*v1alpha1.CredentialBasicAuth, 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.CredentialBasicAuth, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.CredentialBasicAuthList, 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.CredentialBasicAuth, err error) + CredentialBasicAuthExpansion +} + +// credentialBasicAuths implements CredentialBasicAuthInterface +type credentialBasicAuths struct { + *gentype.ClientWithList[*v1alpha1.CredentialBasicAuth, *v1alpha1.CredentialBasicAuthList] +} + +// newCredentialBasicAuths returns a CredentialBasicAuths +func newCredentialBasicAuths(c *ConfigurationV1alpha1Client, namespace string) *credentialBasicAuths { + return &credentialBasicAuths{ + gentype.NewClientWithList[*v1alpha1.CredentialBasicAuth, *v1alpha1.CredentialBasicAuthList]( + "credentialbasicauths", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.CredentialBasicAuth { return &v1alpha1.CredentialBasicAuth{} }, + func() *v1alpha1.CredentialBasicAuthList { return &v1alpha1.CredentialBasicAuthList{} }), + } +} 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 a8c75cd..0622029 100644 --- a/pkg/clientset/typed/configuration/v1alpha1/fake/fake_configuration_client.go +++ b/pkg/clientset/typed/configuration/v1alpha1/fake/fake_configuration_client.go @@ -28,6 +28,10 @@ type FakeConfigurationV1alpha1 struct { *testing.Fake } +func (c *FakeConfigurationV1alpha1) CredentialBasicAuths(namespace string) v1alpha1.CredentialBasicAuthInterface { + return &FakeCredentialBasicAuths{c, namespace} +} + func (c *FakeConfigurationV1alpha1) IngressClassParameterses(namespace string) v1alpha1.IngressClassParametersInterface { return &FakeIngressClassParameterses{c, namespace} } diff --git a/pkg/clientset/typed/configuration/v1alpha1/fake/fake_credentialbasicauth.go b/pkg/clientset/typed/configuration/v1alpha1/fake/fake_credentialbasicauth.go new file mode 100644 index 0000000..b18d491 --- /dev/null +++ b/pkg/clientset/typed/configuration/v1alpha1/fake/fake_credentialbasicauth.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" +) + +// FakeCredentialBasicAuths implements CredentialBasicAuthInterface +type FakeCredentialBasicAuths struct { + Fake *FakeConfigurationV1alpha1 + ns string +} + +var credentialbasicauthsResource = v1alpha1.SchemeGroupVersion.WithResource("credentialbasicauths") + +var credentialbasicauthsKind = v1alpha1.SchemeGroupVersion.WithKind("CredentialBasicAuth") + +// Get takes name of the credentialBasicAuth, and returns the corresponding credentialBasicAuth object, and an error if there is any. +func (c *FakeCredentialBasicAuths) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.CredentialBasicAuth, err error) { + emptyResult := &v1alpha1.CredentialBasicAuth{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(credentialbasicauthsResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.CredentialBasicAuth), err +} + +// List takes label and field selectors, and returns the list of CredentialBasicAuths that match those selectors. +func (c *FakeCredentialBasicAuths) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.CredentialBasicAuthList, err error) { + emptyResult := &v1alpha1.CredentialBasicAuthList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(credentialbasicauthsResource, credentialbasicauthsKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.CredentialBasicAuthList{ListMeta: obj.(*v1alpha1.CredentialBasicAuthList).ListMeta} + for _, item := range obj.(*v1alpha1.CredentialBasicAuthList).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 credentialBasicAuths. +func (c *FakeCredentialBasicAuths) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(credentialbasicauthsResource, c.ns, opts)) + +} + +// Create takes the representation of a credentialBasicAuth and creates it. Returns the server's representation of the credentialBasicAuth, and an error, if there is any. +func (c *FakeCredentialBasicAuths) Create(ctx context.Context, credentialBasicAuth *v1alpha1.CredentialBasicAuth, opts v1.CreateOptions) (result *v1alpha1.CredentialBasicAuth, err error) { + emptyResult := &v1alpha1.CredentialBasicAuth{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(credentialbasicauthsResource, c.ns, credentialBasicAuth, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.CredentialBasicAuth), err +} + +// Update takes the representation of a credentialBasicAuth and updates it. Returns the server's representation of the credentialBasicAuth, and an error, if there is any. +func (c *FakeCredentialBasicAuths) Update(ctx context.Context, credentialBasicAuth *v1alpha1.CredentialBasicAuth, opts v1.UpdateOptions) (result *v1alpha1.CredentialBasicAuth, err error) { + emptyResult := &v1alpha1.CredentialBasicAuth{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(credentialbasicauthsResource, c.ns, credentialBasicAuth, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.CredentialBasicAuth), 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 *FakeCredentialBasicAuths) UpdateStatus(ctx context.Context, credentialBasicAuth *v1alpha1.CredentialBasicAuth, opts v1.UpdateOptions) (result *v1alpha1.CredentialBasicAuth, err error) { + emptyResult := &v1alpha1.CredentialBasicAuth{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(credentialbasicauthsResource, "status", c.ns, credentialBasicAuth, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.CredentialBasicAuth), err +} + +// Delete takes name of the credentialBasicAuth and deletes it. Returns an error if one occurs. +func (c *FakeCredentialBasicAuths) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(credentialbasicauthsResource, c.ns, name, opts), &v1alpha1.CredentialBasicAuth{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeCredentialBasicAuths) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(credentialbasicauthsResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.CredentialBasicAuthList{}) + return err +} + +// Patch applies the patch and returns the patched credentialBasicAuth. +func (c *FakeCredentialBasicAuths) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.CredentialBasicAuth, err error) { + emptyResult := &v1alpha1.CredentialBasicAuth{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(credentialbasicauthsResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.CredentialBasicAuth), err +} diff --git a/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go b/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go index 014047a..decd96a 100644 --- a/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go +++ b/pkg/clientset/typed/configuration/v1alpha1/generated_expansion.go @@ -18,6 +18,8 @@ limitations under the License. package v1alpha1 +type CredentialBasicAuthExpansion interface{} + type IngressClassParametersExpansion interface{} type KongCACertificateExpansion interface{} diff --git a/test/crdsvalidation/credentialbasicauth/credentialbasicauth_test.go b/test/crdsvalidation/credentialbasicauth/credentialbasicauth_test.go new file mode 100644 index 0000000..a4d544b --- /dev/null +++ b/test/crdsvalidation/credentialbasicauth/credentialbasicauth_test.go @@ -0,0 +1,65 @@ +package credentialbasicauth + +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/credentialbasicauth/testcases" +) + +func CredentialBasicAuth(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.CredentialBasicAuths(tc.CredentialBasicAuth.Namespace) + entity, err := cl.Create(ctx, &tc.CredentialBasicAuth, 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.CredentialBasicAuthStatus != nil { + entity.Status = *tc.CredentialBasicAuthStatus + 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.NotNil(t, err) + assert.Contains(t, err.Error(), *tc.ExpectedUpdateErrorMessage) + } else { + assert.NoError(t, err) + } + } + } else { + require.NotNil(t, err) + assert.Contains(t, err.Error(), *tc.ExpectedErrorMessage) + } + }) + } + }) + } +} diff --git a/test/crdsvalidation/credentialbasicauth/testcases/common.go b/test/crdsvalidation/credentialbasicauth/testcases/common.go new file mode 100644 index 0000000..5a53eee --- /dev/null +++ b/test/crdsvalidation/credentialbasicauth/testcases/common.go @@ -0,0 +1,38 @@ +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 KongConsumer validation. +type testCase struct { + Name string + CredentialBasicAuth configurationv1alpha1.CredentialBasicAuth + CredentialBasicAuthStatus *configurationv1alpha1.CredentialBasicAuthStatus + Update func(*configurationv1alpha1.CredentialBasicAuth) + ExpectedErrorMessage *string + ExpectedUpdateErrorMessage *string +} + +// testCasesGroup is a group of test cases related to CredentialBasicAuth validation. +// The grouping is done by a common name. +type testCasesGroup struct { + Name string + TestCases []testCase +} + +// TestCases is a collection of all test cases groups related to CredentialBasicAuth validation. +var TestCases = []testCasesGroup{} + +func init() { + TestCases = append(TestCases, + updatesNotAllowedForStatus, + ) +} + +var commonObjectMeta = metav1.ObjectMeta{ + GenerateName: "test-credentialbasicauth-", + Namespace: "default", +} diff --git a/test/crdsvalidation/credentialbasicauth/testcases/consumerref-update-not-allowed-for-status.go b/test/crdsvalidation/credentialbasicauth/testcases/consumerref-update-not-allowed-for-status.go new file mode 100644 index 0000000..5a40cf7 --- /dev/null +++ b/test/crdsvalidation/credentialbasicauth/testcases/consumerref-update-not-allowed-for-status.go @@ -0,0 +1,115 @@ +package testcases + +import ( + "github.com/samber/lo" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" +) + +// updatesNotAllowedForStatus are test cases checking if updates to consumerRef +// are indeed blocked for some status conditions. +var updatesNotAllowedForStatus = testCasesGroup{ + Name: "updates not allowed for status conditions", + TestCases: []testCase{ + { + Name: "consumerRef change is not allowed for Programmed=True", + CredentialBasicAuth: configurationv1alpha1.CredentialBasicAuth{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.CredentialBasicAuthSpec{ + ConsumerRef: corev1.LocalObjectReference{ + Name: "test-kong-consumer", + }, + SecretRef: corev1.LocalObjectReference{ + Name: "test-secret", + }, + CredentialBasicAuthAPISpec: configurationv1alpha1.CredentialBasicAuthAPISpec{ + Password: lo.ToPtr("password"), + Username: lo.ToPtr("username"), + }, + }, + }, + CredentialBasicAuthStatus: &configurationv1alpha1.CredentialBasicAuthStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{}, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + Update: func(c *configurationv1alpha1.CredentialBasicAuth) { + c.Spec.ConsumerRef.Name = "new-consumer" + }, + ExpectedUpdateErrorMessage: lo.ToPtr("spec.consumerREf is immutable when an entity is already Programmed"), + }, + { + Name: "consumerRef change is allowed when consumer is not Programmed=True nor APIAuthValid=True", + CredentialBasicAuth: configurationv1alpha1.CredentialBasicAuth{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.CredentialBasicAuthSpec{ + ConsumerRef: corev1.LocalObjectReference{ + Name: "test-kong-consumer", + }, + SecretRef: corev1.LocalObjectReference{ + Name: "test-secret", + }, + CredentialBasicAuthAPISpec: configurationv1alpha1.CredentialBasicAuthAPISpec{ + Password: lo.ToPtr("password"), + Username: lo.ToPtr("username"), + }, + }, + }, + CredentialBasicAuthStatus: &configurationv1alpha1.CredentialBasicAuthStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{}, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionFalse, + Reason: "Invalid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + Update: func(c *configurationv1alpha1.CredentialBasicAuth) { + c.Spec.ConsumerRef.Name = "new-consumer" + }, + }, + { + Name: "secretRef change is allowed when consumer is Programmed=True", + CredentialBasicAuth: configurationv1alpha1.CredentialBasicAuth{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.CredentialBasicAuthSpec{ + ConsumerRef: corev1.LocalObjectReference{ + Name: "test-kong-consumer", + }, + SecretRef: corev1.LocalObjectReference{ + Name: "test-secret", + }, + CredentialBasicAuthAPISpec: configurationv1alpha1.CredentialBasicAuthAPISpec{ + Password: lo.ToPtr("password"), + Username: lo.ToPtr("username"), + }, + }, + }, + CredentialBasicAuthStatus: &configurationv1alpha1.CredentialBasicAuthStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{}, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + Update: func(c *configurationv1alpha1.CredentialBasicAuth) { + c.Spec.SecretRef.Name = "new-secret" + }, + }, + }, +} diff --git a/test/crdsvalidation/kongconsumergroup/kongconsumergroup_test.go b/test/crdsvalidation/kongconsumergroup/kongconsumergroup_test.go index bf28f1f..b752c0d 100644 --- a/test/crdsvalidation/kongconsumergroup/kongconsumergroup_test.go +++ b/test/crdsvalidation/kongconsumergroup/kongconsumergroup_test.go @@ -1,4 +1,4 @@ -package kongconsumer +package kongconsumergroup import ( "context"