Ctrl K
ComunidadGuías…

pkg/audit/cnpg.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package audit

import (
	"github.com/skyhook-io/radar/pkg/cnpg"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

const checkCNPGNoDeclarativeBackup = "cnpgNoDeclarativeBackup"

// cnpgBarmanPluginName is the barman-cloud CNPG-I plugin. Clusters that have
// migrated to it keep their backup config in an ObjectStore CR in a different
// API group, and CNPG stops publishing the in-tree RPO status fields.
const cnpgBarmanPluginName = cnpg.BarmanPluginName

// checkCNPGDeclarativeBackup flags CNPG Clusters with no ScheduledBackup
// targeting them.
//
// Deliberately narrow: the absence of a ScheduledBackup does NOT prove a
// cluster is unprotected — on-demand Backups, volume snapshots and
// plugin-external schedulers all exist — so the finding asserts only what is
// observable ("no declarative backup schedule"), at posture severity.
//
// Suspended schedules count as present. Suspension is a deliberate operator
// action, and re-reporting it here would duplicate intent as a defect.
func checkCNPGDeclarativeBackup(tr *evalTracker, input *CheckInput) []Finding {
	if input == nil || len(input.CNPGClusters) == 0 {
		return nil
	}
	// Without a synced cluster-wide ScheduledBackup informer the inventory may
	// cover only a subset of namespaces, so "no schedule found" could simply
	// mean "not listed". Assert nothing, and record nothing — a check that
	// couldn't run must not inflate its own passed count.
	if !input.CNPGScheduledBackupsAuthoritative {
		return nil
	}

	// A ScheduledBackup and the Cluster it targets are always in the same
	// namespace; spec.method may be barmanObjectStore, volumeSnapshot or plugin,
	// and all three are declarative schedules.
	scheduled := map[string]bool{}
	for _, sb := range input.CNPGScheduledBackups {
		if sb == nil {
			continue
		}
		target, _, _ := unstructured.NestedString(sb.Object, "spec", "cluster", "name")
		if target == "" {
			continue
		}
		scheduled[sb.GetNamespace()+"\x00"+target] = true
	}

	var findings []Finding
	for _, c := range input.CNPGClusters {
		if c == nil {
			continue
		}
		ns, name := c.GetNamespace(), c.GetName()
		tr.record(checkCNPGNoDeclarativeBackup, ns)
		if scheduled[ns+"\x00"+name] {
			continue
		}
		findings = append(findings, Finding{
			Kind:      "Cluster",
			Group:     c.GroupVersionKind().Group,
			Namespace: ns,
			Name:      name,
			CheckID:   checkCNPGNoDeclarativeBackup,
			Category:  CategoryReliability,
			Severity:  SeverityWarning,
			Message:   cnpgNoScheduleMessage(c),
		})
	}
	return findings
}

func cnpgNoScheduleMessage(c *unstructured.Unstructured) string {
	const base = "No ScheduledBackup targets this PostgreSQL cluster, so no backup schedule is declared in the cluster. " +
		"On-demand Backups or an external scheduler may still cover it — this reports what is declared, not whether backups exist."
	switch {
	case cnpgHasBarmanPlugin(c):
		return base + " Backup for this cluster runs through the barman-cloud plugin; a ScheduledBackup with spec.method: plugin is what schedules it."
	case cnpgHasInTreeBackup(c):
		return base + " A backup destination is configured (spec.backup.barmanObjectStore), but nothing triggers it on a schedule."
	default:
		return base + " No backup destination is configured on the cluster either."
	}
}

func cnpgHasBarmanPlugin(c *unstructured.Unstructured) bool {
	_, present := cnpg.ParseBackupDeclaration(c).BarmanPlugin()
	return present
}

func cnpgHasInTreeBackup(c *unstructured.Unstructured) bool {
	return cnpg.ParseBackupDeclaration(c).InTreeConfigured
}