Skip to content

Commit 646b0bc

Browse files
authored
Merge pull request kubernetes#128354 from SataQiu/join-control-plane-e2e
kubeadm: support joining control plane nodes in dryrun mode without a real initialized control plane
2 parents 9ec52fc + dc48aed commit 646b0bc

File tree

5 files changed

+81
-7
lines changed

5 files changed

+81
-7
lines changed

cmd/kubeadm/app/cmd/join.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ func (j *joinData) Client() (clientset.Interface, error) {
607607
WithWriter(os.Stdout).
608608
AppendReactor(dryRun.GetClusterInfoReactor()).
609609
AppendReactor(dryRun.GetKubeadmConfigReactor()).
610+
AppendReactor(dryRun.GetKubeadmCertsReactor()).
610611
AppendReactor(dryRun.GetKubeProxyConfigReactor()).
611612
AppendReactor(dryRun.GetKubeletConfigReactor())
612613

cmd/kubeadm/app/cmd/phases/join/checketcd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ func runCheckEtcdPhase(c workflow.RunData) error {
5656
return nil
5757
}
5858

59+
if data.DryRun() {
60+
fmt.Println("[dryrun] Would check that the etcd cluster is healthy")
61+
return nil
62+
}
63+
5964
fmt.Println("[check-etcd] Checking that the etcd cluster is healthy")
6065

6166
// Checks that the etcd cluster is healthy

cmd/kubeadm/app/cmd/phases/join/controlplaneprepare.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ func runControlPlanePrepareKubeconfigPhaseLocal(c workflow.RunData) error {
292292
fmt.Println("[kubeconfig] Generating kubeconfig files")
293293
fmt.Printf("[kubeconfig] Using kubeconfig folder %q\n", data.KubeConfigDir())
294294

295+
// If we're dry-running, load certs from tmp dir, and defer to restore to the path originally specified by the user
296+
certsDir := cfg.CertificatesDir
297+
cfg.CertificatesDir = data.CertificateWriteDir()
298+
defer func() { cfg.CertificatesDir = certsDir }()
299+
295300
// Generate kubeconfig files for controller manager, scheduler and for the admin/kubeadm itself
296301
// NB. The kubeconfig file for kubelet will be generated by the TLS bootstrap process in
297302
// following steps of the join --control-plane workflow
@@ -303,6 +308,14 @@ func runControlPlanePrepareKubeconfigPhaseLocal(c workflow.RunData) error {
303308
}
304309

305310
func bootstrapClient(data JoinData) (clientset.Interface, error) {
311+
if data.DryRun() {
312+
client, err := data.Client()
313+
if err != nil {
314+
return nil, err
315+
}
316+
return client, nil
317+
}
318+
306319
tlsBootstrapCfg, err := data.TLSBootstrapCfg()
307320
if err != nil {
308321
return nil, errors.Wrap(err, "unable to access the cluster")

cmd/kubeadm/app/phases/etcd/local.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"k8s.io/kubernetes/cmd/kubeadm/app/features"
3939
"k8s.io/kubernetes/cmd/kubeadm/app/images"
4040
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
41+
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
4142
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
4243
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
4344
"k8s.io/kubernetes/cmd/kubeadm/app/util/users"
@@ -139,19 +140,20 @@ func RemoveStackedEtcdMemberFromCluster(client clientset.Interface, cfg *kubeadm
139140
// for an additional etcd member that is joining an existing local/stacked etcd cluster.
140141
// Other members of the etcd cluster will be notified of the joining node in beforehand as well.
141142
func CreateStackedEtcdStaticPodManifestFile(client clientset.Interface, manifestDir, patchesDir string, nodeName string, cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.APIEndpoint, isDryRun bool, certificatesDir string) error {
142-
// creates an etcd client that connects to all the local/stacked etcd members
143-
klog.V(1).Info("creating etcd client that connects to etcd pods")
144-
etcdClient, err := etcdutil.NewFromCluster(client, certificatesDir)
145-
if err != nil {
146-
return err
147-
}
148-
149143
etcdPeerAddress := etcdutil.GetPeerURL(endpoint)
150144

151145
var cluster []etcdutil.Member
146+
var etcdClient *etcdutil.Client
147+
var err error
152148
if isDryRun {
153149
fmt.Printf("[etcd] Would add etcd member: %s\n", etcdPeerAddress)
154150
} else {
151+
// Creates an etcd client that connects to all the local/stacked etcd members.
152+
klog.V(1).Info("creating etcd client that connects to etcd pods")
153+
etcdClient, err = etcdutil.NewFromCluster(client, certificatesDir)
154+
if err != nil {
155+
return err
156+
}
155157
klog.V(1).Infof("[etcd] Adding etcd member: %s", etcdPeerAddress)
156158
if features.Enabled(cfg.FeatureGates, features.EtcdLearnerMode) {
157159
cluster, err = etcdClient.AddMemberAsLearner(nodeName, etcdPeerAddress)
@@ -323,5 +325,11 @@ func prepareAndWriteEtcdStaticPod(manifestDir string, patchesDir string, cfg *ku
323325
return err
324326
}
325327

328+
// If dry-running, print the static etcd pod manifest file.
329+
if isDryRun {
330+
realPath := kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.Etcd, manifestDir)
331+
outputPath := kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.Etcd, kubeadmconstants.GetStaticPodDirectory())
332+
return dryrunutil.PrintDryRunFiles([]dryrunutil.FileToPrint{dryrunutil.NewFileToPrint(realPath, outputPath)}, os.Stdout)
333+
}
326334
return nil
327335
}

cmd/kubeadm/app/util/apiclient/dryrun.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,24 @@ func (d *DryRun) GetKubeadmConfigReactor() *testing.SimpleReactor {
415415
}
416416
}
417417

418+
// GetKubeadmCertsReactor returns a reactor that handles the GET action of the "kubeadm-certs" Secret.
419+
func (d *DryRun) GetKubeadmCertsReactor() *testing.SimpleReactor {
420+
return &testing.SimpleReactor{
421+
Verb: "get",
422+
Resource: "secrets",
423+
Reaction: func(action testing.Action) (bool, runtime.Object, error) {
424+
a := action.(testing.GetAction)
425+
if a.GetName() != constants.KubeadmCertsSecret || a.GetNamespace() != metav1.NamespaceSystem {
426+
return false, nil, nil
427+
}
428+
429+
obj := getKubeadmCertsSecret()
430+
d.LogObject(obj, action.GetResource().GroupVersion())
431+
return true, obj, nil
432+
},
433+
}
434+
}
435+
418436
// GetKubeletConfigReactor returns a reactor that handles the GET action of the "kubelet-config"
419437
// ConfigMap.
420438
func (d *DryRun) GetKubeletConfigReactor() *testing.SimpleReactor {
@@ -513,6 +531,17 @@ func getConfigMap(namespace, name string, data map[string]string) *corev1.Config
513531
}
514532
}
515533

534+
// getSecret returns a fake Secret object.
535+
func getSecret(namespace, name string, data map[string][]byte) *corev1.Secret {
536+
return &corev1.Secret{
537+
ObjectMeta: metav1.ObjectMeta{
538+
Name: name,
539+
Namespace: namespace,
540+
},
541+
Data: data,
542+
}
543+
}
544+
516545
// getClusterInfoConfigMap returns a fake "cluster-info" ConfigMap.
517546
func getClusterInfoConfigMap() *corev1.ConfigMap {
518547
kubeconfig := dedent.Dedent(`apiVersion: v1
@@ -546,6 +575,7 @@ controllerManager:
546575
extraArgs:
547576
- name: cluster-signing-duration
548577
value: 24h
578+
controlPlaneEndpoint: 192.168.0.101:6443
549579
dns: {}
550580
encryptionAlgorithm: RSA-2048
551581
etcd:
@@ -568,6 +598,23 @@ scheduler: {}
568598
return getConfigMap(metav1.NamespaceSystem, constants.KubeadmConfigConfigMap, data)
569599
}
570600

601+
// getKubeadmCertsSecret returns a fake "kubeadm-certs" Secret.
602+
func getKubeadmCertsSecret() *corev1.Secret {
603+
// The cert data is empty because the actual content is not relevant for the dryrun test.
604+
data := map[string][]byte{
605+
constants.CACertName: {},
606+
constants.CAKeyName: {},
607+
constants.FrontProxyCACertName: {},
608+
constants.FrontProxyCAKeyName: {},
609+
constants.ServiceAccountPrivateKeyName: {},
610+
constants.ServiceAccountPublicKeyName: {},
611+
strings.ReplaceAll(constants.EtcdCACertName, "/", "-"): {},
612+
strings.ReplaceAll(constants.EtcdCAKeyName, "/", "-"): {},
613+
}
614+
615+
return getSecret(metav1.NamespaceSystem, constants.KubeadmCertsSecret, data)
616+
}
617+
571618
// getKubeletConfigMap returns a fake "kubelet-config" ConfigMap.
572619
func getKubeletConfigMap() *corev1.ConfigMap {
573620
configData := `apiVersion: kubelet.config.k8s.io/v1beta1

0 commit comments

Comments
 (0)