diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 92c397965..eb0dd8798 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -75,6 +75,10 @@ jobs: kind: ${{ matrix.emqx[0] }} name: ${{ matrix.emqx[1] }} file: ${{ matrix.emqx[2] }} + - name: Make sure all of pods can be deleted + run: | + kubectl delete ${{ matrix.emqx[0] }} ${{ matrix.emqx[1] }} + kubectl wait --for=delete pods -l "apps.emqx.io/instance=${{ matrix.emqx[1] }}" - if: failure() run: kubectl logs -l "control-plane=controller-manager" -n emqx-operator-system -c manager --tail=1000 - if: failure() diff --git a/controllers/apps/v2beta1/add_pdb.go b/controllers/apps/v2beta1/add_pdb.go new file mode 100644 index 000000000..681ed42a0 --- /dev/null +++ b/controllers/apps/v2beta1/add_pdb.go @@ -0,0 +1,68 @@ +package v2beta1 + +import ( + "context" + + emperror "emperror.dev/errors" + appsv2beta1 "github.com/emqx/emqx-operator/apis/apps/v2beta1" + innerReq "github.com/emqx/emqx-operator/internal/requester" + "github.com/go-logr/logr" + policyv1 "k8s.io/api/policy/v1beta1" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +type addPdb struct { + *EMQXReconciler +} + +func (a *addPdb) reconcile(ctx context.Context, logger logr.Logger, instance *appsv2beta1.EMQX, _ innerReq.RequesterInterface) subResult { + pdbList := generatePodDisruptionBudget(instance) + for _, pdb := range pdbList { + if err := a.Client.Create(ctx, pdb); err != nil { + if !k8sErrors.IsAlreadyExists(err) { + return subResult{err: emperror.Wrap(err, "failed to create PDB")} + } + } + } + return subResult{} +} + +func generatePodDisruptionBudget(instance *appsv2beta1.EMQX) []*policyv1.PodDisruptionBudget { + pdbList := []*policyv1.PodDisruptionBudget{} + corePdb := &policyv1.PodDisruptionBudget{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "policy/v1beta1", + Kind: "PodDisruptionBudget", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: instance.Namespace, + Name: instance.CoreNamespacedName().Name, + Labels: appsv2beta1.CloneAndMergeMap(appsv2beta1.DefaultLabels(instance), instance.Labels), + }, + Spec: policyv1.PodDisruptionBudgetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: appsv2beta1.CloneAndMergeMap( + appsv2beta1.DefaultCoreLabels(instance), + instance.Spec.CoreTemplate.Labels, + ), + }, + MinAvailable: &intstr.IntOrString{ + Type: intstr.String, + StrVal: "1", + }, + }, + } + pdbList = append(pdbList, corePdb) + if appsv2beta1.IsExistReplicant(instance) { + replPdb := corePdb.DeepCopy() + replPdb.Name = instance.ReplicantNamespacedName().Name + replPdb.Spec.Selector.MatchLabels = appsv2beta1.CloneAndMergeMap( + appsv2beta1.DefaultReplicantLabels(instance), + instance.Spec.ReplicantTemplate.Labels, + ) + pdbList = append(pdbList, replPdb) + } + return pdbList +} diff --git a/controllers/apps/v2beta1/emqx_controller.go b/controllers/apps/v2beta1/emqx_controller.go index 29a1e409f..d4828d1ef 100644 --- a/controllers/apps/v2beta1/emqx_controller.go +++ b/controllers/apps/v2beta1/emqx_controller.go @@ -30,6 +30,7 @@ import ( "github.com/go-logr/logr" "github.com/rory-z/go-hocon" corev1 "k8s.io/api/core/v1" + policyv1 "k8s.io/api/policy/v1beta1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" @@ -101,6 +102,10 @@ func (r *EMQXReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. } if instance.GetDeletionTimestamp() != nil { + _ = r.Client.DeleteAllOf(ctx, &policyv1.PodDisruptionBudget{}, + client.InNamespace(instance.Namespace), + client.MatchingLabels(appsv2beta1.CloneAndMergeMap(appsv2beta1.DefaultLabels(instance), instance.Labels)), + ) return ctrl.Result{}, nil } @@ -126,6 +131,7 @@ func (r *EMQXReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. &addSvc{r}, &addCore{r}, &addRepl{r}, + &addPdb{r}, &updatePodConditions{r}, &updateStatus{r}, &syncPods{r},