diff --git a/cmd/fetcher/fetcher.go b/cmd/fetcher/fetcher.go index e97e347..65f3cbc 100644 --- a/cmd/fetcher/fetcher.go +++ b/cmd/fetcher/fetcher.go @@ -8,17 +8,19 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" + "os" "regexp" "strings" "time" ) type Fetcher struct { - KubernetesNamespace string - SocketPath string - ContainerdNamespace string - Nodename string - ReadyDuration time.Duration + KubernetesNamespace string + SocketPath string + ContainerdNamespace string + Nodename string + ReadyDuration time.Duration + includeControllerNodes bool } func (fetcher *Fetcher) canonicalizeImageName(image string) string { @@ -72,8 +74,14 @@ func (fetcher *Fetcher) canonicalizeImageName(image string) string { return fullimage } -func (fetcher *Fetcher) isReadyForSomeTime(pod *v1.Pod) bool { +func (fetcher *Fetcher) isReadyForSomeTime(pod *v1.Pod, controllers map[string]bool) bool { 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 { if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue { 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 { + + controllers := fetcher.getControllerNames(clientset) + pods, err := clientset.CoreV1().Pods(fetcher.KubernetesNamespace).List(context.Background(), metav1.ListOptions{}) if err != nil { @@ -104,7 +115,7 @@ func (fetcher *Fetcher) getContainers(clientset *kubernetes.Clientset) map[strin if pod.Spec.NodeName == fetcher.Nodename { containersOnCurrentNode[fetcher.canonicalizeImageName(container.Image)] = true } else { - if fetcher.isReadyForSomeTime(&pod) { + if fetcher.isReadyForSomeTime(&pod, controllers) { 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 { containersOnCurrentNode[fetcher.canonicalizeImageName(container.Image)] = true } else { - if fetcher.isReadyForSomeTime(&pod) { + if fetcher.isReadyForSomeTime(&pod, controllers) { containers[fetcher.canonicalizeImageName(container.Image)] = true } } @@ -187,6 +198,21 @@ func (fetcher *Fetcher) pullAndPin() error { 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 // 1. periodic pull and pin, configurable time interval // 2. logging summary results of each pull and pin diff --git a/cmd/fetcher/main.go b/cmd/fetcher/main.go index dfdeaf2..5f04ce9 100644 --- a/cmd/fetcher/main.go +++ b/cmd/fetcher/main.go @@ -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") cmd.PersistentFlags().DurationVar(&fetcher.ReadyDuration, "ready-duration", 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) err := cmd.Execute()