Skip to content

Commit

Permalink
kafka user certificates were refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
Bohdan Siryk authored and testisnullus committed Dec 12, 2023
1 parent 5138361 commit bf823eb
Show file tree
Hide file tree
Showing 35 changed files with 1,210 additions and 590 deletions.
12 changes: 12 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,16 @@ resources:
defaulting: true
validation: true
webhookVersion: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: instaclustr.com
group: kafkamanagement
kind: UserCertificate
path: github.com/instaclustr/operator/apis/kafkamanagement/v1beta1
version: v1beta1
webhooks:
validation: true
webhookVersion: v1
version: "3"
29 changes: 0 additions & 29 deletions apis/kafkamanagement/v1beta1/kafkauser_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
// KafkaUserSpec defines the desired state of KafkaUser
type KafkaUserSpec struct {
SecretRef *v1beta1.SecretReference `json:"secretRef"`
CertificateRequests []*CertificateRequest `json:"certificateRequests,omitempty"`
InitialPermissions string `json:"initialPermissions"`
OverrideExistingUser bool `json:"overrideExistingUser,omitempty"`
SASLSCRAMMechanism string `json:"saslScramMechanism"`
Expand All @@ -40,25 +39,6 @@ type KafkaUserStatus struct {
ClustersEvents map[string]string `json:"clustersEvents,omitempty"`
}

type Certificate struct {
ID string `json:"id,omitempty"`
ExpiryDate string `json:"expiryDate,omitempty"`
SignedCertificate string `json:"signedCertificate,omitempty"`
}

type CertificateRequest struct {
SecretName string `json:"secretName"`
SecretNamespace string `json:"secretNamespace"`
ClusterID string `json:"clusterId"`
CSR string `json:"csr,omitempty"`
ValidPeriod int `json:"validPeriod"`
CommonName string `json:"commonName,omitempty"`
Country string `json:"country,omitempty"`
Organization string `json:"organization,omitempty"`
OrganizationalUnit string `json:"organizationalUnit,omitempty"`
AutoRenew bool `json:"autoRenew"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

Expand Down Expand Up @@ -135,12 +115,3 @@ func (ks *KafkaUserSpec) ToInstAPI(clusterID string, username string, password s
Username: username,
}
}

func (cr *CertificateRequest) ToInstAPI(username string) *models.CertificateRequest {
return &models.CertificateRequest{
ClusterID: cr.ClusterID,
CSR: cr.CSR,
KafkaUsername: username,
ValidPeriod: cr.ValidPeriod,
}
}
28 changes: 10 additions & 18 deletions apis/kafkamanagement/v1beta1/kafkauser_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,35 +55,27 @@ func (r *KafkaUser) Default() {
var _ webhook.Validator = &KafkaUser{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *KafkaUser) ValidateCreate() error {
kafkauserlog.Info("validate create", "name", r.Name)

if len(r.Spec.CertificateRequests) != 0 {
return models.ErrNotEmptyCSRs
}
func (ku *KafkaUser) ValidateCreate() error {
kafkauserlog.Info("validate create", "name", ku.Name)

return nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *KafkaUser) ValidateUpdate(old runtime.Object) error {
kafkauserlog.Info("validate update", "name", r.Name)

for _, request := range r.Spec.CertificateRequests {
if request.CSR == "" {
if request.Organization == "" || request.OrganizationalUnit == "" || request.Country == "" || request.CommonName == "" {
return models.ErrEmptyCertGeneratingFields
}
}
func (ku *KafkaUser) ValidateUpdate(old runtime.Object) error {
kafkauserlog.Info("validate update", "name", ku.Name)

oldUser := old.(*KafkaUser)
if *ku.Spec.SecretRef != *oldUser.Spec.SecretRef {
return models.ErrImmutableSecretRef
}

return nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *KafkaUser) ValidateDelete() error {
kafkauserlog.Info("validate delete", "name", r.Name)
func (ku *KafkaUser) ValidateDelete() error {
kafkauserlog.Info("validate delete", "name", ku.Name)

// TODO(user): fill in your validation logic upon object deletion.
return nil
}
122 changes: 122 additions & 0 deletions apis/kafkamanagement/v1beta1/usercertificate_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
Copyright 2022.
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 v1beta1

import (
"crypto/x509/pkix"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// UserCertificateSpec defines the desired state of UserCertificateSpec
type UserCertificateSpec struct {
// SecretRef references to the secret which stores pre-generated certificate request.
// +kubebuilder:validation:XValidation:message="Cannot be changed after it is set",rule="self == oldSelf"
SecretRef *FromSecret `json:"secretRef,omitempty"`

// UserRef references to the KafkaUser resource to whom a certificate will be created.
// +kubebuilder:validation:XValidation:message="Cannot be changed after it is set",rule="self == oldSelf"
UserRef Reference `json:"userRef"`

// ClusterRef references to the Kafka resource to whom a certificate will be created.
// +kubebuilder:validation:XValidation:message="Cannot be changed after it is set",rule="self == oldSelf"
ClusterRef Reference `json:"clusterRef"`

// ValidPeriod is amount of month until a signed certificate is expired.
// +kubebuilder:validation:Min=3
// +kubebuilder:validation:Max=120
// +kubebuilder:validation:XValidation:message="Cannot be changed after it is set",rule="self == oldSelf"
ValidPeriod int `json:"validPeriod"`

// CertificateRequestTemplate is a template for generating a CSR.
// +kubebuilder:validation:XValidation:message="Cannot be changed after it is set",rule="self == oldSelf"
CertificateRequestTemplate *CSRTemplate `json:"certificateRequestTemplate,omitempty"`
}

type CSRTemplate struct {
Country string `json:"country"`
Organization string `json:"organization"`
OrganizationalUnit string `json:"organizationalUnit"`
}

func (c *CSRTemplate) ToSubject() pkix.Name {
return pkix.Name{
Country: []string{c.Country},
Organization: []string{c.Organization},
OrganizationalUnit: []string{c.OrganizationalUnit},
}
}

type FromSecret struct {
Reference `json:",inline"`
Key string `json:"key"`
}

type Reference struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
}

func (r *Reference) AsNamespacedName() types.NamespacedName {
return types.NamespacedName{
Name: r.Name,
Namespace: r.Namespace,
}
}

// UserCertificateStatus defines the observed state of UserCertificateStatus
type UserCertificateStatus struct {
// CertID is a unique identifier of a certificate on Instaclustr.
CertID string `json:"certId,omitempty"`

// ExpiryDate is a date when a signed certificate is expired.
ExpiryDate string `json:"expiryDate,omitempty"`

// SignedCertSecretRef references to a secret which stores signed cert.
SignedCertSecretRef Reference `json:"signedCertSecretRef,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// UserCertificate is the Schema for the usercertificates API
type UserCertificate struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec UserCertificateSpec `json:"spec,omitempty"`
Status UserCertificateStatus `json:"status,omitempty"`
}

func (csr *UserCertificate) NewPatch() client.Patch {
return client.MergeFrom(csr.DeepCopy())
}

//+kubebuilder:object:root=true

// UserCertificateList contains a list of UserCertificate
type UserCertificateList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []UserCertificate `json:"items"`
}

func init() {
SchemeBuilder.Register(&UserCertificate{}, &UserCertificateList{})
}
79 changes: 79 additions & 0 deletions apis/kafkamanagement/v1beta1/usercertificate_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2022.
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 v1beta1

import (
"errors"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

// log is for logging in this package.
var usercertificatelog = logf.Log.WithName("usercertificate-resource")

func (r *UserCertificate) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

//+kubebuilder:webhook:path=/validate-kafkamanagement-instaclustr-com-v1beta1-usercertificate,mutating=false,failurePolicy=fail,sideEffects=None,groups=kafkamanagement.instaclustr.com,resources=usercertificates,verbs=create;update,versions=v1beta1,name=vusercertificate.kb.io,admissionReviewVersions=v1

var _ webhook.Validator = &UserCertificate{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (cert *UserCertificate) ValidateCreate() error {
usercertificatelog.Info("validate create", "name", cert.Name)

if cert.Spec.SecretRef == nil && cert.Spec.CertificateRequestTemplate == nil {
return errors.New("one of the following fields should be set: spec.secretRef, spec.generateCSR")
}

if cert.Spec.SecretRef != nil && cert.Spec.CertificateRequestTemplate != nil {
return errors.New("only one of the following fields can be set: spec.secretRef, spec.generateCSR")
}

return nil
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (cert *UserCertificate) ValidateUpdate(old runtime.Object) error {
usercertificatelog.Info("validate update", "name", cert.Name)

oldCert := old.(*UserCertificate)

if oldCert.Spec.SecretRef != nil && cert.Spec.SecretRef == nil {
return errors.New("spec.secretRef cannot be removed after it is set")
}

if oldCert.Spec.CertificateRequestTemplate != nil && cert.Spec.CertificateRequestTemplate == nil {
return errors.New("spec.certificateRequestTemplate cannot be removed after it is set")
}

return nil
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (cert *UserCertificate) ValidateDelete() error {
usercertificatelog.Info("validate delete", "name", cert.Name)

// TODO(user): fill in your validation logic upon object deletion.
return nil
}
3 changes: 3 additions & 0 deletions apis/kafkamanagement/v1beta1/webhook_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ var _ = BeforeSuite(func() {
err = (&KafkaUser{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

err = (&UserCertificate{}).SetupWebhookWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

//+kubebuilder:scaffold:webhook

go func() {
Expand Down
Loading

0 comments on commit bf823eb

Please sign in to comment.