kubeadm: add support for patching a corednsdeployment target

This commit is contained in:
SataQiu 2024-05-11 18:21:20 +08:00
parent 925cb2be30
commit 1329935739
10 changed files with 62 additions and 22 deletions

View File

@ -468,7 +468,7 @@ type HostPathMount struct {
type Patches struct { type Patches struct {
// Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". // 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 // 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. // "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". // 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 // "suffix" is an optional string that can be used to determine which patches are applied

View File

@ -438,7 +438,7 @@ type HostPathMount struct {
type Patches struct { type Patches struct {
// Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". // 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 // 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. // "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". // 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 // "suffix" is an optional string that can be used to determine which patches are applied

View File

@ -487,7 +487,7 @@ type HostPathMount struct {
type Patches struct { type Patches struct {
// Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension". // 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 // 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. // "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". // 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 // "suffix" is an optional string that can be used to determine which patches are applied

View File

@ -99,7 +99,7 @@ func AddPatchesFlag(fs *pflag.FlagSet, patchesDir *string) {
const usage = `Path to a directory that contains files named ` + const usage = `Path to a directory that contains files named ` +
`"target[suffix][+patchtype].extension". For example, ` + `"target[suffix][+patchtype].extension". For example, ` +
`"kube-apiserver0+merge.yaml" or just "etcd.json". ` + `"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 ` + `"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 ` + `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 ` + `"json" or "yaml". "suffix" is an optional string that can be used to determine ` +

View File

@ -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) data, ok := c.(InitData)
if !ok { 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() cfg := data.Cfg()
var client clientset.Interface var client clientset.Interface
@ -95,26 +95,27 @@ func getInitData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.I
if !printManifest { if !printManifest {
client, err = data.Client() client, err = data.Client()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, "", nil, err
} }
} }
out := data.OutputWriter() 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 // runCoreDNSAddon installs CoreDNS addon to a Kubernetes cluster
func runCoreDNSAddon(c workflow.RunData) error { func runCoreDNSAddon(c workflow.RunData) error {
cfg, client, out, err := getInitData(c) cfg, client, patchesDir, out, err := getInitData(c)
if err != nil { if err != nil {
return err 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 // runKubeProxyAddon installs KubeProxy addon to a Kubernetes cluster
func runKubeProxyAddon(c workflow.RunData) error { func runKubeProxyAddon(c workflow.RunData) error {
cfg, client, out, err := getInitData(c) cfg, client, _, out, err := getInitData(c)
if err != nil { if err != nil {
return err return err
} }

View File

@ -78,7 +78,7 @@ func runControlPlane() func(c workflow.RunData) error {
return errors.Wrap(err, "couldn't complete the static pod upgrade") 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") return errors.Wrap(err, "failed to perform addons upgrade")
} }

View File

@ -35,6 +35,7 @@ import (
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
clientsetscheme "k8s.io/client-go/kubernetes/scheme" clientsetscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"sigs.k8s.io/yaml"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
@ -42,6 +43,7 @@ import (
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
"k8s.io/kubernetes/cmd/kubeadm/app/util/image" "k8s.io/kubernetes/cmd/kubeadm/app/util/image"
"k8s.io/kubernetes/cmd/kubeadm/app/util/patches"
) )
const ( const (
@ -85,7 +87,7 @@ func deployedDNSReplicas(client clientset.Interface, replicas int32) (*int32, er
} }
// EnsureDNSAddon creates the CoreDNS addon // 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 replicas *int32
var err error var err error
if !printManifest { if !printManifest {
@ -97,10 +99,10 @@ func EnsureDNSAddon(cfg *kubeadmapi.ClusterConfiguration, client clientset.Inter
var defaultReplicas int32 = coreDNSReplicas var defaultReplicas int32 = coreDNSReplicas
replicas = &defaultReplicas 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 // Get the YAML manifest
coreDNSDeploymentBytes, err := kubeadmutil.ParseTemplate(CoreDNSDeployment, struct { coreDNSDeploymentBytes, err := kubeadmutil.ParseTemplate(CoreDNSDeployment, struct {
DeploymentName, Image, ControlPlaneTaintKey string 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") 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 // Get the config file for CoreDNS
coreDNSConfigMapBytes, err := kubeadmutil.ParseTemplate(CoreDNSConfigMap, struct{ DNSDomain, UpstreamNameserver, StubDomain string }{ coreDNSConfigMapBytes, err := kubeadmutil.ParseTemplate(CoreDNSConfigMap, struct{ DNSDomain, UpstreamNameserver, StubDomain string }{
DNSDomain: cfg.Networking.DNSDomain, DNSDomain: cfg.Networking.DNSDomain,
@ -377,3 +387,27 @@ func setCorefile(client clientset.Interface, coreDNSCorefileName string) error {
} }
return nil 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
}

View File

@ -865,7 +865,7 @@ metadata:
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
out := &bytes.Buffer{} out := &bytes.Buffer{}
var replicas int32 = 3 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) t.Errorf("coreDNSAddon() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
@ -1148,7 +1148,7 @@ metadata:
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
out := &bytes.Buffer{} 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) t.Errorf("EnsureDNSAddon() error = %v, wantErr %v", err, tt.wantErr)
return return
} }

View File

@ -105,7 +105,7 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitCon
errs = append(errs, err) 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) 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. // 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) unupgradedControlPlanes, err := unupgradedControlPlaneInstances(client, cfg.NodeRegistration.Name)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to determine whether all the control plane instances have been upgraded") 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) metav1.NamespaceSystem)
} else { } else {
// Upgrade CoreDNS // 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) errs = append(errs, err)
} }
} }

View File

@ -75,8 +75,12 @@ func (ps *patchSet) String() string {
) )
} }
// KubeletConfiguration defines the kubeletconfiguration patch target. const (
const KubeletConfiguration = "kubeletconfiguration" // KubeletConfiguration defines the kubeletconfiguration patch target.
KubeletConfiguration = "kubeletconfiguration"
// CoreDNSDeployment defines the corednsdeployment patch target.
CoreDNSDeployment = "corednsdeployment"
)
var ( var (
pathLock = &sync.RWMutex{} pathLock = &sync.RWMutex{}
@ -100,6 +104,7 @@ var (
kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeControllerManager,
kubeadmconstants.KubeScheduler, kubeadmconstants.KubeScheduler,
KubeletConfiguration, KubeletConfiguration,
CoreDNSDeployment,
} }
) )