github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/controller/pxc/volumes.go (about)

     1  package pxc
     2  
     3  import (
     4  	"context"
     5  	"slices"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  	corev1 "k8s.io/api/core/v1"
    10  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    11  	"sigs.k8s.io/controller-runtime/pkg/client"
    12  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    13  
    14  	api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1"
    15  	"github.com/percona/percona-xtradb-cluster-operator/pkg/k8s"
    16  	"github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/app/statefulset"
    17  )
    18  
    19  func (r *ReconcilePerconaXtraDBCluster) reconcilePersistentVolumes(ctx context.Context, cr *api.PerconaXtraDBCluster) error {
    20  	log := logf.FromContext(ctx)
    21  
    22  	pxcSet := statefulset.NewNode(cr)
    23  	sts := pxcSet.StatefulSet()
    24  
    25  	labels := map[string]string{
    26  		"app.kubernetes.io/component":  "pxc",
    27  		"app.kubernetes.io/instance":   cr.Name,
    28  		"app.kubernetes.io/managed-by": "percona-xtradb-cluster-operator",
    29  		"app.kubernetes.io/name":       "percona-xtradb-cluster",
    30  		"app.kubernetes.io/part-of":    "percona-xtradb-cluster",
    31  	}
    32  
    33  	pvcList := corev1.PersistentVolumeClaimList{}
    34  	if err := r.client.List(ctx, &pvcList, client.InNamespace(cr.Namespace), client.MatchingLabels(labels)); err != nil {
    35  		return errors.Wrap(err, "list persistentvolumeclaims")
    36  	}
    37  
    38  	if cr.PVCResizeInProgress() {
    39  		resizeInProgress := false
    40  		for _, pvc := range pvcList.Items {
    41  			if !strings.HasPrefix(pvc.Name, "datadir-"+sts.Name) {
    42  				continue
    43  			}
    44  
    45  			for _, condition := range pvc.Status.Conditions {
    46  				if condition.Status != corev1.ConditionTrue {
    47  					continue
    48  				}
    49  
    50  				switch condition.Type {
    51  				case corev1.PersistentVolumeClaimResizing, corev1.PersistentVolumeClaimFileSystemResizePending:
    52  					resizeInProgress = true
    53  					log.V(1).Info(condition.Message, "pvc", pvc.Name, "type", condition.Type, "lastTransitionTime", condition.LastTransitionTime)
    54  					log.Info("PVC resize in progress", "pvc", pvc.Name, "lastTransitionTime", condition.LastTransitionTime)
    55  				}
    56  			}
    57  		}
    58  
    59  		if !resizeInProgress {
    60  			if err := k8s.DeannotateObject(ctx, r.client, cr, api.AnnotationPVCResizeInProgress); err != nil {
    61  				return errors.Wrap(err, "deannotate pxc")
    62  			}
    63  
    64  			log.Info("PVC resize completed")
    65  
    66  			return nil
    67  		}
    68  	}
    69  
    70  	err := r.client.Get(ctx, client.ObjectKeyFromObject(sts), sts)
    71  	if err != nil {
    72  		if k8serrors.IsNotFound(err) {
    73  			return nil
    74  		}
    75  		return errors.Wrapf(err, "get statefulset/%s", sts.Name)
    76  	}
    77  
    78  	if cr.Spec.PXC.VolumeSpec.PersistentVolumeClaim == nil {
    79  		return nil
    80  	}
    81  
    82  	var volumeTemplate corev1.PersistentVolumeClaim
    83  	for _, vct := range sts.Spec.VolumeClaimTemplates {
    84  		if vct.Name == "datadir" {
    85  			volumeTemplate = vct
    86  		}
    87  	}
    88  
    89  	requested := cr.Spec.PXC.VolumeSpec.PersistentVolumeClaim.Resources.Requests[corev1.ResourceStorage]
    90  	actual := volumeTemplate.Spec.Resources.Requests[corev1.ResourceStorage]
    91  
    92  	if requested.Cmp(actual) < 0 {
    93  		return errors.Wrap(err, "requested storage is less than actual")
    94  	}
    95  
    96  	if requested.Cmp(actual) == 0 {
    97  		return nil
    98  	}
    99  
   100  	err = k8s.AnnotateObject(ctx, r.client, cr, map[string]string{api.AnnotationPVCResizeInProgress: "true"})
   101  	if err != nil {
   102  		return errors.Wrap(err, "annotate pxc")
   103  	}
   104  
   105  	podList := corev1.PodList{}
   106  	if err := r.client.List(ctx, &podList, client.InNamespace(cr.Namespace), client.MatchingLabels(labels)); err != nil {
   107  		return errors.Wrap(err, "list pods")
   108  	}
   109  
   110  	podNames := make([]string, 0, len(podList.Items))
   111  	for _, pod := range podList.Items {
   112  		podNames = append(podNames, pod.Name)
   113  	}
   114  
   115  	pvcsToUpdate := make([]string, 0, len(pvcList.Items))
   116  	for _, pvc := range pvcList.Items {
   117  		if !strings.HasPrefix(pvc.Name, "datadir-"+sts.Name) {
   118  			continue
   119  		}
   120  
   121  		podName := strings.SplitN(pvc.Name, "-", 2)[1]
   122  		if !slices.Contains(podNames, podName) {
   123  			continue
   124  		}
   125  
   126  		pvcsToUpdate = append(pvcsToUpdate, pvc.Name)
   127  	}
   128  
   129  	log.Info("Resizing PVCs", "requested", requested, "actual", actual, "pvcList", strings.Join(pvcsToUpdate, ","))
   130  
   131  	log.Info("Deleting statefulset", "name", sts.Name)
   132  
   133  	if err := r.client.Delete(ctx, sts, client.PropagationPolicy("Orphan")); err != nil {
   134  		return errors.Wrapf(err, "delete statefulset/%s", sts.Name)
   135  	}
   136  
   137  	for _, pvc := range pvcList.Items {
   138  		if !slices.Contains(pvcsToUpdate, pvc.Name) {
   139  			continue
   140  		}
   141  
   142  		log.Info("Resizing PVC", "name", pvc.Name)
   143  		pvc.Spec.Resources.Requests[corev1.ResourceStorage] = requested
   144  
   145  		if err := r.client.Update(ctx, &pvc); err != nil {
   146  			return errors.Wrapf(err, "update persistentvolumeclaim/%s", pvc.Name)
   147  		}
   148  	}
   149  
   150  	return nil
   151  }