Skip to content

Commit 986679b

Browse files
authored
Merge pull request #6787 from sharifelgamal/m2
Implement multi-node cluster capabilities
2 parents fa07bc2 + 05814cc commit 986679b

File tree

111 files changed

+915
-796
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+915
-796
lines changed

Diff for: cmd/minikube/cmd/node.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ import (
2323

2424
// nodeCmd represents the set of node subcommands
2525
var nodeCmd = &cobra.Command{
26-
Use: "node",
27-
Short: "Node operations",
28-
Long: "Operations on nodes",
29-
Hidden: true, // This won't be fully functional and thus should not be documented yet
26+
Use: "node",
27+
Short: "Node operations",
28+
Long: "Operations on nodes",
3029
Run: func(cmd *cobra.Command, args []string) {
3130
exit.UsageT("Usage: minikube node [add|start|stop|delete]")
3231
},

Diff for: cmd/minikube/cmd/node_add.go

+14-9
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20-
"fmt"
21-
2220
"github.com/spf13/cobra"
2321
"github.com/spf13/pflag"
2422
"github.com/spf13/viper"
2523
"k8s.io/minikube/pkg/minikube/config"
24+
"k8s.io/minikube/pkg/minikube/driver"
2625
"k8s.io/minikube/pkg/minikube/exit"
2726
"k8s.io/minikube/pkg/minikube/node"
2827
"k8s.io/minikube/pkg/minikube/out"
@@ -43,19 +42,25 @@ var nodeAddCmd = &cobra.Command{
4342
exit.WithError("Error getting config", err)
4443
}
4544

46-
//name := profile + strconv.Itoa(len(mc.Nodes)+1)
47-
name := fmt.Sprintf("m%d", len(cc.Nodes)+1)
45+
if driver.BareMetal(cc.Driver) {
46+
out.ErrT(out.FailureType, "none driver does not support multi-node clusters")
47+
}
48+
49+
name := node.Name(len(cc.Nodes) + 1)
4850

4951
out.T(out.Happy, "Adding node {{.name}} to cluster {{.cluster}}", out.V{"name": name, "cluster": profile})
5052

51-
n, err := node.Add(cc, name, cp, worker, "", profile)
52-
if err != nil {
53-
exit.WithError("Error adding node to cluster", err)
53+
// TODO: Deal with parameters better. Ideally we should be able to acceot any node-specific minikube start params here.
54+
n := config.Node{
55+
Name: name,
56+
Worker: worker,
57+
ControlPlane: cp,
58+
KubernetesVersion: cc.KubernetesConfig.KubernetesVersion,
5459
}
5560

56-
_, err = node.Start(*cc, *n, false, nil)
61+
err = node.Add(cc, n)
5762
if err != nil {
58-
exit.WithError("Error starting node", err)
63+
exit.WithError("Error adding node to cluster", err)
5964
}
6065

6166
out.T(out.Ready, "Successfully added {{.name}} to {{.cluster}}!", out.V{"name": name, "cluster": profile})

Diff for: cmd/minikube/cmd/node_delete.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ var nodeDeleteCmd = &cobra.Command{
4646

4747
err = node.Delete(*cc, name)
4848
if err != nil {
49-
out.FatalT("Failed to delete node {{.name}}", out.V{"name": name})
49+
exit.WithError("deleting node", err)
5050
}
5151

5252
out.T(out.Deleted, "Node {{.name}} was successfully deleted.", out.V{"name": name})

Diff for: cmd/minikube/cmd/node_start.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,7 @@ var nodeStartCmd = &cobra.Command{
6161
}
6262

6363
// Start it up baby
64-
_, err = node.Start(*cc, *n, false, nil)
65-
if err != nil {
66-
out.FatalT("Failed to start node {{.name}}", out.V{"name": name})
67-
}
64+
node.Start(*cc, *n, nil, false)
6865
},
6966
}
7067

Diff for: cmd/minikube/cmd/ssh.go

+17-7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"k8s.io/minikube/pkg/minikube/driver"
2828
"k8s.io/minikube/pkg/minikube/exit"
2929
"k8s.io/minikube/pkg/minikube/machine"
30+
"k8s.io/minikube/pkg/minikube/node"
3031
"k8s.io/minikube/pkg/minikube/out"
3132
)
3233

@@ -49,12 +50,20 @@ var sshCmd = &cobra.Command{
4950
if err != nil {
5051
exit.WithError("Error getting config", err)
5152
}
52-
// TODO: allow choice of node to ssh into
53-
cp, err := config.PrimaryControlPlane(cc)
54-
if err != nil {
55-
exit.WithError("Error getting primary control plane", err)
53+
var n *config.Node
54+
if nodeName == "" {
55+
cp, err := config.PrimaryControlPlane(cc)
56+
if err != nil {
57+
exit.WithError("Getting primary control plane", err)
58+
}
59+
n = &cp
60+
} else {
61+
n, _, err = node.Retrieve(cc, nodeName)
62+
if err != nil {
63+
exit.WithCodeT(exit.Unavailable, "Node {{.nodeName}} does not exist.", out.V{"nodeName": nodeName})
64+
}
5665
}
57-
host, err := machine.LoadHost(api, driver.MachineName(*cc, cp))
66+
host, err := machine.LoadHost(api, driver.MachineName(*cc, *n))
5867
if err != nil {
5968
exit.WithError("Error getting host", err)
6069
}
@@ -67,7 +76,7 @@ var sshCmd = &cobra.Command{
6776
ssh.SetDefaultClient(ssh.External)
6877
}
6978

70-
err = machine.CreateSSHShell(api, *cc, cp, args)
79+
err = machine.CreateSSHShell(api, *cc, *n, args)
7180
if err != nil {
7281
// This is typically due to a non-zero exit code, so no need for flourish.
7382
out.ErrLn("ssh: %v", err)
@@ -78,5 +87,6 @@ var sshCmd = &cobra.Command{
7887
}
7988

8089
func init() {
81-
sshCmd.Flags().BoolVar(&nativeSSHClient, nativeSSH, true, "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.")
90+
sshCmd.Flags().Bool(nativeSSH, true, "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.")
91+
sshCmd.Flags().StringVarP(&nodeName, "node", "n", "", "The node to ssh into. Defaults to the primary control plane.")
8292
}

Diff for: cmd/minikube/cmd/start.go

+48-28
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ const (
120120
autoUpdate = "auto-update-drivers"
121121
hostOnlyNicType = "host-only-nic-type"
122122
natNicType = "nat-nic-type"
123+
nodes = "nodes"
123124
)
124125

125126
var (
@@ -162,7 +163,7 @@ func initMinikubeFlags() {
162163
startCmd.Flags().String(containerRuntime, "docker", "The container runtime to be used (docker, crio, containerd).")
163164
startCmd.Flags().Bool(createMount, false, "This will start the mount daemon and automatically mount files into minikube.")
164165
startCmd.Flags().String(mountString, constants.DefaultMountDir+":/minikube-host", "The argument to pass the minikube mount command on start.")
165-
startCmd.Flags().StringArrayVar(&node.AddonList, "addons", nil, "Enable addons. see `minikube addons list` for a list of valid addon names.")
166+
startCmd.Flags().StringArrayVar(&config.AddonList, "addons", nil, "Enable addons. see `minikube addons list` for a list of valid addon names.")
166167
startCmd.Flags().String(criSocket, "", "The cri socket path to be used.")
167168
startCmd.Flags().String(networkPlugin, "", "The name of the network plugin.")
168169
startCmd.Flags().Bool(enableDefaultCNI, false, "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \"--network-plugin=cni\".")
@@ -171,12 +172,13 @@ func initMinikubeFlags() {
171172
startCmd.Flags().Bool(nativeSSH, true, "Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'.")
172173
startCmd.Flags().Bool(autoUpdate, true, "If set, automatically updates drivers to the latest version. Defaults to true.")
173174
startCmd.Flags().Bool(installAddons, true, "If set, install addons. Defaults to true.")
175+
startCmd.Flags().IntP(nodes, "n", 1, "The number of nodes to spin up. Defaults to 1.")
174176
}
175177

176178
// initKubernetesFlags inits the commandline flags for kubernetes related options
177179
func initKubernetesFlags() {
178180
startCmd.Flags().String(kubernetesVersion, "", "The kubernetes version that the minikube VM will use (ex: v1.2.3)")
179-
startCmd.Flags().Var(&node.ExtraOptions, "extra-config",
181+
startCmd.Flags().Var(&config.ExtraOptions, "extra-config",
180182
`A set of key=value pairs that describe configuration that may be passed to different components.
181183
The key should be '.' separated, and the first part before the dot is the component to apply the configuration to.
182184
Valid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler
@@ -229,8 +231,8 @@ func initNetworkingFlags() {
229231
startCmd.Flags().String(imageRepository, "", "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \"auto\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers")
230232
startCmd.Flags().String(imageMirrorCountry, "", "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn.")
231233
startCmd.Flags().String(serviceCIDR, constants.DefaultServiceCIDR, "The CIDR to be used for service cluster IPs.")
232-
startCmd.Flags().StringArrayVar(&node.DockerEnv, "docker-env", nil, "Environment variables to pass to the Docker daemon. (format: key=value)")
233-
startCmd.Flags().StringArrayVar(&node.DockerOpt, "docker-opt", nil, "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)")
234+
startCmd.Flags().StringArrayVar(&config.DockerEnv, "docker-env", nil, "Environment variables to pass to the Docker daemon. (format: key=value)")
235+
startCmd.Flags().StringArrayVar(&config.DockerOpt, "docker-opt", nil, "Specify arbitrary flags to pass to the Docker daemon. (format: key=value)")
234236
}
235237

236238
// startCmd represents the start command
@@ -313,7 +315,7 @@ func runStart(cmd *cobra.Command, args []string) {
313315
}
314316

315317
k8sVersion := getKubernetesVersion(existing)
316-
mc, n, err := generateCfgFromFlags(cmd, k8sVersion, driverName)
318+
cc, n, err := generateCfgFromFlags(cmd, k8sVersion, driverName)
317319
if err != nil {
318320
exit.WithError("Failed to generate config", err)
319321
}
@@ -324,12 +326,12 @@ func runStart(cmd *cobra.Command, args []string) {
324326
return
325327
}
326328

327-
if !driver.BareMetal(driverName) && !driver.IsKIC(driverName) {
329+
if driver.IsVM(driverName) {
328330
url, err := download.ISO(viper.GetStringSlice(isoURL), cmd.Flags().Changed(isoURL))
329331
if err != nil {
330332
exit.WithError("Failed to cache ISO", err)
331333
}
332-
mc.MinikubeISO = url
334+
cc.MinikubeISO = url
333335
}
334336

335337
if viper.GetBool(nativeSSH) {
@@ -338,12 +340,41 @@ func runStart(cmd *cobra.Command, args []string) {
338340
ssh.SetDefaultClient(ssh.External)
339341
}
340342

341-
kubeconfig, err := startNode(existing, mc, n)
342-
if err != nil {
343-
exit.WithError("Starting node", err)
343+
var existingAddons map[string]bool
344+
if viper.GetBool(installAddons) {
345+
existingAddons = map[string]bool{}
346+
if existing != nil && existing.Addons != nil {
347+
existingAddons = existing.Addons
348+
}
344349
}
345350

346-
if err := showKubectlInfo(kubeconfig, k8sVersion, mc.Name); err != nil {
351+
kubeconfig := node.Start(cc, n, existingAddons, true)
352+
353+
numNodes := viper.GetInt(nodes)
354+
if numNodes == 1 && existing != nil {
355+
numNodes = len(existing.Nodes)
356+
}
357+
if numNodes > 1 {
358+
if driver.BareMetal(driverName) {
359+
exit.WithCodeT(exit.Config, "The none driver is not compatible with multi-node clusters.")
360+
} else {
361+
for i := 1; i < numNodes; i++ {
362+
nodeName := node.Name(i + 1)
363+
n := config.Node{
364+
Name: nodeName,
365+
Worker: true,
366+
ControlPlane: false,
367+
KubernetesVersion: cc.KubernetesConfig.KubernetesVersion,
368+
}
369+
err := node.Add(&cc, n)
370+
if err != nil {
371+
exit.WithError("adding node", err)
372+
}
373+
}
374+
}
375+
}
376+
377+
if err := showKubectlInfo(kubeconfig, k8sVersion, cc.Name); err != nil {
347378
glog.Errorf("kubectl info: %v", err)
348379
}
349380
}
@@ -383,17 +414,6 @@ func displayEnviron(env []string) {
383414
}
384415
}
385416

386-
func startNode(existing *config.ClusterConfig, mc config.ClusterConfig, n config.Node) (*kubeconfig.Settings, error) {
387-
var existingAddons map[string]bool
388-
if viper.GetBool(installAddons) {
389-
existingAddons = map[string]bool{}
390-
if existing != nil && existing.Addons != nil {
391-
existingAddons = existing.Addons
392-
}
393-
}
394-
return node.Start(mc, n, true, existingAddons)
395-
}
396-
397417
func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string, machineName string) error {
398418
if kcs.KeepContext {
399419
out.T(out.Kubectl, "To connect to this cluster, use: kubectl --context={{.name}}", out.V{"name": kcs.ClusterName})
@@ -802,7 +822,7 @@ func validateFlags(cmd *cobra.Command, drvName string) {
802822
}
803823

804824
// check that kubeadm extra args contain only whitelisted parameters
805-
for param := range node.ExtraOptions.AsMap().Get(bsutil.Kubeadm) {
825+
for param := range config.ExtraOptions.AsMap().Get(bsutil.Kubeadm) {
806826
if !config.ContainsParam(bsutil.KubeadmExtraArgsWhitelist[bsutil.KubeadmCmdParam], param) &&
807827
!config.ContainsParam(bsutil.KubeadmExtraArgsWhitelist[bsutil.KubeadmConfigParam], param) {
808828
exit.UsageT("Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config", out.V{"parameter_name": param})
@@ -930,8 +950,8 @@ func createNode(cmd *cobra.Command, k8sVersion, kubeNodeName, drvName, repositor
930950
HyperkitVSockPorts: viper.GetStringSlice(vsockPorts),
931951
NFSShare: viper.GetStringSlice(nfsShare),
932952
NFSSharesRoot: viper.GetString(nfsSharesRoot),
933-
DockerEnv: node.DockerEnv,
934-
DockerOpt: node.DockerOpt,
953+
DockerEnv: config.DockerEnv,
954+
DockerOpt: config.DockerOpt,
935955
InsecureRegistry: insecureRegistry,
936956
RegistryMirror: registryMirror,
937957
HostOnlyCIDR: viper.GetString(hostOnlyCIDR),
@@ -962,7 +982,7 @@ func createNode(cmd *cobra.Command, k8sVersion, kubeNodeName, drvName, repositor
962982
NetworkPlugin: selectedNetworkPlugin,
963983
ServiceCIDR: viper.GetString(serviceCIDR),
964984
ImageRepository: repository,
965-
ExtraOptions: node.ExtraOptions,
985+
ExtraOptions: config.ExtraOptions,
966986
ShouldLoadCachedImages: viper.GetBool(cacheImages),
967987
EnableDefaultCNI: selectedEnableDefaultCNI,
968988
},
@@ -984,7 +1004,7 @@ func setDockerProxy() {
9841004
continue
9851005
}
9861006
}
987-
node.DockerEnv = append(node.DockerEnv, fmt.Sprintf("%s=%s", k, v))
1007+
config.DockerEnv = append(config.DockerEnv, fmt.Sprintf("%s=%s", k, v))
9881008
}
9891009
}
9901010
}
@@ -996,7 +1016,7 @@ func autoSetDriverOptions(cmd *cobra.Command, drvName string) (err error) {
9961016
if !cmd.Flags().Changed("extra-config") && len(hints.ExtraOptions) > 0 {
9971017
for _, eo := range hints.ExtraOptions {
9981018
glog.Infof("auto setting extra-config to %q.", eo)
999-
err = node.ExtraOptions.Set(eo)
1019+
err = config.ExtraOptions.Set(eo)
10001020
if err != nil {
10011021
err = errors.Wrapf(err, "setting extra option %s", eo)
10021022
}

0 commit comments

Comments
 (0)