diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 82d89bf086a..b24fbf63ba4 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -468,7 +468,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". + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". // "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 4e9096a4d0d..e5ca733d070 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go @@ -438,7 +438,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". + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". // "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 07a084389ff..bdc2d14d229 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/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". + // "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". // "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 23220a5d23b..475f4bf2420 100644 --- a/cmd/kubeadm/app/cmd/options/generic.go +++ b/cmd/kubeadm/app/cmd/options/generic.go @@ -99,7 +99,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". ` + + `"target" can be one of "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration", "corednsdeployment". ` + `"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 e8438080ebc..cd54fe702ad 100644 --- a/cmd/kubeadm/app/cmd/phases/init/addons.go +++ b/cmd/kubeadm/app/cmd/phases/init/addons.go @@ -84,10 +84,10 @@ func NewAddonPhase() workflow.Phase { } } -func getInitData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.Interface, io.Writer, error) { +func getInitData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.Interface, string, io.Writer, error) { data, ok := c.(InitData) if !ok { - return nil, nil, nil, errors.New("addon phase invoked with an invalid data struct") + return nil, nil, "", nil, errors.New("addon phase invoked with an invalid data struct") } cfg := data.Cfg() var client clientset.Interface @@ -95,26 +95,27 @@ func getInitData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.I if !printManifest { client, err = data.Client() if err != nil { - return nil, nil, nil, err + return nil, nil, "", nil, err } } out := data.OutputWriter() - return cfg, client, out, err + patchesDir := data.PatchesDir() + return cfg, client, patchesDir, out, err } // runCoreDNSAddon installs CoreDNS addon to a Kubernetes cluster func runCoreDNSAddon(c workflow.RunData) error { - cfg, client, out, err := getInitData(c) + cfg, client, patchesDir, out, err := getInitData(c) if err != nil { return err } - return dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, out, printManifest) + return dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, printManifest) } // runKubeProxyAddon installs KubeProxy addon to a Kubernetes cluster func runKubeProxyAddon(c workflow.RunData) error { - cfg, client, out, err := getInitData(c) + cfg, client, _, out, err := getInitData(c) if err != nil { return err } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/node/controlplane.go b/cmd/kubeadm/app/cmd/phases/upgrade/node/controlplane.go index 60480f353e8..cbdca3ec1c6 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/node/controlplane.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/node/controlplane.go @@ -78,7 +78,7 @@ func runControlPlane() func(c workflow.RunData) error { return errors.Wrap(err, "couldn't complete the static pod upgrade") } - if err := upgrade.PerformAddonsUpgrade(client, cfg, data.OutputWriter()); err != nil { + if err := upgrade.PerformAddonsUpgrade(client, cfg, data.PatchesDir(), data.OutputWriter()); err != nil { return errors.Wrap(err, "failed to perform addons upgrade") } diff --git a/cmd/kubeadm/app/phases/addons/dns/dns.go b/cmd/kubeadm/app/phases/addons/dns/dns.go index fc59ff6565e..f41cf51002b 100644 --- a/cmd/kubeadm/app/phases/addons/dns/dns.go +++ b/cmd/kubeadm/app/phases/addons/dns/dns.go @@ -35,6 +35,7 @@ import ( clientset "k8s.io/client-go/kubernetes" clientsetscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/klog/v2" + "sigs.k8s.io/yaml" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -42,6 +43,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/image" + "k8s.io/kubernetes/cmd/kubeadm/app/util/patches" ) const ( @@ -85,7 +87,7 @@ func deployedDNSReplicas(client clientset.Interface, replicas int32) (*int32, er } // EnsureDNSAddon creates the CoreDNS addon -func EnsureDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, out io.Writer, printManifest bool) error { +func EnsureDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, patchesDir string, out io.Writer, printManifest bool) error { var replicas *int32 var err error if !printManifest { @@ -97,10 +99,10 @@ func EnsureDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Inter var defaultReplicas int32 = coreDNSReplicas replicas = &defaultReplicas } - return coreDNSAddon(cfg, client, replicas, out, printManifest) + return coreDNSAddon(cfg, client, replicas, patchesDir, out, printManifest) } -func coreDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, replicas *int32, out io.Writer, printManifest bool) error { +func coreDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, replicas *int32, patchesDir string, out io.Writer, printManifest bool) error { // Get the YAML manifest coreDNSDeploymentBytes, err := kubeadmutil.ParseTemplate(CoreDNSDeployment, struct { DeploymentName, Image, ControlPlaneTaintKey string @@ -115,6 +117,14 @@ func coreDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interfa return errors.Wrap(err, "error when parsing CoreDNS deployment template") } + // Apply patches to the CoreDNS Deployment + if len(patchesDir) != 0 { + coreDNSDeploymentBytes, err = applyCoreDNSDeploymentPatches(coreDNSDeploymentBytes, patchesDir, out) + if err != nil { + return errors.Wrap(err, "could not apply patches to the CoreDNS Deployment") + } + } + // Get the config file for CoreDNS coreDNSConfigMapBytes, err := kubeadmutil.ParseTemplate(CoreDNSConfigMap, struct{ DNSDomain, UpstreamNameserver, StubDomain string }{ DNSDomain: cfg.Networking.DNSDomain, @@ -377,3 +387,27 @@ func setCorefile(client clientset.Interface, coreDNSCorefileName string) error { } return nil } + +// applyCoreDNSDeploymentPatches reads patches from a directory and applies them over the input coreDNSDeploymentBytes +func applyCoreDNSDeploymentPatches(coreDNSDeploymentBytes []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.CoreDNSDeployment, + StrategicMergePatchObject: apps.Deployment{}, + Data: coreDNSDeploymentBytes, + } + if err := patchManager.ApplyPatchesToTarget(patchTarget); err != nil { + return nil, err + } + + coreDNSDeploymentBytes, err = yaml.JSONToYAML(patchTarget.Data) + if err != nil { + return nil, err + } + + return coreDNSDeploymentBytes, nil +} diff --git a/cmd/kubeadm/app/phases/addons/dns/dns_test.go b/cmd/kubeadm/app/phases/addons/dns/dns_test.go index 727916e7eca..cb61e5fae51 100644 --- a/cmd/kubeadm/app/phases/addons/dns/dns_test.go +++ b/cmd/kubeadm/app/phases/addons/dns/dns_test.go @@ -865,7 +865,7 @@ metadata: t.Run(tt.name, func(t *testing.T) { out := &bytes.Buffer{} var replicas int32 = 3 - if err := coreDNSAddon(tt.args.cfg, tt.args.client, &replicas, out, tt.args.printManifest); (err != nil) != tt.wantErr { + if err := coreDNSAddon(tt.args.cfg, tt.args.client, &replicas, "", out, tt.args.printManifest); (err != nil) != tt.wantErr { t.Errorf("coreDNSAddon() error = %v, wantErr %v", err, tt.wantErr) return } @@ -1148,7 +1148,7 @@ metadata: for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { out := &bytes.Buffer{} - if err := EnsureDNSAddon(tt.args.cfg, tt.args.client, out, tt.args.printManifest); (err != nil) != tt.wantErr { + if err := EnsureDNSAddon(tt.args.cfg, tt.args.client, "", out, tt.args.printManifest); (err != nil) != tt.wantErr { t.Errorf("EnsureDNSAddon() error = %v, wantErr %v", err, tt.wantErr) return } diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade.go b/cmd/kubeadm/app/phases/upgrade/postupgrade.go index 0bc227c23a6..02e7bcca618 100644 --- a/cmd/kubeadm/app/phases/upgrade/postupgrade.go +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade.go @@ -105,7 +105,7 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitCon errs = append(errs, err) } - if err := PerformAddonsUpgrade(client, cfg, out); err != nil { + if err := PerformAddonsUpgrade(client, cfg, patchesDir, out); err != nil { errs = append(errs, err) } @@ -113,7 +113,7 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitCon } // PerformAddonsUpgrade performs the upgrade of the coredns and kube-proxy addons. -func PerformAddonsUpgrade(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, out io.Writer) error { +func PerformAddonsUpgrade(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, patchesDir string, out io.Writer) error { unupgradedControlPlanes, err := unupgradedControlPlaneInstances(client, cfg.NodeRegistration.Name) if err != nil { return errors.Wrapf(err, "failed to determine whether all the control plane instances have been upgraded") @@ -147,7 +147,7 @@ func PerformAddonsUpgrade(client clientset.Interface, cfg *kubeadmapi.InitConfig metav1.NamespaceSystem) } else { // Upgrade CoreDNS - if err := dns.EnsureDNSAddon(&cfg.ClusterConfiguration, client, out, false); err != nil { + if err := dns.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, false); err != nil { errs = append(errs, err) } } diff --git a/cmd/kubeadm/app/util/patches/patches.go b/cmd/kubeadm/app/util/patches/patches.go index e28656457e2..665c47f5889 100644 --- a/cmd/kubeadm/app/util/patches/patches.go +++ b/cmd/kubeadm/app/util/patches/patches.go @@ -75,8 +75,12 @@ func (ps *patchSet) String() string { ) } -// KubeletConfiguration defines the kubeletconfiguration patch target. -const KubeletConfiguration = "kubeletconfiguration" +const ( + // KubeletConfiguration defines the kubeletconfiguration patch target. + KubeletConfiguration = "kubeletconfiguration" + // CoreDNSDeployment defines the corednsdeployment patch target. + CoreDNSDeployment = "corednsdeployment" +) var ( pathLock = &sync.RWMutex{} @@ -100,6 +104,7 @@ var ( kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeScheduler, KubeletConfiguration, + CoreDNSDeployment, } )