Skip to content

Commit

Permalink
Restructure workload resources to align with Redis pkg
Browse files Browse the repository at this point in the history
Signed-off-by: Siddhesh Ghadi <[email protected]>
  • Loading branch information
svghadi committed Jan 26, 2024
1 parent 7c6d378 commit 2dec80f
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 121 deletions.
111 changes: 50 additions & 61 deletions controllers/argocd/server/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import (
"github.com/argoproj-labs/argocd-operator/pkg/util"
"github.com/argoproj-labs/argocd-operator/pkg/workloads"

"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
Expand All @@ -26,101 +27,96 @@ import (
// reconcileDeployment will ensure all ArgoCD Server deployment is present
func (sr *ServerReconciler) reconcileDeployment() error {

sr.Logger.Info("reconciling deployment")
deployTpl := sr.getServerDeploymentTpl()

serverDeploymentTmpl := sr.getServerDeploymentTmpl()

deploymentRequest := workloads.DeploymentRequest{
ObjectMeta: serverDeploymentTmpl.ObjectMeta,
Spec: serverDeploymentTmpl.Spec,
deployReq := workloads.DeploymentRequest{
ObjectMeta: deployTpl.ObjectMeta,
Spec: deployTpl.Spec,
Client: sr.Client,
Mutations: []mutation.MutateFunc{mutation.ApplyReconcilerMutation},
}

desiredDeployment, err := workloads.RequestDeployment(deploymentRequest)
desiredDeploy, err := workloads.RequestDeployment(deployReq)
if err != nil {
sr.Logger.Error(err, "reconcileDeployment: failed to request deployment", "name", desiredDeployment.Name, "namespace", desiredDeployment.Namespace)
sr.Logger.V(1).Info("reconcileDeployment: one or more mutations could not be applied")
return err
return errors.Wrapf(err, "reconcileDeployment: failed to request deployment %s in namespace %s", desiredDeploy.Name, desiredDeploy.Namespace)
}

if err := controllerutil.SetControllerReference(sr.Instance, desiredDeploy, sr.Scheme); err != nil {
sr.Logger.Error(err, "reconcileDeployment: failed to set owner reference for deployment", "name", desiredDeploy.Name, "namespace", desiredDeploy.Namespace)
}

// deployment doesn't exist in the namespace, create it
existingDeployment, err := workloads.GetDeployment(desiredDeployment.Name, desiredDeployment.Namespace, sr.Client)
existingDeploy, err := workloads.GetDeployment(desiredDeploy.Name, desiredDeploy.Namespace, sr.Client)
if err != nil {
if !errors.IsNotFound(err) {
sr.Logger.Error(err, "reconcileDeployment: failed to retrieve deployment", "name", desiredDeployment.Name, "namespace", desiredDeployment.Namespace)
return err
if !apierrors.IsNotFound(err) {
return errors.Wrapf(err, "reconcileDeployment: failed to retrieve deployment %s in namespace %s", desiredDeploy.Name, desiredDeploy.Namespace)
}

if err = controllerutil.SetControllerReference(sr.Instance, desiredDeployment, sr.Scheme); err != nil {
sr.Logger.Error(err, "reconcileDeployment: failed to set owner reference for deployment", "name", desiredDeployment.Name, "namespace", desiredDeployment.Namespace)
if err = workloads.CreateDeployment(desiredDeploy, sr.Client); err != nil {
return errors.Wrapf(err, "reconcileDeployment: failed to create deployment %s in namespace %s", desiredDeploy.Name, desiredDeploy.Namespace)
}

if err = workloads.CreateDeployment(desiredDeployment, sr.Client); err != nil {
sr.Logger.Error(err, "reconcileDeployment: failed to create deployment", "name", desiredDeployment.Name, "namespace", desiredDeployment.Namespace)
return err
}
sr.Logger.V(0).Info("reconcileDeployment: deployment created", "name", desiredDeployment.Name, "namespace", desiredDeployment.Namespace)
sr.Logger.V(0).Info("deployment created", "name", desiredDeploy.Name, "namespace", desiredDeploy.Namespace)
return nil
}

// difference in existing & desired deployment, update it
deploymentChanged := false
changed := false

fieldsToCompare := []struct {
existing, desired interface{}
extraAction func()
}{
{&existingDeployment.Spec.Template.Spec.Containers[0].Image, &desiredDeployment.Spec.Template.Spec.Containers[0].Image,
{&existingDeploy.Spec.Template.Spec.Containers[0].Image, &desiredDeploy.Spec.Template.Spec.Containers[0].Image,
func() {
existingDeployment.Spec.Template.ObjectMeta.Labels[common.ImageUpgradedKey] = time.Now().UTC().Format(common.TimeFormatMST)
existingDeploy.Spec.Template.ObjectMeta.Labels[common.ImageUpgradedKey] = time.Now().UTC().Format(common.TimeFormatMST)
},
},
{&existingDeployment.Spec.Template.Spec.Containers[0].Command, &desiredDeployment.Spec.Template.Spec.Containers[0].Command, nil},
{&existingDeployment.Spec.Template.Spec.Containers[0].Env, &desiredDeployment.Spec.Template.Spec.Containers[0].Env, nil},
{&existingDeployment.Spec.Template.Spec.Containers[0].Resources, &desiredDeployment.Spec.Template.Spec.Containers[0].Resources, nil},
{&existingDeployment.Spec.Template.Spec.Containers[0].VolumeMounts, &desiredDeployment.Spec.Template.Spec.Containers[0].VolumeMounts, nil},
{&existingDeployment.Spec.Template.Spec.Volumes, &desiredDeployment.Spec.Template.Spec.Volumes, nil},
{&existingDeployment.Spec.Template.Spec.NodeSelector, &desiredDeployment.Spec.Template.Spec.NodeSelector, nil},
{&existingDeployment.Spec.Template.Spec.Tolerations, &desiredDeployment.Spec.Template.Spec.Tolerations, nil},
{&existingDeployment.Spec.Template.Spec.ServiceAccountName, &desiredDeployment.Spec.Template.Spec.ServiceAccountName, nil},
{&existingDeployment.Spec.Template.Labels, &desiredDeployment.Spec.Template.Labels, nil},
{&existingDeployment.Spec.Replicas, &desiredDeployment.Spec.Replicas, nil},
{&existingDeployment.Spec.Selector, &desiredDeployment.Spec.Selector, nil},
{&existingDeployment.Labels, &desiredDeployment.Labels, nil},
{&existingDeploy.Spec.Template.Spec.Containers[0].Command, &desiredDeploy.Spec.Template.Spec.Containers[0].Command, nil},
{&existingDeploy.Spec.Template.Spec.Containers[0].Env, &desiredDeploy.Spec.Template.Spec.Containers[0].Env, nil},
{&existingDeploy.Spec.Template.Spec.Containers[0].Resources, &desiredDeploy.Spec.Template.Spec.Containers[0].Resources, nil},
{&existingDeploy.Spec.Template.Spec.Containers[0].VolumeMounts, &desiredDeploy.Spec.Template.Spec.Containers[0].VolumeMounts, nil},
{&existingDeploy.Spec.Template.Spec.Volumes, &desiredDeploy.Spec.Template.Spec.Volumes, nil},
{&existingDeploy.Spec.Template.Spec.NodeSelector, &desiredDeploy.Spec.Template.Spec.NodeSelector, nil},
{&existingDeploy.Spec.Template.Spec.Tolerations, &desiredDeploy.Spec.Template.Spec.Tolerations, nil},
{&existingDeploy.Spec.Template.Spec.ServiceAccountName, &desiredDeploy.Spec.Template.Spec.ServiceAccountName, nil},
{&existingDeploy.Spec.Template.Labels, &desiredDeploy.Spec.Template.Labels, nil},
{&existingDeploy.Spec.Replicas, &desiredDeploy.Spec.Replicas, nil},
{&existingDeploy.Spec.Selector, &desiredDeploy.Spec.Selector, nil},
{&existingDeploy.Labels, &desiredDeploy.Labels, nil},
}

for _, field := range fieldsToCompare {
argocdcommon.UpdateIfChanged(field.existing, field.desired, field.extraAction, &deploymentChanged)
argocdcommon.UpdateIfChanged(field.existing, field.desired, field.extraAction, &changed)
}

if deploymentChanged {
if err = workloads.UpdateDeployment(existingDeployment, sr.Client); err != nil {
sr.Logger.Error(err, "reconcileDeployment: failed to update deployment", "name", existingDeployment.Name, "namespace", existingDeployment.Namespace)
return err
}
sr.Logger.V(0).Info("reconcileDeployment: deployment updated", "name", existingDeployment.Name, "namespace", existingDeployment.Namespace)
// nothing changed, exit reconciliation
if !changed {
return nil
}

// deployment found, no changes detected
if err = workloads.UpdateDeployment(existingDeploy, sr.Client); err != nil {
return errors.Wrapf(err, "reconcileDeployment: failed to update deployment %s in namespace %s", existingDeploy.Name, existingDeploy.Namespace)
}

sr.Logger.V(0).Info("deployment updated", "name", existingDeploy.Name, "namespace", existingDeploy.Namespace)
return nil
}

// deleteDeployment will delete deployment with given name.
func (sr *ServerReconciler) deleteDeployment(name, namespace string) error {
if err := workloads.DeleteDeployment(name, namespace, sr.Client); err != nil {
sr.Logger.Error(err, "DeleteDeployment: failed to delete deployment", "name", name, "namespace", namespace)
return err
return errors.Wrapf(err, "deleteDeployment: failed to delete deployment %s in namespace %s", name, namespace)
}
sr.Logger.V(0).Info("DeleteDeployment: deployment deleted", "name", name, "namespace", namespace)
sr.Logger.V(0).Info("deployment deleted", "name", name, "namespace", namespace)
return nil
}

// getServerDeploymentTmpl returns server deployment object
func (sr *ServerReconciler) getServerDeploymentTmpl() *appsv1.Deployment {
func (sr *ServerReconciler) getServerDeploymentTpl() *appsv1.Deployment {

deploymentName := getDeploymentName(sr.Instance.Name)
deploymentLabels := common.DefaultResourceLabels(deploymentName, sr.Instance.Name, ServerControllerComponent)
// deployment metadata
objMeta := argoutil.GetObjMeta(resourceName, sr.Instance.Namespace, sr.Instance.Name, sr.Instance.Namespace, component)

// set deployment params
env := sr.Instance.Spec.Server.Env
Expand All @@ -137,15 +133,8 @@ func (sr *ServerReconciler) getServerDeploymentTmpl() *appsv1.Deployment {
replicas = sr.Instance.Spec.Server.Replicas
}

// create deployment
objMeta := metav1.ObjectMeta{
Name: deploymentName,
Namespace: sr.Instance.Namespace,
Labels: deploymentLabels,
}

podSpec := corev1.PodSpec{
ServiceAccountName: getServiceAccountName(sr.Instance.Name),
ServiceAccountName: resourceName,
Volumes: []corev1.Volume{
{
Name: common.SSHKnownHosts,
Expand Down Expand Up @@ -257,13 +246,13 @@ func (sr *ServerReconciler) getServerDeploymentTmpl() *appsv1.Deployment {
Spec: podSpec,
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
common.AppK8sKeyName: deploymentName,
common.AppK8sKeyName: resourceName,
},
},
},
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
common.AppK8sKeyName: deploymentName,
common.AppK8sKeyName: resourceName,
},
},
Replicas: replicas,
Expand Down
5 changes: 3 additions & 2 deletions controllers/argocd/server/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import (
func TestServerReconciler_createUpdateAndDeleteDeployment(t *testing.T) {
ns := argocdcommon.MakeTestNamespace()
sr := makeTestServerReconciler(t, ns)
setTestResourceNameAndLabels(sr)

expectedName := "argocd-server"
expectedName := "argocd-argocd-server"
expectedLabels := map[string]string{
"app.kubernetes.io/name": expectedName,
"app.kubernetes.io/instance": argocdcommon.TestArgoCDName,
"app.kubernetes.io/component": "server",
"app.kubernetes.io/component": "argocd-server",
"app.kubernetes.io/part-of": "argocd",
"app.kubernetes.io/managed-by": "argocd-operator",
}
Expand Down
83 changes: 37 additions & 46 deletions controllers/argocd/server/hpa.go
Original file line number Diff line number Diff line change
@@ -1,54 +1,42 @@
package server

import (
"reflect"

"github.com/argoproj-labs/argocd-operator/common"
"github.com/argoproj-labs/argocd-operator/controllers/argocd/argocdcommon"
"github.com/argoproj-labs/argocd-operator/pkg/argoutil"
"github.com/argoproj-labs/argocd-operator/pkg/mutation"
"github.com/argoproj-labs/argocd-operator/pkg/workloads"
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
autoscaling "k8s.io/api/autoscaling/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

// reconcileHorizontalPodAutoscaler will ensure that ArgoCD .Spec.Server.Autoscale resource is present.
func (sr *ServerReconciler) reconcileHorizontalPodAutoscaler() error {
sr.Logger.Info("reconciling horizontal pod autoscaler")

var (
maxReplicas int32 = 3
minReplicas int32 = 1
tcup int32 = 50
)

hpaName := getHPAName(sr.Instance.Name)
hpaNS := sr.Instance.Namespace
hpaLabels := common.DefaultResourceLabels(hpaName, hpaNS, ServerControllerComponent)

deploymentName := getDeploymentName(sr.Instance.Name)

// AutoScale not enabled, cleanup any existing hpa & exit
if !sr.Instance.Spec.Server.Autoscale.Enabled {
return sr.deleteHorizontalPodAutoscaler(hpaName, hpaNS)
return sr.deleteHorizontalPodAutoscaler(resourceName, sr.Instance.Namespace)
}

hpaReq := workloads.HorizontalPodAutoscalerRequest{
ObjectMeta: metav1.ObjectMeta{
Name: hpaName,
Namespace: hpaNS,
Labels: hpaLabels,
Annotations: sr.Instance.Annotations,
},
ObjectMeta: argoutil.GetObjMeta(resourceName, sr.Instance.Namespace, sr.Instance.Name, sr.Instance.Namespace, component),
Spec: autoscaling.HorizontalPodAutoscalerSpec{
MaxReplicas: maxReplicas,
MinReplicas: &minReplicas,
TargetCPUUtilizationPercentage: &tcup,
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
APIVersion: appsv1.GroupName,
Kind: common.DeploymentKind,
Name: deploymentName,
Name: resourceName,
},
},
Client: sr.Client,
Expand All @@ -62,61 +50,64 @@ func (sr *ServerReconciler) reconcileHorizontalPodAutoscaler() error {

desiredHPA, err := workloads.RequestHorizontalPodAutoscaler(hpaReq)
if err != nil {
sr.Logger.Error(err, "reconcileHorizontalPodAutoscaler: failed to request hpa", "name", desiredHPA.Name, "namespace", desiredHPA.Namespace)
sr.Logger.V(1).Info("reconcileHorizontalPodAutoscaler: one or more mutations could not be applied")
return err
return errors.Wrapf(err, "reconcileHorizontalPodAutoscaler: failed to request hpa %s in namespace %s", desiredHPA.Name, desiredHPA.Namespace)
}

if err := controllerutil.SetControllerReference(sr.Instance, desiredHPA, sr.Scheme); err != nil {
sr.Logger.Error(err, "reconcileHorizontalPodAutoscaler: failed to set owner reference for hpa", "name", desiredHPA.Name, "namespace", desiredHPA.Namespace)
}

// hpa doesn't exist in the namespace, create it
existingHPA, err := workloads.GetHorizontalPodAutoscaler(desiredHPA.Name, desiredHPA.Namespace, sr.Client)
if err != nil {
if !errors.IsNotFound(err) {
sr.Logger.Error(err, "reconcileHorizontalPodAutoscaler: failed to retrieve hpa", "name", desiredHPA.Name, "namespace", desiredHPA.Namespace)
return err
}

if err = controllerutil.SetControllerReference(sr.Instance, desiredHPA, sr.Scheme); err != nil {
sr.Logger.Error(err, "reconcileHorizontalPodAutoscaler: failed to set owner reference for hpa", "name", desiredHPA.Name, "namespace", desiredHPA.Namespace)
if !apierrors.IsNotFound(err) {
return errors.Wrapf(err, "reconcileHorizontalPodAutoscaler: failed to retrieve hpa %s in namespace %s", desiredHPA.Name, desiredHPA.Namespace)
}

if err = workloads.CreateHorizontalPodAutoscaler(desiredHPA, sr.Client); err != nil {
sr.Logger.Error(err, "reconcileHorizontalPodAutoscaler: failed to create hpa", "name", desiredHPA.Name, "namespace", desiredHPA.Namespace)
return err
return errors.Wrapf(err, "reconcileHorizontalPodAutoscaler: failed to create hpa %s in namespace %s", desiredHPA.Name, desiredHPA.Namespace)
}
sr.Logger.V(0).Info("reconcileHorizontalPodAutoscaler: hpa created", "name", desiredHPA.Name, "namespace", desiredHPA.Namespace)

sr.Logger.V(0).Info("hpa created", "name", desiredHPA.Name, "namespace", desiredHPA.Namespace)
return nil
}

// difference in existing & desired hpa, update it
changed := false
if !reflect.DeepEqual(existingHPA.Spec, desiredHPA.Spec) {
existingHPA.Spec = desiredHPA.Spec
changed = true

fieldsToCompare := []struct {
existing, desired interface{}
extraAction func()
}{
{&existingHPA.Spec, &desiredHPA.Spec, nil},
}

if changed {
if err = workloads.UpdateHorizontalPodAutoscaler(existingHPA, sr.Client); err != nil {
sr.Logger.Error(err, "reconcileHorizontalPodAutoscaler: failed to update hpa", "name", existingHPA.Name, "namespace", existingHPA.Namespace)
return err
}
for _, field := range fieldsToCompare {
argocdcommon.UpdateIfChanged(field.existing, field.desired, field.extraAction, &changed)
}

// nothing changed, exit reconciliation
if !changed {
return nil
}

sr.Logger.V(0).Info("reconcileHorizontalPodAutoscaler: hpa updated", "name", existingHPA.Name, "namespace", existingHPA.Namespace)
if err = workloads.UpdateHorizontalPodAutoscaler(existingHPA, sr.Client); err != nil {
return errors.Wrapf(err, "reconcileHorizontalPodAutoscaler: failed to update hpa %s in namespace %s", existingHPA.Name, existingHPA.Namespace)
}

// hpa found, no changes detected
sr.Logger.V(0).Info("hpa updated", "name", existingHPA.Name, "namespace", existingHPA.Namespace)
return nil

}

// deleteHorizontalPodAutoscaler will delete hpa with given name.
func (sr *ServerReconciler) deleteHorizontalPodAutoscaler(name, namespace string) error {
if err := workloads.DeleteHorizontalPodAutoscaler(name, namespace, sr.Client); err != nil {
if errors.IsNotFound(err) {
if apierrors.IsNotFound(err) {
return nil
}
sr.Logger.Error(err, "deleteHorizontalPodAutoscaler: failed to delete hpa", "name", name, "namespace", namespace)
return err
return errors.Wrapf(err, "deleteHorizontalPodAutoscaler: failed to delete hpa %s in namespace %s", name, namespace)
}
sr.Logger.V(0).Info("deleteHorizontalPodAutoscaler: hpa deleted", "name", name, "namespace", namespace)
sr.Logger.V(0).Info("hpa deleted", "name", name, "namespace", namespace)
return nil
}
7 changes: 4 additions & 3 deletions controllers/argocd/server/hpa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
func TestServerReconciler_createUpdateAndDeleteHPA(t *testing.T) {
ns := argocdcommon.MakeTestNamespace()
sr := makeTestServerReconciler(t, ns)
setTestResourceNameAndLabels(sr)

// configure autoscale in ArgoCD
sr.Instance.Spec.Server.Autoscale = argoproj.ArgoCDServerAutoscaleSpec{
Expand All @@ -27,7 +28,7 @@ func TestServerReconciler_createUpdateAndDeleteHPA(t *testing.T) {

// hpa resource should be created with default values
hpa := &autoscaling.HorizontalPodAutoscaler{}
err = sr.Client.Get(context.TODO(), types.NamespacedName{Name: "argocd-server", Namespace: "argocd"}, hpa)
err = sr.Client.Get(context.TODO(), types.NamespacedName{Name: "argocd-argocd-server", Namespace: "argocd"}, hpa)
assert.NoError(t, err)
assert.Equal(t, int32(3), hpa.Spec.MaxReplicas)

Expand All @@ -44,7 +45,7 @@ func TestServerReconciler_createUpdateAndDeleteHPA(t *testing.T) {

// hpa resource should be updated
hpa = &autoscaling.HorizontalPodAutoscaler{}
err = sr.Client.Get(context.TODO(), types.NamespacedName{Name: "argocd-server", Namespace: "argocd"}, hpa)
err = sr.Client.Get(context.TODO(), types.NamespacedName{Name: "argocd-argocd-server", Namespace: "argocd"}, hpa)
assert.NoError(t, err)
assert.Equal(t, int32(2), hpa.Spec.MaxReplicas)

Expand All @@ -55,7 +56,7 @@ func TestServerReconciler_createUpdateAndDeleteHPA(t *testing.T) {

// hpa resource should be deleted
hpa = &autoscaling.HorizontalPodAutoscaler{}
err = sr.Client.Get(context.TODO(), types.NamespacedName{Name: "argocd-server", Namespace: "argocd"}, hpa)
err = sr.Client.Get(context.TODO(), types.NamespacedName{Name: "argocd-argocd-server", Namespace: "argocd"}, hpa)
assert.Error(t, err)
assert.True(t, errors.IsNotFound(err))
}
Loading

0 comments on commit 2dec80f

Please sign in to comment.