github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/k8s/graceful_shutdown.go (about)

     1  package k8s
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1"
     8  	"github.com/pkg/errors"
     9  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    10  	"sigs.k8s.io/controller-runtime/pkg/client"
    11  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    12  	"sigs.k8s.io/controller-runtime/pkg/manager/signals"
    13  )
    14  
    15  var log = logf.Log
    16  
    17  // StartStopSignalHandler starts gorutine which is waiting for termination
    18  // signal and returns a context which is cancelled when operator can really
    19  // stop.
    20  func StartStopSignalHandler(client client.Client, namespaces []string) context.Context {
    21  	ctx, shutdownFunc := context.WithCancel(context.Background())
    22  	go handleStopSignal(client, namespaces, shutdownFunc)
    23  	return ctx
    24  }
    25  
    26  func handleStopSignal(client client.Client, namespaces []string, shutdownFunc context.CancelFunc) {
    27  	<-signals.SetupSignalHandler().Done()
    28  	stop(client, namespaces)
    29  	shutdownFunc()
    30  }
    31  
    32  // Stop is used to understand, when we need to stop operator(usially SIGTERM)
    33  // to start cleanup process and delete required pxc clusters in current(operator)
    34  // namespace. See K8SPXC-529
    35  func stop(cl client.Client, namespaces []string) {
    36  	log.Info("Got stop signal, starting to list clusters")
    37  
    38  	readyToDelete := false
    39  
    40  	for !readyToDelete {
    41  		time.Sleep(5 * time.Second)
    42  		ready, err := checkClusters(cl, namespaces)
    43  		if err != nil {
    44  			log.Error(err, "delete clusters")
    45  		}
    46  		readyToDelete = ready
    47  	}
    48  }
    49  
    50  func checkClusters(cl client.Client, namespaces []string) (bool, error) {
    51  	for _, ns := range namespaces {
    52  
    53  		clusterList := &api.PerconaXtraDBClusterList{}
    54  
    55  		err := cl.List(context.TODO(), clusterList, &client.ListOptions{Namespace: ns})
    56  		if err != nil && !k8serrors.IsNotFound(err) {
    57  			return false, errors.Wrapf(err, "list clusters in namespace: %s", ns)
    58  		}
    59  
    60  		if clusterList.HasUnfinishedFinalizers() {
    61  			return false, nil
    62  		}
    63  
    64  		bcpList := api.PerconaXtraDBClusterBackupList{}
    65  
    66  		err = cl.List(context.TODO(), &bcpList, &client.ListOptions{Namespace: ns})
    67  		if err != nil && !k8serrors.IsNotFound(err) {
    68  			return false, errors.Wrap(err, "failed to get backup object")
    69  		}
    70  
    71  		if bcpList.HasUnfinishedFinalizers() {
    72  			return false, nil
    73  		}
    74  	}
    75  
    76  	return true, nil
    77  }