Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

KO-358: Enhance AerospikeBackupService CR with all the possible deployment configuration params #328

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions api/v1beta1/aerospikebackupservice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,12 @@ type AerospikeBackupServiceSpec struct {
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Backup Service Config"
Config runtime.RawExtension `json:"config"`

// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Pod Configuration"
ServicePodSpec ServicePodSpec `json:"servicePodSpec,omitempty"`
Comment on lines +58 to +59
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Add comments for all new fields.
Suggested change
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Pod Configuration"
ServicePodSpec ServicePodSpec `json:"servicePodSpec,omitempty"`
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Pod Configuration"
PodSpec ServicePodSpec `json:"podSpec,omitempty"`


// Resources defines the requests and limits for the backup service container.
// Resources.Limits should be more than Resources.Requests.
// Deprecated: Resources field is now part of serviceContainerSpec
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources"
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`

Expand All @@ -82,9 +86,7 @@ type AerospikeBackupServiceStatus struct {
// It includes: service, backup-policies, storage, secret-agent.
Config runtime.RawExtension `json:"config,omitempty"`

// Resources defines the requests and limits for the backup service container.
// Resources.Limits should be more than Resources.Requests.
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
ServicePodSpec ServicePodSpec `json:"servicePodSpec,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above


// SecretMounts is the list of secret to be mounted in the backup service.
SecretMounts []SecretMount `json:"secrets,omitempty"`
Expand All @@ -103,6 +105,27 @@ type AerospikeBackupServiceStatus struct {
Port int32 `json:"port,omitempty"`
}

type ServicePodSpec struct {
// ServiceContainerSpec configures the backup service container
// created by the operator.
ServiceContainerSpec ServiceContainerSpec `json:"serviceContainer,omitempty"`

ObjectMeta AerospikeObjectMeta `json:"metadata,omitempty"`

// SchedulingPolicy controls pods placement on Kubernetes nodes.
SchedulingPolicy `json:",inline"`

ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
}

type ServiceContainerSpec struct {
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"`

// Resources defines the requests and limits for the backup service container.
// Resources.Limits should be more than Resources.Requests.
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:metadata:annotations="aerospike-kubernetes-operator/version=3.4.0"
Expand Down
84 changes: 82 additions & 2 deletions api/v1beta1/aerospikebackupservice_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v1beta1

import (
"fmt"
"reflect"

set "github.com/deckarep/golang-set/v2"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -28,6 +29,7 @@ import (
"sigs.k8s.io/yaml"

"github.com/aerospike/aerospike-backup-service/pkg/model"
asdbv1 "github.com/aerospike/aerospike-kubernetes-operator/api/v1"
)

func (r *AerospikeBackupService) SetupWebhookWithManager(mgr ctrl.Manager) error {
Expand All @@ -36,14 +38,20 @@ func (r *AerospikeBackupService) SetupWebhookWithManager(mgr ctrl.Manager) error
Complete()
}

// Implemented Defaulter interface for future reference
//nolint:lll // for readability
//+kubebuilder:webhook:path=/mutate-asdb-aerospike-com-v1beta1-aerospikebackupservice,mutating=true,failurePolicy=fail,sideEffects=None,groups=asdb.aerospike.com,resources=aerospikebackupservices,verbs=create;update,versions=v1beta1,name=maerospikebackupservice.kb.io,admissionReviewVersions=v1

Comment on lines +42 to +43
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Run make generate - to generate deepcopy code
  • Run make manifests - to generate manifests
  • Add new manifests new in the helm-chart

var _ webhook.Defaulter = &AerospikeBackupService{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *AerospikeBackupService) Default() {
absLog := logf.Log.WithName(namespacedName(r))

absLog.Info("Setting defaults for aerospikeBackupService")

if r.Spec.Resources != nil && r.Spec.ServicePodSpec.ServiceContainerSpec.Resources == nil {
r.Spec.ServicePodSpec.ServiceContainerSpec.Resources = r.Spec.Resources
}
}

//nolint:lll // for readability
Expand All @@ -65,11 +73,19 @@ func (r *AerospikeBackupService) ValidateCreate() (admission.Warnings, error) {
return nil, err
}

warn, err := r.validateServicePodSpec()

if err != nil {
return nil, err
} else if warn != nil {
return warn, nil
}

return nil, nil
Comment on lines +76 to 84
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
warn, err := r.validateServicePodSpec()
if err != nil {
return nil, err
} else if warn != nil {
return warn, nil
}
return nil, nil
return r.validateServicePodSpec()

}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *AerospikeBackupService) ValidateUpdate(_ runtime.Object) (admission.Warnings, error) {
func (r *AerospikeBackupService) ValidateUpdate(oldObj runtime.Object) (admission.Warnings, error) {
absLog := logf.Log.WithName(namespacedName(r))

absLog.Info("Validate update")
Expand All @@ -82,6 +98,14 @@ func (r *AerospikeBackupService) ValidateUpdate(_ runtime.Object) (admission.War
return nil, err
}

warn, err := r.validateServicePodSpec()

if err != nil {
return nil, err
} else if warn != nil {
return warn, nil
}

return nil, nil
Comment on lines +101 to 109
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}

Expand Down Expand Up @@ -139,3 +163,59 @@ func (r *AerospikeBackupService) validateBackupServiceSecrets() error {

return nil
}

func (r *AerospikeBackupService) validateServicePodSpec() (admission.Warnings, error) {
warn, err := r.validateResourcesField()
if warn != nil {
return warn, nil
} else if err != nil {
return nil, err
}
Comment on lines +169 to +173
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of warning, we don't have to return immediately as it's not an error.
Cover all the existing checks and return the warning in the very end


if err := r.validateResourceLimitsAndRequests(); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Club validateResourcesField and validateResourceLimitsAndRequests funcs -> validateResources

return nil, err
}

if err := validateObjectMeta(&r.Spec.ServicePodSpec.ObjectMeta); err != nil {
return nil, err
}

return nil, nil
}
func (r *AerospikeBackupService) validateResourcesField() (admission.Warnings, error) {
if r.Spec.Resources != nil && r.Spec.ServicePodSpec.ServiceContainerSpec.Resources != nil {
if !reflect.DeepEqual(r.Spec.Resources, r.Spec.ServicePodSpec.ServiceContainerSpec.Resources) {
return nil, fmt.Errorf("resources mismatched, different resources requirements shouldn't be allowed")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, fmt.Errorf("resources mismatched, different resources requirements shouldn't be allowed")
return nil, fmt.Errorf("resources mismatch, different resources requirements found in <deprecated-path > and <new -path>")

Update placeholder of path in the error msg something like spec.resources

} else {
return []string{"resources field in spec is deprecated, " +
"resources field is now part of servicePodSpec.serviceContainer"}, nil
Comment on lines +190 to +191
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Use path in the error msg for deprecated field as well
  2. Use complete path starting from spec.

}
}

return nil, nil
}
func (r *AerospikeBackupService) validateResourceLimitsAndRequests() error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think k8s native pkg has some validation funcs related to resource requirements.
Try to use them.

if r.Spec.ServicePodSpec.ServiceContainerSpec.Resources != nil {
resources := r.Spec.ServicePodSpec.ServiceContainerSpec.Resources
if resources.Limits != nil && resources.Requests != nil &&
((resources.Limits.Cpu().Cmp(*resources.Requests.Cpu()) < 0) ||
(resources.Limits.Memory().Cmp(*resources.Requests.Memory()) < 0)) {
return fmt.Errorf("resources.Limits cannot be less than resource.Requests. Resources %v",
resources)
}
}

return nil
}
func validateObjectMeta(objectMeta *AerospikeObjectMeta) error {
for label := range objectMeta.Labels {
if label == asdbv1.AerospikeAppLabel || label == asdbv1.AerospikeCustomResourceLabel {
return fmt.Errorf(
"label: %s is automatically defined by operator and shouldn't be specified by user",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"label: %s is automatically defined by operator and shouldn't be specified by user",
"label: %s is internally set by operator and shouldn't be specified by user",

label,
)
}
}

return nil
}
46 changes: 38 additions & 8 deletions internal/controller/backup-service/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
asdbv1beta1 "github.com/aerospike/aerospike-kubernetes-operator/api/v1beta1"
"github.com/aerospike/aerospike-kubernetes-operator/internal/controller/common"
"github.com/aerospike/aerospike-kubernetes-operator/pkg/utils"
lib "github.com/aerospike/aerospike-management-lib"
)

type serviceConfig struct {
Expand Down Expand Up @@ -314,12 +315,6 @@ func (r *SingleBackupServiceReconciler) getDeploymentObject() (*app.Deployment,
svcLabels := utils.LabelsForAerospikeBackupService(r.aeroBackupService.Name)
volumeMounts, volumes := r.getVolumeAndMounts()

resources := corev1.ResourceRequirements{}

if r.aeroBackupService.Spec.Resources != nil {
resources = *r.aeroBackupService.Spec.Resources
}

svcConf, err := r.getBackupServiceConfig()
if err != nil {
return nil, err
Expand Down Expand Up @@ -358,7 +353,6 @@ func (r *SingleBackupServiceReconciler) getDeploymentObject() (*app.Deployment,
Image: r.aeroBackupService.Spec.Image,
ImagePullPolicy: corev1.PullIfNotPresent,
VolumeMounts: volumeMounts,
Resources: resources,
Ports: containerPorts,
},
},
Expand Down Expand Up @@ -392,9 +386,44 @@ func (r *SingleBackupServiceReconciler) getDeploymentObject() (*app.Deployment,
},
}

r.updateDeploymentFromPodSpec(deploy)

return deploy, nil
}

func (r *SingleBackupServiceReconciler) updateDeploymentFromPodSpec(deploy *app.Deployment) {
r.updateDeploymentSchedulingPolicy(deploy)

defaultLabels := utils.LabelsForAerospikeBackupService(r.aeroBackupService.Name)
userDefinedLabels := r.aeroBackupService.Spec.ServicePodSpec.ObjectMeta.Labels
mergedLabels := utils.MergeLabels(defaultLabels, userDefinedLabels)
deploy.Spec.Template.ObjectMeta.Labels = mergedLabels

deploy.Spec.Template.ObjectMeta.Annotations = r.aeroBackupService.Spec.ServicePodSpec.ObjectMeta.Annotations

deploy.Spec.Template.Spec.ImagePullSecrets = r.aeroBackupService.Spec.ServicePodSpec.ImagePullSecrets

r.updateBackupServiceContainer(deploy)
}

func (r *SingleBackupServiceReconciler) updateDeploymentSchedulingPolicy(deploy *app.Deployment) {
deploy.Spec.Template.Spec.Affinity = r.aeroBackupService.Spec.ServicePodSpec.Affinity
deploy.Spec.Template.Spec.NodeSelector = r.aeroBackupService.Spec.ServicePodSpec.NodeSelector
deploy.Spec.Template.Spec.Tolerations = r.aeroBackupService.Spec.ServicePodSpec.Tolerations
}

func (r *SingleBackupServiceReconciler) updateBackupServiceContainer(deploy *app.Deployment) {
resources := r.aeroBackupService.Spec.ServicePodSpec.ServiceContainerSpec.Resources
if resources != nil {
deploy.Spec.Template.Spec.Containers[0].Resources = *resources
} else {
deploy.Spec.Template.Spec.Containers[0].Resources = corev1.ResourceRequirements{}
}

deploy.Spec.Template.Spec.Containers[0].SecurityContext =
r.aeroBackupService.Spec.ServicePodSpec.ServiceContainerSpec.SecurityContext
}

func (r *SingleBackupServiceReconciler) getVolumeAndMounts() ([]corev1.VolumeMount, []corev1.Volume) {
volumes := make([]corev1.Volume, 0, len(r.aeroBackupService.Spec.SecretMounts))
volumeMounts := make([]corev1.VolumeMount, 0, len(r.aeroBackupService.Spec.SecretMounts))
Expand Down Expand Up @@ -674,7 +703,8 @@ func (r *SingleBackupServiceReconciler) CopySpecToStatus() *asdbv1beta1.Aerospik
status := asdbv1beta1.AerospikeBackupServiceStatus{}
status.Image = r.aeroBackupService.Spec.Image
status.Config = r.aeroBackupService.Spec.Config
status.Resources = r.aeroBackupService.Spec.Resources
statusServicePodSpec := lib.DeepCopy(r.aeroBackupService.Spec.ServicePodSpec).(asdbv1beta1.ServicePodSpec)
status.ServicePodSpec = statusServicePodSpec
status.SecretMounts = r.aeroBackupService.Spec.SecretMounts
status.Service = r.aeroBackupService.Spec.Service

Expand Down
Loading
Loading