mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
kubeadm: make "upgrade node" include URL scheme in socket paths
The CRI socket that kubeadm writes as an annotation on a particular Node object can include an endpoint that does not have an URL scheme. This is undesired as long term the kubelet can stop allowing endpoints without URL scheme. For control plane nodes "kubeadm upgrade apply" takes the locally defaulted / populated NodeRegistration and refreshes the CRI socket in PerformPostUpgradeTasks. But for secondary nodes "kubeadm upgrade node" does not. Adapt "upgrade node" to fetch the NodeRegistration for this node and fix the CRI socket missing URL scheme if needed in the Node annotation.
This commit is contained in:
parent
7594f0ef90
commit
207556e057
@ -34,4 +34,5 @@ type Data interface {
|
||||
Client() clientset.Interface
|
||||
IgnorePreflightErrors() sets.String
|
||||
PatchesDir() string
|
||||
KubeConfigPath() string
|
||||
}
|
||||
|
@ -20,15 +20,22 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
|
||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
|
||||
patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
|
||||
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
||||
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
|
||||
)
|
||||
|
||||
@ -87,6 +94,34 @@ func runKubeletConfigPhase() func(c workflow.RunData) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle a missing URL scheme in the Node CRI socket.
|
||||
// Older versions of kubeadm tolerate CRI sockets without URL schemes (/var/run/foo without unix://).
|
||||
// During "upgrade node" for worker nodes the cfg.NodeRegistration would be left empty.
|
||||
// This requires to call GetNodeRegistration on demand and fetch the node name and CRI socket.
|
||||
// If the NodeRegistration (nro) contains a socket without a URL scheme, update it.
|
||||
//
|
||||
// TODO: this workaround can be removed in 1.25 once all user node sockets have a URL scheme:
|
||||
// https://github.com/kubernetes/kubeadm/issues/2426
|
||||
var nro *kubeadmapi.NodeRegistrationOptions
|
||||
var missingURLScheme bool
|
||||
if !dryRun {
|
||||
if err := configutil.GetNodeRegistration(data.KubeConfigPath(), data.Client(), nro); err != nil {
|
||||
return errors.Wrap(err, "could not retrieve the node registration options for this node")
|
||||
}
|
||||
missingURLScheme = strings.HasPrefix(nro.CRISocket, kubeadmapiv1.DefaultContainerRuntimeURLScheme)
|
||||
}
|
||||
if missingURLScheme {
|
||||
if !dryRun {
|
||||
newSocket := kubeadmapiv1.DefaultContainerRuntimeURLScheme + "://" + nro.CRISocket
|
||||
klog.V(2).Infof("ensuring that Node %q has a CRI socket annotation with URL scheme %q", nro.Name, newSocket)
|
||||
if err := patchnodephase.AnnotateCRISocket(data.Client(), nro.Name, newSocket); err != nil {
|
||||
return errors.Wrapf(err, "error updating the CRI socket for Node %q", nro.Name)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("[dryrun] would update the node CRI socket path to include an URL scheme")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("[upgrade] The configuration for this node was successfully updated!")
|
||||
fmt.Println("[upgrade] Now you should go ahead and upgrade the kubelet package using your package manager.")
|
||||
return nil
|
||||
|
@ -61,6 +61,7 @@ type nodeData struct {
|
||||
client clientset.Interface
|
||||
patchesDir string
|
||||
ignorePreflightErrors sets.String
|
||||
kubeConfigPath string
|
||||
}
|
||||
|
||||
// newCmdNode returns the cobra command for `kubeadm upgrade node`
|
||||
@ -159,6 +160,7 @@ func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions) (*node
|
||||
isControlPlaneNode: isControlPlaneNode,
|
||||
patchesDir: options.patchesDir,
|
||||
ignorePreflightErrors: ignorePreflightErrorsSet,
|
||||
kubeConfigPath: options.kubeConfigPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -201,3 +203,8 @@ func (d *nodeData) PatchesDir() string {
|
||||
func (d *nodeData) IgnorePreflightErrors() sets.String {
|
||||
return d.ignorePreflightErrors
|
||||
}
|
||||
|
||||
// KubeconfigPath returns the path to the user kubeconfig file.
|
||||
func (d *nodeData) KubeConfigPath() string {
|
||||
return d.kubeConfigPath
|
||||
}
|
||||
|
@ -97,7 +97,8 @@ func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Inte
|
||||
// get nodes specific information as well
|
||||
if !newControlPlane {
|
||||
// gets the nodeRegistration for the current from the node object
|
||||
if err := getNodeRegistration(kubeconfigDir, client, &initcfg.NodeRegistration); err != nil {
|
||||
kubeconfigFile := filepath.Join(kubeconfigDir, constants.KubeletKubeConfigFileName)
|
||||
if err := GetNodeRegistration(kubeconfigFile, client, &initcfg.NodeRegistration); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get node registration")
|
||||
}
|
||||
// gets the APIEndpoint for the current node
|
||||
@ -117,10 +118,10 @@ func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Inte
|
||||
return initcfg, nil
|
||||
}
|
||||
|
||||
// getNodeRegistration returns the nodeRegistration for the current node
|
||||
func getNodeRegistration(kubeconfigDir string, client clientset.Interface, nodeRegistration *kubeadmapi.NodeRegistrationOptions) error {
|
||||
// GetNodeRegistration returns the nodeRegistration for the current node
|
||||
func GetNodeRegistration(kubeconfigFile string, client clientset.Interface, nodeRegistration *kubeadmapi.NodeRegistrationOptions) error {
|
||||
// gets the name of the current node
|
||||
nodeName, err := getNodeNameFromKubeletConfig(kubeconfigDir)
|
||||
nodeName, err := getNodeNameFromKubeletConfig(kubeconfigFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get node name from kubelet config")
|
||||
}
|
||||
@ -149,9 +150,8 @@ func getNodeRegistration(kubeconfigDir string, client clientset.Interface, nodeR
|
||||
// getNodeNameFromKubeletConfig gets the node name from a kubelet config file
|
||||
// TODO: in future we want to switch to a more canonical way for doing this e.g. by having this
|
||||
// information in the local kubelet config.yaml
|
||||
func getNodeNameFromKubeletConfig(kubeconfigDir string) (string, error) {
|
||||
func getNodeNameFromKubeletConfig(fileName string) (string, error) {
|
||||
// loads the kubelet.conf file
|
||||
fileName := filepath.Join(kubeconfigDir, constants.KubeletKubeConfigFileName)
|
||||
config, err := clientcmd.LoadFromFile(fileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -261,7 +261,7 @@ func TestGetNodeNameFromKubeletConfig(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
name, err := getNodeNameFromKubeletConfig(tmpdir)
|
||||
name, err := getNodeNameFromKubeletConfig(kubeconfigPath)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from getNodeRegistration: %v", err)
|
||||
return
|
||||
@ -338,7 +338,7 @@ func TestGetNodeRegistration(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := &kubeadmapi.InitConfiguration{}
|
||||
err = getNodeRegistration(tmpdir, client, &cfg.NodeRegistration)
|
||||
err = GetNodeRegistration(cfgPath, client, &cfg.NodeRegistration)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from getNodeRegistration: %v", err)
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user