excluding controller node pods by default.

This commit is contained in:
Erik Brakkee 2025-03-02 17:18:13 +01:00
parent 1688aa6dea
commit 891fdc990c
2 changed files with 36 additions and 8 deletions

View File

@ -8,6 +8,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"os"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@ -19,6 +20,7 @@ type Fetcher struct {
ContainerdNamespace string ContainerdNamespace string
Nodename string Nodename string
ReadyDuration time.Duration ReadyDuration time.Duration
includeControllerNodes bool
} }
func (fetcher *Fetcher) canonicalizeImageName(image string) string { func (fetcher *Fetcher) canonicalizeImageName(image string) string {
@ -72,8 +74,14 @@ func (fetcher *Fetcher) canonicalizeImageName(image string) string {
return fullimage return fullimage
} }
func (fetcher *Fetcher) isReadyForSomeTime(pod *v1.Pod) bool { func (fetcher *Fetcher) isReadyForSomeTime(pod *v1.Pod, controllers map[string]bool) bool {
ready := false ready := false
if !fetcher.includeControllerNodes {
klog.Infof("Checking %s (%s)", pod.Name, pod.Spec.NodeName)
if _, ok := controllers[pod.Spec.NodeName]; ok {
return false
}
}
for _, condition := range pod.Status.Conditions { for _, condition := range pod.Status.Conditions {
if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue { if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue {
if time.Now().Sub(condition.LastTransitionTime.Time) > fetcher.ReadyDuration { if time.Now().Sub(condition.LastTransitionTime.Time) > fetcher.ReadyDuration {
@ -88,6 +96,9 @@ func (fetcher *Fetcher) isReadyForSomeTime(pod *v1.Pod) bool {
} }
func (fetcher *Fetcher) getContainers(clientset *kubernetes.Clientset) map[string]bool { func (fetcher *Fetcher) getContainers(clientset *kubernetes.Clientset) map[string]bool {
controllers := fetcher.getControllerNames(clientset)
pods, err := clientset.CoreV1().Pods(fetcher.KubernetesNamespace).List(context.Background(), pods, err := clientset.CoreV1().Pods(fetcher.KubernetesNamespace).List(context.Background(),
metav1.ListOptions{}) metav1.ListOptions{})
if err != nil { if err != nil {
@ -104,7 +115,7 @@ func (fetcher *Fetcher) getContainers(clientset *kubernetes.Clientset) map[strin
if pod.Spec.NodeName == fetcher.Nodename { if pod.Spec.NodeName == fetcher.Nodename {
containersOnCurrentNode[fetcher.canonicalizeImageName(container.Image)] = true containersOnCurrentNode[fetcher.canonicalizeImageName(container.Image)] = true
} else { } else {
if fetcher.isReadyForSomeTime(&pod) { if fetcher.isReadyForSomeTime(&pod, controllers) {
containers[fetcher.canonicalizeImageName(container.Image)] = true containers[fetcher.canonicalizeImageName(container.Image)] = true
} }
} }
@ -114,7 +125,7 @@ func (fetcher *Fetcher) getContainers(clientset *kubernetes.Clientset) map[strin
if pod.Spec.NodeName == fetcher.Nodename { if pod.Spec.NodeName == fetcher.Nodename {
containersOnCurrentNode[fetcher.canonicalizeImageName(container.Image)] = true containersOnCurrentNode[fetcher.canonicalizeImageName(container.Image)] = true
} else { } else {
if fetcher.isReadyForSomeTime(&pod) { if fetcher.isReadyForSomeTime(&pod, controllers) {
containers[fetcher.canonicalizeImageName(container.Image)] = true containers[fetcher.canonicalizeImageName(container.Image)] = true
} }
} }
@ -187,6 +198,21 @@ func (fetcher *Fetcher) pullAndPin() error {
return nil return nil
} }
func (fetcher *Fetcher) getControllerNames(clientset *kubernetes.Clientset) map[string]bool {
nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{
LabelSelector: "node-role.kubernetes.io/control-plane=,!node-role.kubernetes.io/master",
})
if err != nil {
fmt.Printf("Error listing nodes: %v\n", err)
os.Exit(1)
}
nodeset := make(map[string]bool)
for _, node := range nodes.Items {
nodeset[node.Name] = true
}
return nodeset
}
// TODO // TODO
// 1. periodic pull and pin, configurable time interval // 1. periodic pull and pin, configurable time interval
// 2. logging summary results of each pull and pin // 2. logging summary results of each pull and pin

View File

@ -37,6 +37,8 @@ so they don't get garbage collected'`,
"Kubernetes node name the fetcher is running on, it will only fetch images running on other nodes") "Kubernetes node name the fetcher is running on, it will only fetch images running on other nodes")
cmd.PersistentFlags().DurationVar(&fetcher.ReadyDuration, "ready-duration", cmd.PersistentFlags().DurationVar(&fetcher.ReadyDuration, "ready-duration",
1*time.Hour, "Time a pod must be ready before its image will be fetched") 1*time.Hour, "Time a pod must be ready before its image will be fetched")
cmd.PersistentFlags().BoolVar(&fetcher.includeControllerNodes, "include-controllers",
false, "Include controller nodes")
cmd.Flags().AddGoFlagSet(klogFlags) cmd.Flags().AddGoFlagSet(klogFlags)
err := cmd.Execute() err := cmd.Execute()