From 4df6d0904c94db97afd8832907d6abcce0aeb6fe Mon Sep 17 00:00:00 2001 From: SataQiu Date: Sun, 29 Mar 2026 11:56:38 +0800 Subject: [PATCH] kubeadm: add kubeproxydaemonset patch target --- cmd/kubeadm/app/apis/kubeadm/types.go | 2 +- cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go | 2 +- cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go | 2 +- cmd/kubeadm/app/cmd/options/generic.go | 2 +- cmd/kubeadm/app/cmd/phases/init/addons.go | 4 +- cmd/kubeadm/app/cmd/phases/upgrade/addons.go | 6 +-- cmd/kubeadm/app/phases/addons/proxy/proxy.go | 39 +++++++++++++++++-- .../app/phases/addons/proxy/proxy_test.go | 39 ++++++++++++++++++- cmd/kubeadm/app/util/patches/patches.go | 3 ++ 9 files changed, 86 insertions(+), 13 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 4b7acc2fe32..3022b9b969d 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -487,7 +487,7 @@ type HostPathMount struct { type Patches struct { // Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". // For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of - // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment", "kubeproxydaemonset". // "patchtype" can be one of "strategic" "merge" or "json" and they match the patch formats supported by kubectl. // The default "patchtype" is "strategic". "extension" must be either "json" or "yaml". // "suffix" is an optional string that can be used to determine which patches are applied diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go index 74aa4d79744..cc0d8cc37f0 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go @@ -441,7 +441,7 @@ type HostPathMount struct { type Patches struct { // Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". // For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of - // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment", "kubeproxydaemonset". // "patchtype" can be one of "strategic" "merge" or "json" and they match the patch formats supported by kubectl. // The default "patchtype" is "strategic". "extension" must be either "json" or "yaml". // "suffix" is an optional string that can be used to determine which patches are applied diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go index 7da4c05063e..50bf8e4491e 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go @@ -507,7 +507,7 @@ type HostPathMount struct { type Patches struct { // Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". // For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of - // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment", "kubeproxydaemonset". // "patchtype" can be one of "strategic" "merge" or "json" and they match the patch formats supported by kubectl. // The default "patchtype" is "strategic". "extension" must be either "json" or "yaml". // "suffix" is an optional string that can be used to determine which patches are applied diff --git a/cmd/kubeadm/app/cmd/options/generic.go b/cmd/kubeadm/app/cmd/options/generic.go index 950386f3e3e..3deb47525aa 100644 --- a/cmd/kubeadm/app/cmd/options/generic.go +++ b/cmd/kubeadm/app/cmd/options/generic.go @@ -105,7 +105,7 @@ func AddPatchesFlag(fs *pflag.FlagSet, patchesDir *string) { const usage = `Path to a directory that contains files named ` + `"target[suffix][+patchtype].extension". For example, ` + `"kube-apiserver0+merge.yaml" or just "etcd.json". ` + - `"target" can be one of "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". ` + + `"target" can be one of "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment", "kubeproxydaemonset". ` + `"patchtype" can be one of "strategic", "merge" or "json" and they match the patch formats ` + `supported by kubectl. The default "patchtype" is "strategic". "extension" must be either ` + `"json" or "yaml". "suffix" is an optional string that can be used to determine ` + diff --git a/cmd/kubeadm/app/cmd/phases/init/addons.go b/cmd/kubeadm/app/cmd/phases/init/addons.go index 71fcb812bf3..c63c3bf1a2c 100644 --- a/cmd/kubeadm/app/cmd/phases/init/addons.go +++ b/cmd/kubeadm/app/cmd/phases/init/addons.go @@ -114,11 +114,11 @@ func runCoreDNSAddon(c workflow.RunData) error { // runKubeProxyAddon installs KubeProxy addon to a Kubernetes cluster func runKubeProxyAddon(c workflow.RunData) error { - cfg, client, _, out, err := getInitData(c) + cfg, client, patchesDir, out, err := getInitData(c) if err != nil { return err } - return proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, printManifest) + return proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, patchesDir, out, printManifest) } func getAddonPhaseFlags(name string) []string { diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/addons.go b/cmd/kubeadm/app/cmd/phases/upgrade/addons.go index f07edad3cba..90cb1b938d7 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/addons.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/addons.go @@ -118,7 +118,7 @@ func runCoreDNSAddon(c workflow.RunData) error { func runKubeProxyAddon(c workflow.RunData) error { const skipMessagePrefix = "[upgrade/addon] Skipping the addon/kube-proxy phase." - cfg, client, _, out, dryRun, isControlPlaneNode, err := getInitData(c) + cfg, client, patchesDir, out, dryRun, isControlPlaneNode, err := getInitData(c) if err != nil { return err } @@ -141,7 +141,7 @@ func runKubeProxyAddon(c workflow.RunData) error { return nil } - if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, dryRun); err != nil { + if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, patchesDir, out, dryRun); err != nil { return err } @@ -154,7 +154,7 @@ func getAddonPhaseFlags(name string) []string { options.KubeconfigPath, options.DryRun, } - if name == "all" || name == "coredns" { + if name == "all" || name == "coredns" || name == "kube-proxy" { flags = append(flags, options.Patches, ) diff --git a/cmd/kubeadm/app/phases/addons/proxy/proxy.go b/cmd/kubeadm/app/phases/addons/proxy/proxy.go index ee2e072ba07..8fc8f4bc83e 100644 --- a/cmd/kubeadm/app/phases/addons/proxy/proxy.go +++ b/cmd/kubeadm/app/phases/addons/proxy/proxy.go @@ -30,6 +30,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" clientset "k8s.io/client-go/kubernetes" clientsetscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/yaml" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs" @@ -38,6 +39,7 @@ import ( kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" + "k8s.io/kubernetes/cmd/kubeadm/app/util/patches" ) const ( @@ -49,13 +51,13 @@ const ( ) // EnsureProxyAddon creates the kube-proxy addons -func EnsureProxyAddon(cfg *kubeadmapi.ClusterConfiguration, localEndpoint *kubeadmapi.APIEndpoint, client clientset.Interface, out io.Writer, printManifest bool) error { +func EnsureProxyAddon(cfg *kubeadmapi.ClusterConfiguration, localEndpoint *kubeadmapi.APIEndpoint, client clientset.Interface, patchesDir string, out io.Writer, printManifest bool) error { cmByte, err := createKubeProxyConfigMap(cfg, localEndpoint, client, printManifest) if err != nil { return err } - dsByte, err := createKubeProxyAddon(cfg, client, printManifest) + dsByte, err := createKubeProxyAddon(cfg, client, patchesDir, out, printManifest) if err != nil { return err } @@ -245,7 +247,7 @@ func createKubeProxyConfigMap(cfg *kubeadmapi.ClusterConfiguration, localEndpoin return []byte(""), apiclient.CreateOrUpdate(client.CoreV1().ConfigMaps(kubeproxyConfigMap.GetNamespace()), kubeproxyConfigMap) } -func createKubeProxyAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, printManifest bool) ([]byte, error) { +func createKubeProxyAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, patchesDir string, output io.Writer, printManifest bool) ([]byte, error) { daemonSetbytes, err := kubeadmutil.ParseTemplate(KubeProxyDaemonSet19, struct{ Image, ProxyConfigMap, ProxyConfigMapKey string }{ Image: images.GetKubernetesImage(constants.KubeProxy, cfg), ProxyConfigMap: constants.KubeProxyConfigMap, @@ -255,6 +257,13 @@ func createKubeProxyAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset return []byte(""), errors.Wrap(err, "error when parsing kube-proxy daemonset template") } + if len(patchesDir) != 0 { + daemonSetbytes, err = applyKubeProxyDaemonSetPatches(daemonSetbytes, patchesDir, output) + if err != nil { + return []byte(""), errors.Wrap(err, "could not apply patches to the kube-proxy DaemonSet") + } + } + if printManifest { return daemonSetbytes, nil } @@ -270,3 +279,27 @@ func createKubeProxyAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset // Create the DaemonSet for kube-proxy or update it in case it already exists return []byte(""), apiclient.CreateOrUpdate(client.AppsV1().DaemonSets(kubeproxyDaemonSet.GetNamespace()), kubeproxyDaemonSet) } + +// applyKubeProxyDaemonSetPatches reads patches from a directory and applies them over the input kubeProxyDaemonSetBytes. +func applyKubeProxyDaemonSetPatches(kubeProxyDaemonSetBytes []byte, patchesDir string, output io.Writer) ([]byte, error) { + patchManager, err := patches.GetPatchManagerForPath(patchesDir, patches.KnownTargets(), output) + if err != nil { + return nil, err + } + + patchTarget := &patches.PatchTarget{ + Name: patches.KubeProxyDaemonSet, + StrategicMergePatchObject: apps.DaemonSet{}, + Data: kubeProxyDaemonSetBytes, + } + if err := patchManager.ApplyPatchesToTarget(patchTarget); err != nil { + return nil, err + } + + kubeProxyDaemonSetBytes, err = yaml.JSONToYAML(patchTarget.Data) + if err != nil { + return nil, err + } + + return kubeProxyDaemonSetBytes, nil +} diff --git a/cmd/kubeadm/app/phases/addons/proxy/proxy_test.go b/cmd/kubeadm/app/phases/addons/proxy/proxy_test.go index 2f7b722d1d2..a17c7f03e06 100644 --- a/cmd/kubeadm/app/phases/addons/proxy/proxy_test.go +++ b/cmd/kubeadm/app/phases/addons/proxy/proxy_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "os" + "path/filepath" "strings" "testing" "time" @@ -156,7 +157,7 @@ func TestEnsureProxyAddon(t *testing.T) { initConfiguration.ClusterConfiguration.Networking.PodSubnet = "2001:101::/48" } - err = EnsureProxyAddon(&initConfiguration.ClusterConfiguration, &initConfiguration.LocalAPIEndpoint, client, os.Stdout, false) + err = EnsureProxyAddon(&initConfiguration.ClusterConfiguration, &initConfiguration.LocalAPIEndpoint, client, "", os.Stdout, false) // Compare actual to expected errors actErr := "No error" @@ -178,6 +179,42 @@ func TestEnsureProxyAddon(t *testing.T) { } } +func TestApplyKubeProxyDaemonSetPatches(t *testing.T) { + daemonSetBytes, err := kubeadmutil.ParseTemplate(KubeProxyDaemonSet19, struct{ Image, ProxyConfigMap, ProxyConfigMapKey string }{ + Image: "foo", + ProxyConfigMap: "bar", + ProxyConfigMapKey: "baz", + }) + if err != nil { + t.Fatalf("unexpected ParseTemplate failure: %v", err) + } + + tmpDir := t.TempDir() + patchFile := filepath.Join(tmpDir, "kubeproxydaemonset+strategic.yaml") + patch := `spec: + template: + spec: + hostNetwork: false +` + if err := os.WriteFile(patchFile, []byte(patch), 0600); err != nil { + t.Fatalf("failed writing patch file: %v", err) + } + + patchedDaemonSetBytes, err := applyKubeProxyDaemonSetPatches(daemonSetBytes, tmpDir, os.Stdout) + if err != nil { + t.Fatalf("applyKubeProxyDaemonSetPatches returned error: %v", err) + } + + kubeproxyDaemonSet := &apps.DaemonSet{} + if err := runtime.DecodeInto(clientsetscheme.Codecs.UniversalDecoder(), patchedDaemonSetBytes, kubeproxyDaemonSet); err != nil { + t.Fatalf("unable to decode kube-proxy daemonset: %v", err) + } + + if kubeproxyDaemonSet.Spec.Template.Spec.HostNetwork { + t.Fatal("expected patched kube-proxy daemonset hostNetwork to be false") + } +} + func TestDaemonSetsHaveSystemNodeCriticalPriorityClassName(t *testing.T) { testCases := []struct { name string diff --git a/cmd/kubeadm/app/util/patches/patches.go b/cmd/kubeadm/app/util/patches/patches.go index 17115f0c42c..6ed76d1d020 100644 --- a/cmd/kubeadm/app/util/patches/patches.go +++ b/cmd/kubeadm/app/util/patches/patches.go @@ -82,6 +82,8 @@ const ( KubeletConfiguration = "kubeletconfiguration" // CoreDNSDeployment defines the corednsdeployment patch target. CoreDNSDeployment = "corednsdeployment" + // KubeProxyDaemonSet defines the kubeproxydaemonset patch target. + KubeProxyDaemonSet = "kubeproxydaemonset" ) var ( @@ -107,6 +109,7 @@ var ( kubeadmconstants.KubeScheduler, KubeletConfiguration, CoreDNSDeployment, + KubeProxyDaemonSet, } )