diff --git a/.github/workflows/cts.yaml b/.github/workflows/cts.yaml index 9a0c5e0f7..fb22a966e 100644 --- a/.github/workflows/cts.yaml +++ b/.github/workflows/cts.yaml @@ -141,6 +141,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/.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/config/rbac/role.yaml b/config/rbac/role.yaml index fcc1026d2..e41a782b6 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -253,3 +253,14 @@ rules: - list - update - watch +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - update + - watch diff --git a/controllers/apps/v2beta1/add_pdb.go b/controllers/apps/v2beta1/add_pdb.go new file mode 100644 index 000000000..58cd15110 --- /dev/null +++ b/controllers/apps/v2beta1/add_pdb.go @@ -0,0 +1,72 @@ +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/v1" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + ctrl "sigs.k8s.io/controller-runtime" +) + +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 := ctrl.SetControllerReference(instance, pdb, a.Scheme); err != nil { + return subResult{err: emperror.Wrap(err, "failed to set controller reference")} + } + 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/v1", + 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.Int, + IntVal: 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..08c76b31a 100644 --- a/controllers/apps/v2beta1/emqx_controller.go +++ b/controllers/apps/v2beta1/emqx_controller.go @@ -126,6 +126,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}, diff --git a/deploy/charts/emqx-operator/Chart.yaml b/deploy/charts/emqx-operator/Chart.yaml index 5a5971ac7..a0e069e85 100644 --- a/deploy/charts/emqx-operator/Chart.yaml +++ b/deploy/charts/emqx-operator/Chart.yaml @@ -15,12 +15,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 2.2.17 +version: 2.2.18 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. -appVersion: 2.2.17 +appVersion: 2.2.18 sources: - https://github.com/emqx/emqx-operator/tree/main/deploy/charts/emqx-operator diff --git a/deploy/charts/emqx-operator/templates/controller-manager-rbac.yaml b/deploy/charts/emqx-operator/templates/controller-manager-rbac.yaml index 278ffaed9..173d5f40b 100644 --- a/deploy/charts/emqx-operator/templates/controller-manager-rbac.yaml +++ b/deploy/charts/emqx-operator/templates/controller-manager-rbac.yaml @@ -142,6 +142,17 @@ rules: - list - update - watch +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - update + - watch - apiGroups: - apps.emqx.io resources: diff --git a/main.go b/main.go index 81774f8bb..dfd5561ac 100644 --- a/main.go +++ b/main.go @@ -70,6 +70,7 @@ func init() { //+kubebuilder:rbac:groups="",resources=endpoints,verbs=get;list;watch;create;update //+kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;delete //+kubebuilder:rbac:groups=apps,resources=replicasets,verbs=get;list;watch;create;update;delete +//+kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;delete //+kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update func main() {