Merge pull request #134319 from neolit123/1.35-refactor-fetch-init-config-flags

kubeadm: rework the FetchInitConfigurationFromCluster node flags
This commit is contained in:
Kubernetes Prow Robot
2025-09-30 09:14:32 -07:00
committed by GitHub
11 changed files with 82 additions and 41 deletions

View File

@@ -49,6 +49,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/errors"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
)
var (
@@ -346,7 +347,10 @@ func getInternalCfg(cfgPath string, client kubernetes.Interface, cfg kubeadmapiv
// In case the user is not providing a custom config, try to get current config from the cluster.
// NB. this operation should not block, because we want to allow certificate renewal also in case of not-working clusters
if cfgPath == "" && client != nil {
internalcfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, logPrefix, false, false)
getNodeRegistration := true
getAPIEndpoint := staticpodutil.IsControlPlaneNode()
getComponentConfigs := true
internalcfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, logPrefix, getNodeRegistration, getAPIEndpoint, getComponentConfigs)
if err == nil {
printer.Println() // add empty line to separate the FetchInitConfigurationFromCluster output from the command output
// certificate renewal or expiration checking doesn't depend on a running cluster, which means the CertificatesDir

View File

@@ -709,7 +709,10 @@ func fetchInitConfigurationFromJoinConfiguration(cfg *kubeadmapi.JoinConfigurati
// fetchInitConfiguration reads the cluster configuration from the kubeadm-admin configMap
func fetchInitConfiguration(client clientset.Interface) (*kubeadmapi.InitConfiguration, error) {
initConfiguration, err := configutil.FetchInitConfigurationFromCluster(client, nil, "preflight", true, false)
getNodeRegistration := false
getAPIEndpoint := false
getComponentConfigs := true
initConfiguration, err := configutil.FetchInitConfigurationFromCluster(client, nil, "preflight", getNodeRegistration, getAPIEndpoint, getComponentConfigs)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
}

View File

@@ -44,6 +44,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/errors"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
)
var (
@@ -132,7 +133,10 @@ func newResetData(cmd *cobra.Command, opts *resetOptions, in io.Reader, out io.W
if err == nil {
klog.V(1).Infof("[reset] Loaded client set from kubeconfig file: %s", opts.kubeconfigPath)
initCfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "reset", false, false)
getNodeRegistration := true
getAPIEndpoint := staticpodutil.IsControlPlaneNode()
getComponentConfigs := true
initCfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "reset", getNodeRegistration, getAPIEndpoint, getComponentConfigs)
if err != nil {
klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap from cluster: %v", err)
}

View File

@@ -238,7 +238,10 @@ func newApplyData(cmd *cobra.Command, args []string, applyFlags *applyFlags) (*a
// Fetches the cluster configuration.
klog.V(1).Infoln("[upgrade] retrieving configuration from cluster")
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", false, false)
getNodeRegistration := true
isControlPlaneNode := true
getComponentConfigs := true
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", getNodeRegistration, isControlPlaneNode, getComponentConfigs)
if err != nil {
if apierrors.IsNotFound(err) {
_, _ = printer.Printf("[upgrade] In order to upgrade, a ConfigMap called %q in the %q namespace must exist.\n", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)

View File

@@ -45,6 +45,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/errors"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
)
// enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure
@@ -92,7 +93,10 @@ func enforceRequirements(flagSet *pflag.FlagSet, flags *applyPlanFlags, args []s
return nil, nil, nil, nil, err
}
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, "upgrade/config", false, false)
getNodeRegistration := true
getAPIEndpoint := staticpodutil.IsControlPlaneNode()
getComponentConfigs := true
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, "upgrade/config", getNodeRegistration, getAPIEndpoint, getComponentConfigs)
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, "[upgrade/init config] FATAL")
}

View File

@@ -41,6 +41,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/errors"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
)
type diffFlags struct {
@@ -106,7 +107,7 @@ func validateManifestsPath(manifests ...string) (err error) {
}
// FetchInitConfigurationFunc defines the signature of the function which will fetch InitConfiguration from cluster.
type FetchInitConfigurationFunc func(client clientset.Interface, printer output.Printer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error)
type FetchInitConfigurationFunc func(client clientset.Interface, printer output.Printer, logPrefix string, getNodeRegistration, getAPIEndpoint, getComponentConfigs bool) (*kubeadmapi.InitConfiguration, error)
func runDiff(fs *pflag.FlagSet, flags *diffFlags, args []string, fetchInitConfigurationFromCluster FetchInitConfigurationFunc) error {
externalCfg := &v1beta4.UpgradeConfiguration{}
@@ -119,7 +120,10 @@ func runDiff(fs *pflag.FlagSet, flags *diffFlags, args []string, fetchInitConfig
if err != nil {
return errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
}
initCfg, err := fetchInitConfigurationFromCluster(client, &output.TextPrinter{}, "upgrade/diff", false, true)
getNodeRegistration := true
getAPIEndpoint := staticpodutil.IsControlPlaneNode()
getComponentConfigs := false
initCfg, err := fetchInitConfigurationFromCluster(client, &output.TextPrinter{}, "upgrade/diff", getNodeRegistration, getAPIEndpoint, getComponentConfigs)
if err != nil {
return err
}

View File

@@ -44,7 +44,7 @@ func createTestRunDiffFile(contents []byte) (string, error) {
return file.Name(), nil
}
func fakeFetchInitConfig(client clientset.Interface, printer output.Printer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
func fakeFetchInitConfig(client clientset.Interface, printer output.Printer, logPrefix string, getNodeRegistration, getAPIEndpoint, getComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
return &kubeadmapi.InitConfiguration{
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
KubernetesVersion: "v1.0.1",

View File

@@ -19,7 +19,6 @@ package upgrade
import (
"fmt"
"io"
"os"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
@@ -40,6 +39,7 @@ import (
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
"k8s.io/kubernetes/cmd/kubeadm/app/util/errors"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
)
// nodeOptions defines all the options exposed via flags by kubeadm upgrade node.
@@ -165,13 +165,8 @@ func addUpgradeNodeFlags(flagSet *flag.FlagSet, nodeOptions *nodeOptions) {
// This func takes care of validating nodeOptions passed to the command, and then it converts
// options into the internal InitConfiguration type that is used as input all the phases in the kubeadm upgrade node workflow
func newNodeData(cmd *cobra.Command, nodeOptions *nodeOptions, out io.Writer) (*nodeData, error) {
// Checks if a node is a control-plane node by looking up the kube-apiserver manifest file
isControlPlaneNode := true
filepath := constants.GetStaticPodFilepath(constants.KubeAPIServer, constants.GetStaticPodDirectory())
if _, err := os.Stat(filepath); os.IsNotExist(err) {
klog.V(1).Infof("assuming this is not a control plane node because %q is missing", filepath)
isControlPlaneNode = false
}
isControlPlaneNode := staticpodutil.IsControlPlaneNode()
if len(nodeOptions.kubeConfigPath) == 0 {
// Update the kubeconfig path depending on whether this is a control plane node or not.
nodeOptions.kubeConfigPath = constants.GetKubeletKubeConfigPath()
@@ -207,9 +202,10 @@ func newNodeData(cmd *cobra.Command, nodeOptions *nodeOptions, out io.Writer) (*
}
// Fetches the cluster configuration
// NB in case of control-plane node, we are reading all the info for the node; in case of NOT control-plane node
// (worker node), we are not reading local API address and the CRI socket from the node object
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", !isControlPlaneNode, false)
getNodeRegistration := true
getAPIEndpoint := isControlPlaneNode
getComponentConfigs := true
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", getNodeRegistration, getAPIEndpoint, getComponentConfigs)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
}

View File

@@ -51,7 +51,7 @@ import (
)
// FetchInitConfigurationFromCluster fetches configuration from a ConfigMap in the cluster
func FetchInitConfigurationFromCluster(client clientset.Interface, printer output.Printer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
func FetchInitConfigurationFromCluster(client clientset.Interface, printer output.Printer, logPrefix string, getNodeRegistration, getAPIEndpoint, getComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
if printer == nil {
printer = &output.TextPrinter{}
}
@@ -60,7 +60,7 @@ func FetchInitConfigurationFromCluster(client clientset.Interface, printer outpu
_, _ = printer.Printf("[%s] Use 'kubeadm init phase upload-config kubeadm --config your-config-file' to re-upload it.\n", logPrefix)
// Fetch the actual config from cluster
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane, skipComponentConfigs)
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, getNodeRegistration, getAPIEndpoint, getComponentConfigs)
if err != nil {
return nil, err
}
@@ -75,7 +75,7 @@ func FetchInitConfigurationFromCluster(client clientset.Interface, printer outpu
}
// getInitConfigurationFromCluster is separate only for testing purposes, don't call it directly, use FetchInitConfigurationFromCluster instead
func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Interface, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Interface, getNodeRegistration, getAPIEndpoint, getComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
// Also, the config map really should be KubeadmConfigConfigMap...
configMap, err := apiclient.GetConfigMapWithShortRetry(client, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap)
if err != nil {
@@ -105,26 +105,28 @@ func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Inte
return nil, errors.Wrap(err, "failed to decode cluster configuration data")
}
if !skipComponentConfigs {
if getComponentConfigs {
// get the component configs from the corresponding config maps
if err := componentconfigs.FetchFromCluster(&initcfg.ClusterConfiguration, client); err != nil {
return nil, errors.Wrap(err, "failed to get component configs")
}
}
// if this isn't a new controlplane instance (e.g. in case of kubeadm upgrades)
// get nodes specific information as well
if !newControlPlane {
if getNodeRegistration {
// gets the nodeRegistration for the current from the node object
kubeconfigFile := filepath.Join(kubeconfigDir, constants.KubeletKubeConfigFileName)
if err := GetNodeRegistration(kubeconfigFile, client, &initcfg.NodeRegistration, &initcfg.ClusterConfiguration); err != nil {
return nil, errors.Wrap(err, "failed to get node registration")
}
// gets the APIEndpoint for the current node
if err := getAPIEndpoint(client, initcfg.NodeRegistration.Name, &initcfg.LocalAPIEndpoint); err != nil {
}
if getAPIEndpoint {
// gets the APIEndpoint for the current control plane node
if err := GetAPIEndpoint(client, initcfg.NodeRegistration.Name, &initcfg.LocalAPIEndpoint); err != nil {
return nil, errors.Wrap(err, "failed to getAPIEndpoint")
}
}
return initcfg, nil
}
@@ -257,7 +259,8 @@ func getNodeNameFromSSR(client clientset.Interface) (string, error) {
return strings.TrimPrefix(user, constants.NodesUserPrefix), nil
}
func getAPIEndpoint(client clientset.Interface, nodeName string, apiEndpoint *kubeadmapi.APIEndpoint) error {
// GetAPIEndpoint gets the API endpoint for a given node.
func GetAPIEndpoint(client clientset.Interface, nodeName string, apiEndpoint *kubeadmapi.APIEndpoint) error {
return getAPIEndpointWithRetry(client, nodeName, apiEndpoint,
constants.KubernetesAPICallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration)
}

View File

@@ -491,13 +491,14 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
defer os.RemoveAll(tmpdir)
var tests = []struct {
name string
fileContents []byte
node *v1.Node
staticPods []testresources.FakeStaticPod
configMaps []testresources.FakeConfigMap
newControlPlane bool
expectedError bool
name string
fileContents []byte
node *v1.Node
staticPods []testresources.FakeStaticPod
configMaps []testresources.FakeConfigMap
getNodeRegistration bool
getAPIEndpoint bool
expectedError bool
}{
{
name: "invalid - No kubeadm-config ConfigMap",
@@ -556,6 +557,8 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
Taints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
},
},
getNodeRegistration: true,
getAPIEndpoint: true,
},
{
name: "valid v1beta3 - new control plane == true", // InitConfiguration composed with data from different places, without node specific information
@@ -588,7 +591,8 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
},
},
},
newControlPlane: true,
getNodeRegistration: false,
getAPIEndpoint: false,
},
}
@@ -629,7 +633,8 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
}
}
cfg, err := getInitConfigurationFromCluster(tmpdir, client, rt.newControlPlane, false)
getComponentConfigs := true
cfg, err := getInitConfigurationFromCluster(tmpdir, client, rt.getNodeRegistration, rt.getAPIEndpoint, getComponentConfigs)
if rt.expectedError != (err != nil) {
t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
return
@@ -649,13 +654,13 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
if cfg.NodeRegistration.ImagePullPolicy != kubeadmapiv1.DefaultImagePullPolicy {
t.Errorf("invalid cfg.NodeRegistration.ImagePullPolicy %v", cfg.NodeRegistration.ImagePullPolicy)
}
if !rt.newControlPlane && (cfg.LocalAPIEndpoint.AdvertiseAddress != "1.2.3.4" || cfg.LocalAPIEndpoint.BindPort != 1234) {
if rt.getNodeRegistration && rt.getAPIEndpoint && (cfg.LocalAPIEndpoint.AdvertiseAddress != "1.2.3.4" || cfg.LocalAPIEndpoint.BindPort != 1234) {
t.Errorf("invalid cfg.LocalAPIEndpoint: %v", cfg.LocalAPIEndpoint)
}
if !rt.newControlPlane && (cfg.NodeRegistration.Name != nodeName || cfg.NodeRegistration.CRISocket != "myCRIsocket" || len(cfg.NodeRegistration.Taints) != 1) {
if rt.getNodeRegistration && (cfg.NodeRegistration.Name != nodeName || cfg.NodeRegistration.CRISocket != "myCRIsocket" || len(cfg.NodeRegistration.Taints) != 1) {
t.Errorf("invalid cfg.NodeRegistration: %v", cfg.NodeRegistration)
}
if rt.newControlPlane && len(cfg.NodeRegistration.CRISocket) > 0 {
if !rt.getNodeRegistration && len(cfg.NodeRegistration.CRISocket) > 0 {
t.Errorf("invalid cfg.NodeRegistration.CRISocket: expected empty CRISocket, but got %v", cfg.NodeRegistration.CRISocket)
}
if _, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup]; !ok {

View File

@@ -419,3 +419,18 @@ func DeepHashObject(hasher hash.Hash, objectToWrite interface{}) {
hasher.Reset()
fmt.Fprintf(hasher, "%v", dump.ForHash(objectToWrite))
}
// IsControlPlaneNode returns true if the kube-apiserver static pod manifest is present
// on the host.
func IsControlPlaneNode() bool {
isControlPlaneNode := true
filepath := kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.KubeAPIServer,
kubeadmconstants.GetStaticPodDirectory())
if _, err := os.Stat(filepath); os.IsNotExist(err) {
isControlPlaneNode = false
}
return isControlPlaneNode
}