Browse Source

add PodDisruptionBudge resources

tags/0.0.3
Vladimir Smagin 7 months ago
parent
commit
404de23507
9 changed files with 179 additions and 21 deletions
  1. +3
    -3
      Dockerfile
  2. +1
    -1
      VERSION
  3. +1
    -1
      deploy/operator.yaml
  4. +11
    -0
      pkg/apis/blindage/v1alpha1/redis_types.go
  5. +23
    -0
      pkg/controller/manifests/manifest_pdb.go
  6. +51
    -12
      pkg/controller/redis/reconcile.go
  7. +18
    -0
      pkg/controller/redis/reconcile_rules.go
  8. +70
    -3
      pkg/controller/redis/redis_controller.go
  9. +1
    -1
      version/version.go

+ 3
- 3
Dockerfile View File

@@ -1,7 +1,7 @@
FROM golang:1.13-alpine
FROM golang:1.13

RUN apk update
RUN apk add git mercurial
RUN apt-get update
RUN apt-get install -y git mercurial

WORKDIR /app/operator
COPY . .


+ 1
- 1
VERSION View File

@@ -1 +1 @@
0.0.2.1
0.0.3

+ 1
- 1
deploy/operator.yaml View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: redis-operator
containers:
- name: redis-operator
image: iam21h/redis-operator:0.0.2.1
image: iam21h/redis-operator:0.0.3
command:
- redis-operator
imagePullPolicy: Always


+ 11
- 0
pkg/apis/blindage/v1alpha1/redis_types.go View File

@@ -6,6 +6,12 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// PDB used to describe PodDisruptionBudget resources in RedisSpec
type PDB struct {
MinAvailable *int32 `json:"minAvailable,omitempty"`
MaxUnavailable *int32 `json:"maxUnavailable,omitempty"`
}

// RedisSpec defines the desired state of Redis
// +k8s:openapi-gen=true
type RedisSpec struct {
@@ -42,6 +48,11 @@ type RedisSpec struct {
RedisAffinity *v1.Affinity `json:"redisAffinity,omitempty"`
RedisNodeSelector *map[string]string `json:"redisNodeSelector"`
RedisTolerations *[]v1.Toleration `json:"redisTolerations"`

// Create PodDisruptionBudget resources for Redis, Sentinel and Haproxy
PdbRedis *PDB `json:"pdbRedis,omitempty"`
PdbSentinel *PDB `json:"pdbSentinel,omitempty"`
PdbHaproxy *PDB `json:"pdbHaproxy,omitempty"`
}

// RedisStatus defines the observed state of Redis


+ 23
- 0
pkg/controller/manifests/manifest_pdb.go View File

@@ -0,0 +1,23 @@
package manifests

import (
blindagev1alpha1 "git.blindage.org/21h/redis-operator/pkg/apis/blindage/v1alpha1"

"k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// GeneratePdbResource used to create PodDisruptionBudget resources
func GeneratePdbResource(cr *blindagev1alpha1.Redis, name string, pdbSpec v1beta1.PodDisruptionBudgetSpec) v1beta1.PodDisruptionBudget {

pdb := v1beta1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: cr.Namespace,
Labels: BaseLabels(cr),
},
Spec: pdbSpec,
}
return pdb

}

+ 51
- 12
pkg/controller/redis/reconcile.go View File

@@ -5,6 +5,7 @@ import (

"git.blindage.org/21h/redis-operator/pkg/controller/manifests"
v1 "k8s.io/api/apps/v1"
"k8s.io/api/policy/v1beta1"

blindagev1alpha1 "git.blindage.org/21h/redis-operator/pkg/apis/blindage/v1alpha1"
raven "github.com/getsentry/raven-go"
@@ -16,6 +17,44 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// ReconcilePodDisruptionBudget means full reconcile of PodDisruptionBudget
func (r *ReconcileRedis) ReconcilePodDisruptionBudget(reqLogger logr.Logger, cr *blindagev1alpha1.Redis, pdbName string, pdbSpec v1beta1.PodDisruptionBudgetSpec) (reconcile.Result, error) {

newPodDisruptionBudget := manifests.GeneratePdbResource(cr, pdbName, pdbSpec)

if err := controllerutil.SetControllerReference(cr, &newPodDisruptionBudget, r.scheme); err != nil {
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
}

foundPodDisruptionBudget := v1beta1.PodDisruptionBudget{}
err := r.client.Get(context.TODO(), types.NamespacedName{Name: newPodDisruptionBudget.Name, Namespace: newPodDisruptionBudget.Namespace}, &foundPodDisruptionBudget)
if err != nil && errors.IsNotFound(err) {
reqLogger.Info("Creating a PodDisruptionBudget", "Namespace", newPodDisruptionBudget.Namespace, "Name", newPodDisruptionBudget.Name)
err = r.client.Create(context.TODO(), &newPodDisruptionBudget)
if err != nil {
reqLogger.Info("Creating PodDisruptionBudget error", "Namespace", newPodDisruptionBudget.Namespace, "Name", newPodDisruptionBudget.Name, "Error", err)
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
}
} else if err != nil {
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
} else {
if reconcileRequired, reconResource := reconcilePodDisruptionBudgetApply(foundPodDisruptionBudget, newPodDisruptionBudget); reconcileRequired {
reqLogger.Info("Updating PodDisruptionBudget", "Namespace", reconResource.Namespace, "Name", reconResource.Name)
if err = r.client.Update(context.TODO(), &reconResource); err != nil {
reqLogger.Info("Reconcile PodDisruptionBudget error", "Namespace", foundPodDisruptionBudget.Namespace, "Name", foundPodDisruptionBudget.Name, "Error", err)
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
}
}
}

reqLogger.Info("Reconcile PodDisruptionBudget complete", "PodDisruptionBudget Name", pdbName, "Namespace", cr.Namespace, "Name", cr.Name)
return reconcile.Result{}, nil
}

// ReconcileConfigmap means full reconcile of configmap
func (r *ReconcileRedis) ReconcileConfigmap(reqLogger logr.Logger, cr *blindagev1alpha1.Redis, configmapName string, configmapData map[string]string) (reconcile.Result, error) {

@@ -40,9 +79,9 @@ func (r *ReconcileRedis) ReconcileConfigmap(reqLogger logr.Logger, cr *blindagev
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
} else {
if reconcileRequired, reconService := reconcileConfigmapApply(foundConfigmap, newConfigmap); reconcileRequired {
reqLogger.Info("Updating Sentinel Configmap", "Namespace", reconService.Namespace, "Name", reconService.Name)
if err = r.client.Update(context.TODO(), &reconService); err != nil {
if reconcileRequired, reconResource := reconcileConfigmapApply(foundConfigmap, newConfigmap); reconcileRequired {
reqLogger.Info("Updating Sentinel Configmap", "Namespace", reconResource.Namespace, "Name", reconResource.Name)
if err = r.client.Update(context.TODO(), &reconResource); err != nil {
reqLogger.Info("Reconcile Sentinel Configmap error", "Namespace", foundConfigmap.Namespace, "Name", foundConfigmap.Name, "Error", err)
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
@@ -79,9 +118,9 @@ func (r *ReconcileRedis) ReconcileService(reqLogger logr.Logger, cr *blindagev1a
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
} else {
if reconcileRequired, reconService := reconcileServiceApply(foundService, newService); reconcileRequired {
reqLogger.Info("Updating Service", "Namespace", reconService.Namespace, "Name", reconService.Name)
if err = r.client.Update(context.TODO(), &reconService); err != nil {
if reconcileRequired, reconResource := reconcileServiceApply(foundService, newService); reconcileRequired {
reqLogger.Info("Updating Service", "Namespace", reconResource.Namespace, "Name", reconResource.Name)
if err = r.client.Update(context.TODO(), &reconResource); err != nil {
reqLogger.Info("Reconcile Service error", "Namespace", foundService.Namespace, "Name", foundService.Name, "Error", err)
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
@@ -115,9 +154,9 @@ func (r *ReconcileRedis) ReconcileDeployment(reqLogger logr.Logger, cr *blindage
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
} else {
if reconcileRequired, reconDeployment := reconcileDeploymentApply(foundDeployment, deployment); reconcileRequired {
reqLogger.Info("Updating Deployment", "Namespace", reconDeployment.Namespace, "Name", reconDeployment.Name)
if err = r.client.Update(context.TODO(), &reconDeployment); err != nil {
if reconcileRequired, reconResource := reconcileDeploymentApply(foundDeployment, deployment); reconcileRequired {
reqLogger.Info("Updating Deployment", "Namespace", reconResource.Namespace, "Name", reconResource.Name)
if err = r.client.Update(context.TODO(), &reconResource); err != nil {
reqLogger.Info("Reconcile Deployment error", "Namespace", foundDeployment.Namespace, "Name", foundDeployment.Name, "Error", err)
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
@@ -151,9 +190,9 @@ func (r *ReconcileRedis) ReconcileStatefulSet(reqLogger logr.Logger, cr *blindag
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
} else {
if reconcileRequired, reconDeployment := reconcileStatefulsetApply(foundStatefulset, newStatefulset); reconcileRequired {
reqLogger.Info("Updating Redis Statefulset", "Namespace", reconDeployment.Namespace, "Name", reconDeployment.Name)
if err = r.client.Update(context.TODO(), &reconDeployment); err != nil {
if reconcileRequired, reconResource := reconcileStatefulsetApply(foundStatefulset, newStatefulset); reconcileRequired {
reqLogger.Info("Updating Redis Statefulset", "Namespace", reconResource.Namespace, "Name", reconResource.Name)
if err = r.client.Update(context.TODO(), &reconResource); err != nil {
reqLogger.Info("Reconcile Redis Statefulset error", "Namespace", foundStatefulset.Namespace, "Name", foundStatefulset.Name, "Error", err)
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err


+ 18
- 0
pkg/controller/redis/reconcile_rules.go View File

@@ -5,6 +5,7 @@ import (

v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/api/policy/v1beta1"
)

// check if reconcile required
@@ -34,6 +35,23 @@ func reconcileConfigmapApply(foundConfigmap corev1.ConfigMap, newConfigmap corev
return reconcileRequired, foundConfigmap
}

func reconcilePodDisruptionBudgetApply(foundPodDisruptionBudget v1beta1.PodDisruptionBudget, newPodDisruptionBudget v1beta1.PodDisruptionBudget) (bool, v1beta1.PodDisruptionBudget) {

reconcileRequired := false

if !reflect.DeepEqual(foundPodDisruptionBudget.Spec.MaxUnavailable, newPodDisruptionBudget.Spec.MaxUnavailable) {
foundPodDisruptionBudget.Spec.MaxUnavailable = newPodDisruptionBudget.Spec.MaxUnavailable
reconcileRequired = true
}

if !reflect.DeepEqual(foundPodDisruptionBudget.Spec.MinAvailable, newPodDisruptionBudget.Spec.MinAvailable) {
foundPodDisruptionBudget.Spec.MinAvailable = newPodDisruptionBudget.Spec.MinAvailable
reconcileRequired = true
}

return reconcileRequired, foundPodDisruptionBudget
}

func reconcileStatefulsetApply(foundStatefulSet v1.StatefulSet, newStatefulSet v1.StatefulSet) (bool, v1.StatefulSet) {

reconcileRequired := false


+ 70
- 3
pkg/controller/redis/redis_controller.go View File

@@ -8,12 +8,15 @@ import (

"git.blindage.org/21h/redis-operator/pkg/controller/manifests"
rediscli "github.com/go-redis/redis"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"

blindagev1alpha1 "git.blindage.org/21h/redis-operator/pkg/apis/blindage/v1alpha1"
raven "github.com/getsentry/raven-go"
v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -103,10 +106,10 @@ func (r *ReconcileRedis) Reconcile(request reconcile.Request) (reconcile.Result,

_, err = r.reconcileFinalizers(reqLogger, instance)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
raven.CaptureErrorAndWait(err, nil)
return reconcile.Result{}, err
}
// Prepare Sentinel config
configSentinelName := instance.Name + "-sentinel"
configSentinelTemplate := `
@@ -174,6 +177,70 @@ fi
return reconcile.Result{}, err
}

// create PDB resources
if instance.Spec.PdbRedis != nil {
pdbName := instance.Name + "-redis"
pdbSpec := v1beta1.PodDisruptionBudgetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: manifests.MergeLabels(manifests.BaseLabels(instance), map[string]string{"component": "redis"}),
},
}

if instance.Spec.PdbRedis.MaxUnavailable != nil {
pdbSpec.MaxUnavailable = &intstr.IntOrString{Type: intstr.Int, IntVal: *instance.Spec.PdbRedis.MaxUnavailable}
}
if instance.Spec.PdbRedis.MinAvailable != nil {
pdbSpec.MinAvailable = &intstr.IntOrString{Type: intstr.Int, IntVal: *instance.Spec.PdbRedis.MinAvailable}
}

if _, err := r.ReconcilePodDisruptionBudget(reqLogger, instance, pdbName, pdbSpec); err != nil {
return reconcile.Result{}, err
}
}

if instance.Spec.PdbSentinel != nil {
pdbName := instance.Name + "-sentinel"
pdbSpec := v1beta1.PodDisruptionBudgetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: manifests.MergeLabels(manifests.BaseLabels(instance), map[string]string{"component": "sentinel"}),
},
}
if instance.Spec.PdbSentinel.MaxUnavailable != nil {
pdbSpec.MaxUnavailable = &intstr.IntOrString{Type: intstr.Int, IntVal: *instance.Spec.PdbSentinel.MaxUnavailable}
}
if instance.Spec.PdbSentinel.MinAvailable != nil {
// adorable if MinAvailable > (SentinelReplicas/2), just to save quorum
if *instance.Spec.PdbSentinel.MinAvailable <= (*instance.Spec.SentinelReplicas / 2) {
reqLogger.Error(err, "Sentinel MinAvailable must be greater then sentinelReplicas/2 to save quorum", "Namespace", instance.Namespace, "Name", instance.Name)
return reconcile.Result{}, err
}
pdbSpec.MinAvailable = &intstr.IntOrString{Type: intstr.Int, IntVal: *instance.Spec.PdbSentinel.MinAvailable}
}

if _, err := r.ReconcilePodDisruptionBudget(reqLogger, instance, pdbName, pdbSpec); err != nil {
return reconcile.Result{}, err
}
}

if instance.Spec.PdbHaproxy != nil {
pdbName := instance.Name + "-haproxy"
pdbSpec := v1beta1.PodDisruptionBudgetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: manifests.MergeLabels(manifests.BaseLabels(instance), map[string]string{"component": "haproxy"}),
},
}
if instance.Spec.PdbHaproxy.MaxUnavailable != nil {
pdbSpec.MaxUnavailable = &intstr.IntOrString{Type: intstr.Int, IntVal: *instance.Spec.PdbHaproxy.MaxUnavailable}
}
if instance.Spec.PdbHaproxy.MinAvailable != nil {
pdbSpec.MinAvailable = &intstr.IntOrString{Type: intstr.Int, IntVal: *instance.Spec.PdbHaproxy.MinAvailable}
}

if _, err := r.ReconcilePodDisruptionBudget(reqLogger, instance, pdbName, pdbSpec); err != nil {
return reconcile.Result{}, err
}
}

// set Redis master

podList := &corev1.PodList{}


+ 1
- 1
version/version.go View File

@@ -1,5 +1,5 @@
package version

var (
Version = "0.0.2.1"
Version = "0.0.3"
)

Loading…
Cancel
Save