diff --git a/cmd/minikube/cmd/node_start.go b/cmd/minikube/cmd/node_start.go index 81f9ac6b80..7399e87123 100644 --- a/cmd/minikube/cmd/node_start.go +++ b/cmd/minikube/cmd/node_start.go @@ -75,7 +75,6 @@ var nodeStartCmd = &cobra.Command{ } func init() { - nodeStartCmd.Flags().String("name", "", "The name of the node to start") nodeStartCmd.Flags().Bool(deleteOnFailure, false, "If set, delete the current cluster if start fails and try again. Defaults to false.") nodeCmd.AddCommand(nodeStartCmd) } diff --git a/cmd/minikube/cmd/node_stop.go b/cmd/minikube/cmd/node_stop.go index 5dbceba1bc..3f5c8d7433 100644 --- a/cmd/minikube/cmd/node_stop.go +++ b/cmd/minikube/cmd/node_stop.go @@ -53,6 +53,5 @@ var nodeStopCmd = &cobra.Command{ } func init() { - nodeStopCmd.Flags().String("name", "", "The name of the node to delete") nodeCmd.AddCommand(nodeStopCmd) } diff --git a/cmd/minikube/cmd/status.go b/cmd/minikube/cmd/status.go index 5f4ca77767..c63ab1b7bc 100644 --- a/cmd/minikube/cmd/status.go +++ b/cmd/minikube/cmd/status.go @@ -104,12 +104,11 @@ var statusCmd = &cobra.Command{ cname := ClusterFlagValue() api, cc := mustload.Partial(cname) - var st *Status - var err error + var statuses []*Status for _, n := range cc.Nodes { glog.Infof("checking status of %s ...", n.Name) machineName := driver.MachineName(*cc, n) - st, err = status(api, *cc, n) + st, err := status(api, *cc, n) glog.Infof("%s status: %+v", machineName, st) if err != nil { @@ -118,36 +117,40 @@ var statusCmd = &cobra.Command{ if st.Host == Nonexistent { glog.Errorf("The %q host does not exist!", machineName) } + statuses = append(statuses, st) + } - switch strings.ToLower(output) { - case "text": + switch strings.ToLower(output) { + case "text": + for _, st := range statuses { if err := statusText(st, os.Stdout); err != nil { exit.WithError("status text failure", err) } - case "json": - if err := statusJSON(st, os.Stdout); err != nil { - exit.WithError("status json failure", err) - } - default: - exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output)) } + case "json": + if err := statusJSON(statuses, os.Stdout); err != nil { + exit.WithError("status json failure", err) + } + default: + exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output)) } - // TODO: Update for multi-node - os.Exit(exitCode(st)) + os.Exit(exitCode(statuses)) }, } -func exitCode(st *Status) int { +func exitCode(statuses []*Status) int { c := 0 - if st.Host != state.Running.String() { - c |= minikubeNotRunningStatusFlag - } - if (st.APIServer != state.Running.String() && st.APIServer != Irrelevant) || st.Kubelet != state.Running.String() { - c |= clusterNotRunningStatusFlag - } - if st.Kubeconfig != Configured && st.Kubeconfig != Irrelevant { - c |= k8sNotRunningStatusFlag + for _, st := range statuses { + if st.Host != state.Running.String() { + c |= minikubeNotRunningStatusFlag + } + if (st.APIServer != state.Running.String() && st.APIServer != Irrelevant) || st.Kubelet != state.Running.String() { + c |= clusterNotRunningStatusFlag + } + if st.Kubeconfig != Configured && st.Kubeconfig != Irrelevant { + c |= k8sNotRunningStatusFlag + } } return c } @@ -270,8 +273,15 @@ func statusText(st *Status, w io.Writer) error { return nil } -func statusJSON(st *Status, w io.Writer) error { - js, err := json.Marshal(st) +func statusJSON(st []*Status, w io.Writer) error { + var js []byte + var err error + // Keep backwards compat with single node clusters to not break anyone + if len(st) == 1 { + js, err = json.Marshal(st[0]) + } else { + js, err = json.Marshal(st) + } if err != nil { return err } diff --git a/cmd/minikube/cmd/status_test.go b/cmd/minikube/cmd/status_test.go index e74d1080bd..8bae781037 100644 --- a/cmd/minikube/cmd/status_test.go +++ b/cmd/minikube/cmd/status_test.go @@ -35,7 +35,7 @@ func TestExitCode(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - got := exitCode(tc.state) + got := exitCode([]*Status{tc.state}) if got != tc.want { t.Errorf("exitcode(%+v) = %d, want: %d", tc.state, got, tc.want) } @@ -93,7 +93,7 @@ func TestStatusJSON(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { var b bytes.Buffer - err := statusJSON(tc.state, &b) + err := statusJSON([]*Status{tc.state}, &b) if err != nil { t.Errorf("json(%+v) error: %v", tc.state, err) } diff --git a/deploy/iso/minikube-iso/package/automount/minikube-automount b/deploy/iso/minikube-iso/package/automount/minikube-automount index 77e5fda1fe..f1a8484bee 100755 --- a/deploy/iso/minikube-iso/package/automount/minikube-automount +++ b/deploy/iso/minikube-iso/package/automount/minikube-automount @@ -101,6 +101,10 @@ if [ -n "$BOOT2DOCKER_DATA" ]; then mkdir -p /var/lib/docker mount --bind /mnt/$PARTNAME/var/lib/docker /var/lib/docker + mkdir -p /mnt/$PARTNAME/var/lib/containerd + mkdir -p /var/lib/containerd + mount --bind /mnt/$PARTNAME/var/lib/containerd /var/lib/containerd + mkdir -p /mnt/$PARTNAME/var/lib/containers mkdir -p /var/lib/containers mount --bind /mnt/$PARTNAME/var/lib/containers /var/lib/containers diff --git a/deploy/iso/minikube-iso/package/podman/podman.mk b/deploy/iso/minikube-iso/package/podman/podman.mk index fb12281c9d..ccc3af60b8 100644 --- a/deploy/iso/minikube-iso/package/podman/podman.mk +++ b/deploy/iso/minikube-iso/package/podman/podman.mk @@ -30,7 +30,7 @@ endef define PODMAN_INSTALL_TARGET_CMDS $(INSTALL) -Dm755 $(@D)/bin/podman $(TARGET_DIR)/usr/bin/podman $(INSTALL) -d -m 755 $(TARGET_DIR)/etc/cni/net.d/ - $(INSTALL) -m 644 cni/87-podman-bridge.conflist $(TARGET_DIR)/etc/cni/net.d/87-podman-bridge.conflist + $(INSTALL) -m 644 $(@D)/cni/87-podman-bridge.conflist $(TARGET_DIR)/etc/cni/net.d/87-podman-bridge.conflist endef $(eval $(generic-package)) diff --git a/hack/jenkins/linux_integration_tests_podman.sh b/hack/jenkins/linux_integration_tests_podman.sh index 1dfbdb4456..f6c704643f 100755 --- a/hack/jenkins/linux_integration_tests_podman.sh +++ b/hack/jenkins/linux_integration_tests_podman.sh @@ -27,7 +27,7 @@ set -e OS_ARCH="linux-amd64" VM_DRIVER="podman" -JOB_NAME="Podman_Linux" +JOB_NAME="Experimental_Podman_Linux" mkdir -p cron && gsutil -qm rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES" sudo install cron/cleanup_and_reboot_Linux.sh /etc/cron.hourly/cleanup_and_reboot || echo "FAILED TO INSTALL CLEANUP" diff --git a/hack/jenkins/release_github_page.sh b/hack/jenkins/release_github_page.sh index 3ce137911b..676fea3942 100755 --- a/hack/jenkins/release_github_page.sh +++ b/hack/jenkins/release_github_page.sh @@ -75,34 +75,13 @@ github-release -v release ${RELEASE_FLAGS} \ --name "${TAGNAME}" \ --description "${DESCRIPTION}" -# Uploading the files into github -FILES_TO_UPLOAD=( - 'minikube-linux-amd64' - 'minikube-linux-amd64.sha256' - 'minikube-darwin-amd64' - 'minikube-darwin-amd64.sha256' - 'minikube-windows-amd64.exe' - 'minikube-windows-amd64.exe.sha256' - 'minikube-installer.exe' - "minikube_${DEB_VERSION}-0_amd64.deb" - "minikube-${RPM_VERSION}-0.x86_64.rpm" - 'docker-machine-driver-kvm2' - 'docker-machine-driver-kvm2.sha256' - 'docker-machine-driver-hyperkit' - 'docker-machine-driver-hyperkit.sha256' -) - -# ISO files are special, as they are generated pre-release tagging -ISO_FILES=("minikube-v${VERSION}.iso" "minikube-v${VERSION}.iso.sha256") -for DOWNLOAD in "${ISO_FILES[@]}" -do - gsutil cp "gs://${ISO_BUCKET}/${DOWNLOAD}" out/ \ - && FILES_TO_UPLOAD+=("${DOWNLOAD}") \ - || echo "${DOWNLOAD} was not generated for this release" +# ISO files are built from a separate process, and may not be included in this release +for path in $(gsutil ls "gs://${ISO_BUCKET}/minikube-v${VERSION}*" || true); do + gsutil cp "${path}" out/ done - -for UPLOAD in "${FILES_TO_UPLOAD[@]}" -do + +# Upload all end-user assets other than preload files, as they are release independent +for file in out/minikube[_-]* out/docker-machine-*; do n=0 until [ $n -ge 5 ] do @@ -110,8 +89,8 @@ do --user "${GITHUB_ORGANIZATION}" \ --repo "${GITHUB_REPO}" \ --tag "${TAGNAME}" \ - --name "$UPLOAD" \ - --file "out/$UPLOAD" && break + --name "$(basename ${file})" \ + --file "${file}" && break n=$((n+1)) sleep 15 done diff --git a/hack/preload-images/generate.go b/hack/preload-images/generate.go index a85ff36349..daa9e77f09 100644 --- a/hack/preload-images/generate.go +++ b/hack/preload-images/generate.go @@ -31,7 +31,6 @@ import ( "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/cruntime" - "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/sysinit" "k8s.io/minikube/pkg/util" @@ -41,7 +40,7 @@ import ( func generateTarball(kubernetesVersion, containerRuntime, tarballFilename string) error { driver := kic.NewDriver(kic.Config{ KubernetesVersion: kubernetesVersion, - ContainerRuntime: driver.Docker, + ContainerRuntime: containerRuntime, OCIBinary: oci.Docker, MachineName: profile, ImageDigest: kic.BaseImage, diff --git a/site/content/en/docs/commands/node.md b/site/content/en/docs/commands/node.md index 9aec995f71..fae5a2ed6b 100644 --- a/site/content/en/docs/commands/node.md +++ b/site/content/en/docs/commands/node.md @@ -155,7 +155,6 @@ minikube node start [flags] ``` --delete-on-failure If set, delete the current cluster if start fails and try again. Defaults to false. -h, --help help for start - --name string The name of the node to start ``` ### Options inherited from parent commands @@ -187,8 +186,7 @@ minikube node stop [flags] ### Options ``` - -h, --help help for stop - --name string The name of the node to delete + -h, --help help for stop ``` ### Options inherited from parent commands diff --git a/test/integration/multinode_test.go b/test/integration/multinode_test.go index efa662964a..5a9cc4f16a 100644 --- a/test/integration/multinode_test.go +++ b/test/integration/multinode_test.go @@ -29,24 +29,154 @@ func TestMultiNode(t *testing.T) { if NoneDriver() { t.Skip("none driver does not support multinode") } + MaybeParallel(t) + type validatorFunc func(context.Context, *testing.T, string) profile := UniqueProfileName("multinode") ctx, cancel := context.WithTimeout(context.Background(), Minutes(30)) defer CleanupWithLogs(t, profile, cancel) - startArgs := append([]string{"start", "-p", profile, "--wait=true"}, StartArgs()...) + t.Run("serial", func(t *testing.T) { + tests := []struct { + name string + validator validatorFunc + }{ + {"FreshStart2Nodes", validateMultiNodeStart}, + {"AddNode", validateAddNodeToMultiNode}, + {"StopNode", validateStopRunningNode}, + {"StartAfterStop", validateStartNodeAfterStop}, + {"DeleteNode", validateDeleteNodeFromMultiNode}, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + tc.validator(ctx, t, profile) + }) + } + }) +} + +func validateMultiNodeStart(ctx context.Context, t *testing.T, profile string) { + // Start a 2 node cluster with the --nodes param + startArgs := append([]string{"start", "-p", profile, "--wait=true", "--nodes=2"}, StartArgs()...) rr, err := Run(t, exec.CommandContext(ctx, Target(), startArgs...)) if err != nil { t.Fatalf("failed to start cluster. args %q : %v", rr.Command(), err) } - // Add a node to the current cluster - addArgs := []string{"node", "add", "-p", profile, "-v", "3", "--alsologtostderr"} - rr, err = Run(t, exec.CommandContext(ctx, Target(), addArgs...)) - if err != nil { - t.Fatalf("failed to add node to current cluster. args %q : %v", rr.Command(), err) - } - + // Make sure minikube status shows 2 nodes + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) + if err != nil { + t.Fatalf("failed to run minikube status. args %q : %v", rr.Command(), err) + } + + if strings.Count(rr.Stdout.String(), "host: Running") != 2 { + t.Errorf("status says both hosts are not running: args %q: %v", rr.Command(), rr.Stdout.String()) + } + + if strings.Count(rr.Stdout.String(), "kubelet: Running") != 2 { + t.Errorf("status says both kubelets are not running: args %q: %v", rr.Command(), rr.Stdout.String()) + } + +} + +func validateAddNodeToMultiNode(ctx context.Context, t *testing.T, profile string) { + // Add a node to the current cluster + addArgs := []string{"node", "add", "-p", profile, "-v", "3", "--alsologtostderr"} + rr, err := Run(t, exec.CommandContext(ctx, Target(), addArgs...)) + if err != nil { + t.Fatalf("failed to add node to current cluster. args %q : %v", rr.Command(), err) + } + + // Make sure minikube status shows 3 nodes + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) + if err != nil { + t.Fatalf("failed to run minikube status. args %q : %v", rr.Command(), err) + } + + if strings.Count(rr.Stdout.String(), "host: Running") != 3 { + t.Errorf("status says all hosts are not running: args %q: %v", rr.Command(), rr.Stdout.String()) + } + + if strings.Count(rr.Stdout.String(), "kubelet: Running") != 3 { + t.Errorf("status says all kubelets are not running: args %q: %v", rr.Command(), rr.Stdout.String()) + } +} + +func validateStopRunningNode(ctx context.Context, t *testing.T, profile string) { + // Names are autogenerated using the node.Name() function + name := "m03" + + // Run minikube node stop on that node + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "node", "stop", name)) + if err != nil { + t.Errorf("node stop returned an error. args %q: %v", rr.Command(), err) + } + + // Run status again to see the stopped host + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) + // Exit code 7 means one host is stopped, which we are expecting + if err != nil && rr.ExitCode != 7 { + t.Fatalf("failed to run minikube status. args %q : %v", rr.Command(), err) + } + + // Make sure minikube status shows 2 running nodes and 1 stopped one + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) + if err != nil && rr.ExitCode != 7 { + t.Fatalf("failed to run minikube status. args %q : %v", rr.Command(), err) + } + + if strings.Count(rr.Stdout.String(), "kubelet: Running") != 2 { + t.Errorf("incorrect number of running kubelets: args %q: %v", rr.Command(), rr.Stdout.String()) + } + + if strings.Count(rr.Stdout.String(), "host: Stopped") != 1 { + t.Errorf("incorrect number of stopped hosts: args %q: %v", rr.Command(), rr.Stdout.String()) + } + + if strings.Count(rr.Stdout.String(), "kubelet: Stopped") != 1 { + t.Errorf("incorrect number of stopped kubelets: args %q: %v", rr.Command(), rr.Stdout.String()) + } +} + +func validateStartNodeAfterStop(ctx context.Context, t *testing.T, profile string) { + // TODO (#7496): remove skip once restarts work + t.Skip("Restarting nodes is broken :(") + + // Grab the stopped node + name := "m03" + + // Start the node back up + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "node", "start", name)) + if err != nil { + t.Errorf("node start returned an error. args %q: %v", rr.Command(), err) + } + + // Make sure minikube status shows 3 running hosts + rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) + if err != nil { + t.Fatalf("failed to run minikube status. args %q : %v", rr.Command(), err) + } + + if strings.Count(rr.Stdout.String(), "host: Running") != 3 { + t.Errorf("status says both hosts are not running: args %q: %v", rr.Command(), rr.Stdout.String()) + } + + if strings.Count(rr.Stdout.String(), "kubelet: Running") != 3 { + t.Errorf("status says both kubelets are not running: args %q: %v", rr.Command(), rr.Stdout.String()) + } +} + +func validateDeleteNodeFromMultiNode(ctx context.Context, t *testing.T, profile string) { + name := "m03" + + // Start the node back up + rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "node", "delete", name)) + if err != nil { + t.Errorf("node stop returned an error. args %q: %v", rr.Command(), err) + } + + // Make sure status is back down to 2 hosts rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status")) if err != nil { t.Fatalf("failed to run minikube status. args %q : %v", rr.Command(), err)