restic/kubernetes/pod_list.go

Summary

Maintainability
A
1 hr
Test Coverage
package kubernetes

import (
    "context"
    "fmt"

    "github.com/go-logr/logr"
    "sigs.k8s.io/controller-runtime/pkg/client"

    corev1 "k8s.io/api/core/v1"
)

// PodLister holds the state for listing the pods.
type PodLister struct {
    backupCommandAnnotation   string
    backupContainerAnnotation string
    fileExtensionAnnotation   string
    k8scli                    client.Client
    err                       error
    namespace                 string
    targetPods                map[string]struct{}
    skipPreBackup             bool
    log                       logr.Logger
    ctx                       context.Context
}

// BackupPod contains all information nessecary to execute the backupcommands.
type BackupPod struct {
    Command       string
    PodName       string
    ContainerName string
    Namespace     string
    FileExtension string
}

// NewPodLister returns a PodLister configured to find the defined annotations.
func NewPodLister(ctx context.Context, k8cli client.Client, backupCommandAnnotation, fileExtensionAnnotation, backupContainerAnnotation, namespace string, targetPods []string, skipPreBackup bool, log logr.Logger) *PodLister {
    tp := make(map[string]struct{})
    for _, name := range targetPods {
        tp[name] = struct{}{}
    }

    return &PodLister{
        backupCommandAnnotation:   backupCommandAnnotation,
        backupContainerAnnotation: backupContainerAnnotation,
        fileExtensionAnnotation:   fileExtensionAnnotation,
        k8scli:                    k8cli,
        namespace:                 namespace,
        targetPods:                tp,
        skipPreBackup:             skipPreBackup,
        log:                       log.WithName("k8sClient"),
        ctx:                       ctx,
    }
}

// ListPods finds a list of pods which have backup commands in their annotations.
func (p *PodLister) ListPods() ([]BackupPod, error) {
    p.log.Info("listing all pods", "annotation", p.backupCommandAnnotation, "namespace", p.namespace)

    if p.err != nil {
        return nil, p.err
    }

    if p.skipPreBackup {
        return nil, nil
    }

    pods := &corev1.PodList{}
    err := p.k8scli.List(p.ctx, pods, &client.ListOptions{Namespace: p.namespace})
    if err != nil {
        return nil, fmt.Errorf("can't list pods: %v", err)
    }

    foundPods := make([]BackupPod, 0)
    sameOwner := make(map[string]bool)
    for _, pod := range pods.Items {
        var execInContainer = pod.Spec.Containers[0].Name
        annotations := pod.GetAnnotations()

        if execInContainerName, ok := annotations[p.backupContainerAnnotation]; ok {
            execInContainer = execInContainerName
        }

        if pod.Status.Phase != corev1.PodRunning {
            continue
        }

        // if TARGET_PODS is set, skip pods which should not be targetd.
        _, ok := p.targetPods[pod.GetName()]
        if len(p.targetPods) > 0 && !ok {
            p.log.V(1).Info("pod not in target pod list, skipping", "pod", pod.GetName())
            continue
        }

        if command, ok := annotations[p.backupCommandAnnotation]; ok {

            fileExtension := annotations[p.fileExtensionAnnotation]

            owner := pod.OwnerReferences
            var firstOwnerID string
            if len(owner) > 0 {
                firstOwnerID = string(owner[0].UID)
            } else {
                firstOwnerID = string(pod.ObjectMeta.UID)
            }

            if _, ok := sameOwner[firstOwnerID]; !ok {
                sameOwner[firstOwnerID] = true
                p.log.Info("adding to backup list", "namespace", p.namespace, "pod", pod.Name)
                foundPods = append(foundPods, BackupPod{
                    Command:       command,
                    PodName:       pod.Name,
                    ContainerName: execInContainer,
                    Namespace:     p.namespace,
                    FileExtension: fileExtension,
                })
            }
        }
    }

    return foundPods, nil
}