kubeadm: add means to create Node objects via the API client

This commit is contained in:
Lubomir I. Ivanov 2019-09-30 23:50:17 +03:00
parent 02469a023f
commit 2dedfddf81
6 changed files with 46 additions and 18 deletions

View File

@ -121,7 +121,7 @@ func TestGetKubeConfigSpecs(t *testing.T) {
organizations: []string{kubeadmconstants.SystemPrivilegedGroup},
},
{
kubeConfigFile: kubeadmconstants.KubeletKubeConfigFileName,
kubeConfigFile: kubeadmconstants.KubeletBootstrapKubeConfigFileName,
clientName: fmt.Sprintf("%s%s", kubeadmconstants.NodesUserPrefix, cfg.NodeRegistration.Name),
organizations: []string{kubeadmconstants.NodesGroup},
},
@ -557,8 +557,8 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
},
"some files don't exist": {
filesToWrite: map[string]*clientcmdapi.Config{
kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletKubeConfigFileName: config,
kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletBootstrapKubeConfigFileName: config,
},
initConfig: initConfig,
expectedError: true,
@ -566,7 +566,7 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
"some files have invalid CA": {
filesToWrite: map[string]*clientcmdapi.Config{
kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletKubeConfigFileName: config,
kubeadmconstants.KubeletBootstrapKubeConfigFileName: config,
kubeadmconstants.ControllerManagerKubeConfigFileName: configWithAnotherClusterCa,
kubeadmconstants.SchedulerKubeConfigFileName: config,
},
@ -576,7 +576,7 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
"some files have invalid Server Url": {
filesToWrite: map[string]*clientcmdapi.Config{
kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletKubeConfigFileName: config,
kubeadmconstants.KubeletBootstrapKubeConfigFileName: config,
kubeadmconstants.ControllerManagerKubeConfigFileName: config,
kubeadmconstants.SchedulerKubeConfigFileName: configWithAnotherServerURL,
},
@ -586,7 +586,7 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
"all files are valid": {
filesToWrite: map[string]*clientcmdapi.Config{
kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletKubeConfigFileName: config,
kubeadmconstants.KubeletBootstrapKubeConfigFileName: config,
kubeadmconstants.ControllerManagerKubeConfigFileName: config,
kubeadmconstants.SchedulerKubeConfigFileName: config,
},

View File

@ -29,6 +29,7 @@ go_library(
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)

View File

@ -19,14 +19,20 @@ package markcontrolplane
import (
"fmt"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
)
// MarkControlPlane taints the control-plane and sets the control-plane label
func MarkControlPlane(client clientset.Interface, controlPlaneName string, taints []v1.Taint) error {
klog.V(1).Infof("[patchnode] Creating the Node API object %q if missing", controlPlaneName)
// See the comments for this function
if err := apiclient.EnsureNodeObject(client, controlPlaneName); err != nil {
return err
}
fmt.Printf("[mark-control-plane] Marking the node %s as control-plane by adding the label \"%s=''\"\n", controlPlaneName, constants.LabelNodeRoleMaster)

View File

@ -24,7 +24,7 @@ import (
"net/http/httptest"
"testing"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
@ -138,14 +138,9 @@ func TestMarkControlPlane(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
if req.URL.Path != "/api/v1/nodes/"+hostname {
t.Errorf("MarkControlPlane(%s): request for unexpected HTTP resource: %v", tc.name, req.URL.Path)
http.Error(w, "", http.StatusNotFound)
return
}
switch req.Method {
case "GET":
case "POST":
case "PATCH":
patchRequest = toString(req.Body)
default:

View File

@ -17,7 +17,7 @@ limitations under the License.
package patchnode
import (
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
@ -26,9 +26,12 @@ import (
// AnnotateCRISocket annotates the node with the given crisocket
func AnnotateCRISocket(client clientset.Interface, nodeName string, criSocket string) error {
klog.V(1).Infof("[patchnode] Uploading the CRI Socket information %q to the Node API object %q as an annotation\n", criSocket, nodeName)
klog.V(1).Infof("[patchnode] Creating the Node API object %q if missing", nodeName)
// See the comments for this function
if err := apiclient.EnsureNodeObject(client, nodeName); err != nil {
return err
}
klog.V(1).Infof("[patchnode] Uploading the CRI Socket information %q to the Node API object %q as an annotation", criSocket, nodeName)
return apiclient.PatchNode(client, nodeName, func(n *v1.Node) {
annotateNodeWithCRISocket(n, criSocket)
})

View File

@ -285,6 +285,29 @@ func PatchNodeOnce(client clientset.Interface, nodeName string, patchFn func(*v1
}
}
// EnsureNodeObject creates a Node with the given name if the Node doesn't exist.
// Adding a placeholder v1.LabelHostname label makes the object suitable for patching using PatchNodeOnce.
//
// Currently this function is used to make sure a Node object exists before patching it
// during the "kubeadm init" phases. The creation of the Node object is delayed due to TLS boostrap
// and instead of waiting for the object, we create it as a placeholder and patch it right away.
// Later the same Node object is populated with dynamic values by the kubelet.
func EnsureNodeObject(client clientset.Interface, nodeName string) error {
node := &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: nodeName,
Labels: map[string]string{v1.LabelHostname: ""},
},
}
if _, err := client.CoreV1().Nodes().Create(node); err != nil {
if apierrors.IsAlreadyExists(err) {
return nil
}
return errors.Wrapf(err, "error creating Node %q", nodeName)
}
return nil
}
// PatchNode tries to patch a node using patchFn for the actual mutating logic.
// Retries are provided by the wait package.
func PatchNode(client clientset.Interface, nodeName string, patchFn func(*v1.Node)) error {