Merge pull request #124820 from SataQiu/feat-kubeadm-20240511
kubeadm: add support for patching a corednsdeployment target
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ` +
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user