Skip to content

Commit

Permalink
Merge pull request #99 from cybozu-go/add-pdb
Browse files Browse the repository at this point in the history
create PodDisruptionBudget
  • Loading branch information
kfyharukz authored Nov 4, 2020
2 parents 03b1206 + 86078ca commit dc86d17
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,13 @@ rules:
- get
- patch
- update
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- create
- get
- list
- update
- watch
47 changes: 47 additions & 0 deletions controllers/mysqlcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import (
appsv1 "k8s.io/api/apps/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -86,6 +88,7 @@ type MySQLClusterReconciler struct {
// +kubebuilder:rbac:groups="batch",resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="batch",resources=cronjobs/status,verbs=get
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;patch
// +kubebuilder:rbac:groups="policy",resources=poddisruptionbudgets,verbs=get;list;watch;create;update

// Reconcile reconciles MySQLCluster.
func (r *MySQLClusterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
Expand Down Expand Up @@ -232,6 +235,12 @@ func (r *MySQLClusterReconciler) reconcileInitialize(ctx context.Context, log lo
return false, err
}

isUpdated, err = r.createOrUpdatePodDisruptionBudget(ctx, log, cluster)
isUpdatedAtLeastOnce = isUpdatedAtLeastOnce || isUpdated
if err != nil {
return false, err
}

return isUpdatedAtLeastOnce, nil
}

Expand Down Expand Up @@ -264,6 +273,7 @@ func (r *MySQLClusterReconciler) SetupWithManager(mgr ctrl.Manager, watcherInter
Owns(&corev1.ServiceAccount{}).
Owns(&corev1.ConfigMap{}).
Owns(&batchv1beta1.CronJob{}).
Owns(&policyv1beta1.PodDisruptionBudget{}).
Watches(&src, &handler.EnqueueRequestForObject{}).
WithOptions(
controller.Options{MaxConcurrentReconciles: 8},
Expand Down Expand Up @@ -1121,6 +1131,43 @@ func (r *MySQLClusterReconciler) createOrUpdateService(ctx context.Context, clus
return isUpdated, nil
}

func (r *MySQLClusterReconciler) createOrUpdatePodDisruptionBudget(ctx context.Context, log logr.Logger, cluster *mocov1alpha1.MySQLCluster) (bool, error) {
isUpdated := false

pdb := &policyv1beta1.PodDisruptionBudget{}
pdb.SetNamespace(cluster.Namespace)
pdb.SetName(moco.UniqueName(cluster))

op, err := ctrl.CreateOrUpdate(ctx, r.Client, pdb, func() error {
setStandardLabels(&pdb.ObjectMeta, cluster)
pdb.Spec.MaxUnavailable = &intstr.IntOrString{}
pdb.Spec.MaxUnavailable.Type = intstr.Int
if cluster.Spec.Replicas > 1 {
pdb.Spec.MaxUnavailable.IntVal = cluster.Spec.Replicas / 2
} else {
pdb.Spec.MaxUnavailable.IntVal = 1
}
pdb.Spec.Selector = &metav1.LabelSelector{}
pdb.Spec.Selector.MatchLabels = map[string]string{
moco.ClusterKey: moco.UniqueName(cluster),
moco.ManagedByKey: moco.MyName,
moco.AppNameKey: moco.AppName,
}

return ctrl.SetControllerReference(cluster, pdb, r.Scheme)
})
if err != nil {
log.Error(err, "unable to create-or-update PodDisruptionBudget")
return false, err
}
if op != controllerutil.OperationResultNone {
log.Info("reconcile PodDisruptionBudget successfully", "op", op)
isUpdated = true
}

return isUpdated, nil
}

func (r *MySQLClusterReconciler) generateAgentToken(ctx context.Context, log logr.Logger, cluster *mocov1alpha1.MySQLCluster) (bool, error) {
if len(cluster.Status.AgentToken) != 0 {
return false, nil
Expand Down
75 changes: 75 additions & 0 deletions controllers/mysqlcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
k8serror "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -737,4 +738,78 @@ var _ = Describe("MySQLCluster controller", func() {
Expect(isUpdated).Should(BeFalse())
})
})

Context("PodDisruptionBudget", func() {
It("should create pod disruption budget", func() {
expectedSpec := policyv1beta1.PodDisruptionBudgetSpec{
MaxUnavailable: &intstr.IntOrString{
Type: intstr.Int,
IntVal: 1,
},
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
moco.ClusterKey: moco.UniqueName(cluster),
moco.AppNameKey: moco.AppName,
moco.ManagedByKey: moco.MyName,
},
},
}

cluster.Spec.Replicas = 1

isUpdated, err := reconciler.createOrUpdatePodDisruptionBudget(ctx, reconciler.Log, cluster)
Expect(err).ShouldNot(HaveOccurred())
Expect(isUpdated).Should(BeTrue())

pdb := &policyv1beta1.PodDisruptionBudget{}
err = k8sClient.Get(ctx, client.ObjectKey{Name: moco.UniqueName(cluster), Namespace: cluster.Namespace}, pdb)
Expect(err).ShouldNot(HaveOccurred())

Expect(pdb.Spec).Should(Equal(expectedSpec))

isUpdated, err = reconciler.createOrUpdatePodDisruptionBudget(ctx, reconciler.Log, cluster)
Expect(err).ShouldNot(HaveOccurred())
Expect(isUpdated).Should(BeFalse())
})

It("should fill appropriate MaxUnavailable", func() {
expectedSpec := policyv1beta1.PodDisruptionBudgetSpec{
MaxUnavailable: &intstr.IntOrString{
Type: intstr.Int,
IntVal: 1,
},
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
moco.ClusterKey: moco.UniqueName(cluster),
moco.AppNameKey: moco.AppName,
moco.ManagedByKey: moco.MyName,
},
},
}

By("checking in case of 5 replicas")
cluster.Spec.Replicas = 5
isUpdated, err := reconciler.createOrUpdatePodDisruptionBudget(ctx, reconciler.Log, cluster)
Expect(err).ShouldNot(HaveOccurred())
Expect(isUpdated).Should(BeTrue())

pdb := &policyv1beta1.PodDisruptionBudget{}
err = k8sClient.Get(ctx, client.ObjectKey{Name: moco.UniqueName(cluster), Namespace: cluster.Namespace}, pdb)
Expect(err).ShouldNot(HaveOccurred())
expectedSpec.MaxUnavailable.IntVal = 2
Expect(pdb.Spec).Should(Equal(expectedSpec))

By("checking in case of 3 replicas")
cluster.Spec.Replicas = 3
isUpdated, err = reconciler.createOrUpdatePodDisruptionBudget(ctx, reconciler.Log, cluster)
Expect(err).ShouldNot(HaveOccurred())
Expect(isUpdated).Should(BeTrue())

pdb = &policyv1beta1.PodDisruptionBudget{}
err = k8sClient.Get(ctx, client.ObjectKey{Name: moco.UniqueName(cluster), Namespace: cluster.Namespace}, pdb)
Expect(err).ShouldNot(HaveOccurred())
expectedSpec.MaxUnavailable.IntVal = 1
Expect(pdb.Spec).Should(Equal(expectedSpec))
})
})
})

0 comments on commit dc86d17

Please sign in to comment.