functional_test.go 36.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// +build integration

/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package integration

import (
22 23 24 25 26 27 28 29 30
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
31
	"path"
32
	"path/filepath"
33
	"regexp"
T
tstromberg 已提交
34
	"runtime"
35
	"strings"
36
	"testing"
37 38
	"time"

39 40
	"github.com/google/go-cmp/cmp"

M
Medya Gh 已提交
41
	"k8s.io/minikube/pkg/minikube/config"
42
	"k8s.io/minikube/pkg/minikube/localpath"
M
lint  
Medya Gh 已提交
43
	"k8s.io/minikube/pkg/util/retry"
44

45 46
	"github.com/elazarl/goproxy"
	"github.com/hashicorp/go-retryablehttp"
47
	"github.com/otiai10/copy"
48 49 50
	"github.com/phayes/freeport"
	"github.com/pkg/errors"
	"golang.org/x/build/kubernetes/api"
51 52
)

53 54 55
// validateFunc are for subtests that share a single setup
type validateFunc func(context.Context, *testing.T, string)

M
Medya Gh 已提交
56 57 58
// used in validateStartWithProxy and validateSoftStart
var apiPortTest = 8441

59
// TestFunctional are functionality tests which can safely share a profile in parallel
N
Nick Kubala 已提交
60
func TestFunctional(t *testing.T) {
61 62

	profile := UniqueProfileName("functional")
M
Medya Gh 已提交
63
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(40))
64
	defer func() {
65 66 67
		if !*cleanup {
			return
		}
68 69
		p := localSyncTestPath()
		if err := os.Remove(p); err != nil {
70
			t.Logf("unable to remove %q: %v", p, err)
71
		}
72

73
		Cleanup(t, profile, cancel)
74
	}()
75 76 77 78 79 80 81

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
		}{
82 83
			{"CopySyncFile", setupFileSync},                 // Set file for the file sync test case
			{"StartWithProxy", validateStartWithProxy},      // Set everything else up for success
M
Medya Gh 已提交
84
			{"SoftStart", validateSoftStart},                // do a soft start. ensure config didnt change.
85 86 87 88
			{"KubeContext", validateKubeContext},            // Racy: must come immediately after "minikube start"
			{"KubectlGetPods", validateKubectlGetPods},      // Make sure apiserver is up
			{"CacheCmd", validateCacheCmd},                  // Caches images needed for subsequent tests because of proxy
			{"MinikubeKubectlCmd", validateMinikubeKubectl}, // Make sure `minikube kubectl` works
89 90 91 92
		}
		for _, tc := range tests {
			tc := tc
			t.Run(tc.name, func(t *testing.T) {
M
lint  
Medya Gh 已提交
93
				tc.validator(ctx, t, profile)
94
			})
M
Medya Gh 已提交
95
		}
96
	})
M
Medya Gh 已提交
97

98 99 100 101 102 103 104 105 106 107 108 109 110
	// Now that we are out of the woods, lets go.
	MaybeParallel(t)

	// Parallelized tests
	t.Run("parallel", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
		}{
			{"ComponentHealth", validateComponentHealth},
			{"ConfigCmd", validateConfigCmd},
			{"DashboardCmd", validateDashboardCmd},
			{"DNS", validateDNS},
T
Thomas Stromberg 已提交
111
			{"DryRun", validateDryRun},
J
Josh Woodcock 已提交
112
			{"StatusCmd", validateStatusCmd},
113 114 115
			{"LogsCmd", validateLogsCmd},
			{"MountCmd", validateMountCmd},
			{"ProfileCmd", validateProfileCmd},
116
			{"ServiceCmd", validateServiceCmd},
117
			{"AddonsCmd", validateAddonsCmd},
118 119 120
			{"PersistentVolumeClaim", validatePersistentVolumeClaim},
			{"TunnelCmd", validateTunnelCmd},
			{"SSHCmd", validateSSHCmd},
121
			{"MySQL", validateMySQL},
122
			{"FileSync", validateFileSync},
123
			{"CertSync", validateCertSync},
124
			{"UpdateContextCmd", validateUpdateContextCmd},
125
			{"DockerEnv", validateDockerEnv},
M
Medya Gh 已提交
126
			{"NodeLabels", validateNodeLabels},
127 128 129 130 131 132 133 134
		}
		for _, tc := range tests {
			tc := tc
			t.Run(tc.name, func(t *testing.T) {
				MaybeParallel(t)
				tc.validator(ctx, t, profile)
			})
		}
M
Medya Gh 已提交
135
	})
136 137
}

M
Medya Gh 已提交
138 139
// validateNodeLabels checks if minikube cluster is created with correct kubernetes's node label
func validateNodeLabels(ctx context.Context, t *testing.T, profile string) {
140 141
	defer PostMortemLogs(t, profile)

M
Medya Gh 已提交
142
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "nodes", "--output=go-template", "--template='{{range $k, $v := (index .items 0).metadata.labels}}{{$k}} {{end}}'"))
M
Medya Gh 已提交
143
	if err != nil {
M
Medya Gh 已提交
144
		t.Errorf("failed to 'kubectl get nodes' with args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
145 146 147
	}
	expectedLabels := []string{"minikube.k8s.io/commit", "minikube.k8s.io/version", "minikube.k8s.io/updated_at", "minikube.k8s.io/name"}
	for _, el := range expectedLabels {
M
Medya Gh 已提交
148
		if !strings.Contains(rr.Output(), el) {
M
Medya Gh 已提交
149
			t.Errorf("expected to have label %q in node labels but got : %s", el, rr.Output())
M
Medya Gh 已提交
150 151 152 153
		}
	}
}

154 155
// check functionality of minikube after evaling docker-env
func validateDockerEnv(ctx context.Context, t *testing.T, profile string) {
156 157
	defer PostMortemLogs(t, profile)

M
Medya Gh 已提交
158
	mctx, cancel := context.WithTimeout(ctx, Minutes(1))
159
	defer cancel()
M
Medya Gh 已提交
160 161 162 163 164 165 166 167 168 169
	var rr *RunResult
	var err error
	if runtime.GOOS == "windows" { // golang exec powershell needs some tricks !
		c := exec.CommandContext(mctx, Target(), "-p "+profile+" docker-env | Invoke-Expression ;"+Target()+" status -p "+profile)
		rr, err = Run(t, c, true) // golang exec powershell needs some tricks !
	} else {
		c := exec.CommandContext(mctx, "/bin/bash", "-c", "eval $("+Target()+" -p "+profile+" docker-env) && "+Target()+" status -p "+profile)
		// we should be able to get minikube status with a bash which evaled docker-env
		rr, err = Run(t, c)
	}
170
	if err != nil {
M
Medya Gh 已提交
171
		t.Fatalf("failed to do status after eval-ing docker-env. error: %v", err)
172 173
	}
	if !strings.Contains(rr.Output(), "Running") {
M
Medya Gh 已提交
174
		t.Fatalf("expected status output to include 'Running' after eval docker-env but got: *%s*", rr.Output())
175 176
	}

M
Medya Gh 已提交
177
	mctx, cancel = context.WithTimeout(ctx, Seconds(13))
178 179
	defer cancel()
	// do a eval $(minikube -p profile docker-env) and check if we are point to docker inside minikube
M
Medya Gh 已提交
180 181 182
	if runtime.GOOS == "windows" { // testing docker-env eval in powershell
		c := exec.CommandContext(mctx, Target(), "-p "+profile+" docker-env | Invoke-Expression ; docker images")
		rr, err = Run(t, c, true) // golang exec powershell needs some tricks !
M
Medya Gh 已提交
183
	} else {
M
Medya Gh 已提交
184
		c := exec.CommandContext(mctx, "/bin/bash", "-c", "eval $("+Target()+" -p "+profile+" docker-env) && docker images")
M
Medya Gh 已提交
185 186 187
		rr, err = Run(t, c)
	}

188
	if err != nil {
M
Medya Gh 已提交
189
		t.Fatalf("failed to run minikube docker-env. args %q : %v ", rr.Command(), err)
190 191 192 193
	}

	expectedImgInside := "gcr.io/k8s-minikube/storage-provisioner"
	if !strings.Contains(rr.Output(), expectedImgInside) {
M
Medya Gh 已提交
194
		t.Fatalf("expected 'docker images' to have %q inside minikube. but the output is: *%s*", expectedImgInside, rr.Output())
195 196 197 198
	}

}

M
lint  
Medya Gh 已提交
199
func validateStartWithProxy(ctx context.Context, t *testing.T, profile string) {
200 201
	defer PostMortemLogs(t, profile)

202 203
	srv, err := startHTTPProxy(t)
	if err != nil {
204
		t.Fatalf("failed to set up the test proxy: %s", err)
205
	}
206 207

	// Use more memory so that we may reliably fit MySQL and nginx
M
Medya Gh 已提交
208
	// changing api server so later in soft start we verify it didn't change
209
	startArgs := append([]string{"start", "-p", profile, "--memory=2800", fmt.Sprintf("--apiserver-port=%d", apiPortTest), "--wait=true"}, StartArgs()...)
210
	c := exec.CommandContext(ctx, Target(), startArgs...)
211 212 213 214 215 216
	env := os.Environ()
	env = append(env, fmt.Sprintf("HTTP_PROXY=%s", srv.Addr))
	env = append(env, "NO_PROXY=")
	c.Env = env
	rr, err := Run(t, c)
	if err != nil {
M
Medya Gh 已提交
217
		t.Errorf("failed minikube start. args %q: %v", rr.Command(), err)
218 219 220 221 222 223 224 225 226 227 228 229 230
	}

	want := "Found network options:"
	if !strings.Contains(rr.Stdout.String(), want) {
		t.Errorf("start stdout=%s, want: *%s*", rr.Stdout.String(), want)
	}

	want = "You appear to be using a proxy"
	if !strings.Contains(rr.Stderr.String(), want) {
		t.Errorf("start stderr=%s, want: *%s*", rr.Stderr.String(), want)
	}
}

M
Medya Gh 已提交
231
// validateSoftStart validates that after minikube already started, a "minikube start" should not change the configs.
M
lint  
Medya Gh 已提交
232
func validateSoftStart(ctx context.Context, t *testing.T, profile string) {
233 234
	defer PostMortemLogs(t, profile)

M
Medya Gh 已提交
235
	start := time.Now()
M
Medya Gh 已提交
236
	// the test before this had been start with --apiserver-port=8441
M
lint  
Medya Gh 已提交
237 238 239 240
	beforeCfg, err := config.LoadProfile(profile)
	if err != nil {
		t.Errorf("error reading cluster config before soft start: %v", err)
	}
M
Medya Gh 已提交
241 242
	if beforeCfg.Config.KubernetesConfig.NodePort != apiPortTest {
		t.Errorf("expected cluster config node port before soft start to be %d but got %d", apiPortTest, beforeCfg.Config.KubernetesConfig.NodePort)
M
Medya Gh 已提交
243 244
	}

M
lint  
Medya Gh 已提交
245
	softStartArgs := []string{"start", "-p", profile}
M
Medya Gh 已提交
246
	c := exec.CommandContext(ctx, Target(), softStartArgs...)
M
lint  
Medya Gh 已提交
247
	rr, err := Run(t, c)
M
Medya Gh 已提交
248 249 250
	if err != nil {
		t.Errorf("failed to soft start minikube. args %q: %v", rr.Command(), err)
	}
M
Medya Gh 已提交
251
	t.Logf("soft start took %s for %q cluster.", time.Since(start), profile)
M
Medya Gh 已提交
252

M
lint  
Medya Gh 已提交
253 254 255 256 257
	afterCfg, err := config.LoadProfile(profile)
	if err != nil {
		t.Errorf("error reading cluster config after soft start: %v", err)
	}

M
Medya Gh 已提交
258 259
	if afterCfg.Config.KubernetesConfig.NodePort != apiPortTest {
		t.Errorf("expected node port in the config not change after soft start. exepceted node port to be %d but got %d.", apiPortTest, afterCfg.Config.KubernetesConfig.NodePort)
M
Medya Gh 已提交
260
	}
M
Medya Gh 已提交
261

M
Medya Gh 已提交
262 263
}

264 265
// validateKubeContext asserts that kubectl is properly configured (race-condition prone!)
func validateKubeContext(ctx context.Context, t *testing.T, profile string) {
266 267
	defer PostMortemLogs(t, profile)

268 269
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "config", "current-context"))
	if err != nil {
M
Medya Gh 已提交
270
		t.Errorf("failed to get current-context. args %q : %v", rr.Command(), err)
271 272
	}
	if !strings.Contains(rr.Stdout.String(), profile) {
273
		t.Errorf("expected current-context = %q, but got *%q*", profile, rr.Stdout.String())
274 275 276
	}
}

P
Priya Wadhwa 已提交
277 278
// validateKubectlGetPods asserts that `kubectl get pod -A` returns non-zero content
func validateKubectlGetPods(ctx context.Context, t *testing.T, profile string) {
279 280
	defer PostMortemLogs(t, profile)

281
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "po", "-A"))
P
Priya Wadhwa 已提交
282
	if err != nil {
M
Medya Gh 已提交
283
		t.Errorf("failed to get kubectl pods: args %q : %v", rr.Command(), err)
P
Priya Wadhwa 已提交
284
	}
285
	if rr.Stderr.String() != "" {
286
		t.Errorf("expected stderr to be empty but got *%q*: args %q", rr.Stderr, rr.Command())
287
	}
T
Thomas Stromberg 已提交
288
	if !strings.Contains(rr.Stdout.String(), "kube-system") {
289
		t.Errorf("expected stdout to include *kube-system* but got *%q*. args: %q", rr.Stdout, rr.Command())
P
Priya Wadhwa 已提交
290 291 292
	}
}

293 294
// validateMinikubeKubectl validates that the `minikube kubectl` command returns content
func validateMinikubeKubectl(ctx context.Context, t *testing.T, profile string) {
295 296
	defer PostMortemLogs(t, profile)

297 298
	// Must set the profile so that it knows what version of Kubernetes to use
	kubectlArgs := []string{"-p", profile, "kubectl", "--", "--context", profile, "get", "pods"}
P
Priya Wadhwa 已提交
299
	rr, err := Run(t, exec.CommandContext(ctx, Target(), kubectlArgs...))
300
	if err != nil {
M
Medya Gh 已提交
301
		t.Fatalf("failed to get pods. args %q: %v", rr.Command(), err)
302 303 304
	}
}

305 306
// validateComponentHealth asserts that all Kubernetes components are healthy
func validateComponentHealth(ctx context.Context, t *testing.T, profile string) {
307 308
	defer PostMortemLogs(t, profile)

309 310
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "cs", "-o=json"))
	if err != nil {
M
Medya Gh 已提交
311
		t.Fatalf("failed to get components. args %q: %v", rr.Command(), err)
312 313 314 315
	}
	cs := api.ComponentStatusList{}
	d := json.NewDecoder(bytes.NewReader(rr.Stdout.Bytes()))
	if err := d.Decode(&cs); err != nil {
M
Medya Gh 已提交
316
		t.Fatalf("failed to decode kubectl json output: args %q : %v", rr.Command(), err)
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
	}

	for _, i := range cs.Items {
		status := api.ConditionFalse
		for _, c := range i.Conditions {
			if c.Type != api.ComponentHealthy {
				continue
			}
			status = c.Status
		}
		if status != api.ConditionTrue {
			t.Errorf("unexpected status: %v - item: %+v", status, i)
		}
	}
}

J
Josh Woodcock 已提交
333
func validateStatusCmd(ctx context.Context, t *testing.T, profile string) {
334 335
	defer PostMortemLogs(t, profile)

336
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status"))
J
Josh Woodcock 已提交
337
	if err != nil {
M
Medya Gh 已提交
338
		t.Errorf("failed to run minikube status. args %q : %v", rr.Command(), err)
J
Josh Woodcock 已提交
339 340 341
	}

	// Custom format
342
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-f", "host:{{.Host}},kublet:{{.Kubelet}},apiserver:{{.APIServer}},kubeconfig:{{.Kubeconfig}}"))
J
Josh Woodcock 已提交
343
	if err != nil {
M
Medya Gh 已提交
344
		t.Errorf("failed to run minikube status with custom format: args %q: %v", rr.Command(), err)
J
Josh Woodcock 已提交
345
	}
346 347
	re := `host:([A-z]+),kublet:([A-z]+),apiserver:([A-z]+),kubeconfig:([A-z]+)`
	match, _ := regexp.MatchString(re, rr.Stdout.String())
J
Josh Woodcock 已提交
348
	if !match {
M
Medya Gh 已提交
349
		t.Errorf("failed to match regex %q for minikube status with custom format. args %q. output: %s", re, rr.Command(), rr.Output())
J
Josh Woodcock 已提交
350 351 352
	}

	// Json output
353
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-o", "json"))
J
Josh Woodcock 已提交
354
	if err != nil {
M
Medya Gh 已提交
355
		t.Errorf("failed to run minikube status with json output. args %q : %v", rr.Command(), err)
J
Josh Woodcock 已提交
356 357 358 359
	}
	var jsonObject map[string]interface{}
	err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
	if err != nil {
M
Medya Gh 已提交
360
		t.Errorf("failed to decode json from minikube status. args %q. %v", rr.Command(), err)
J
Josh Woodcock 已提交
361 362
	}
	if _, ok := jsonObject["Host"]; !ok {
M
Medya Gh 已提交
363
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "Host")
J
Josh Woodcock 已提交
364 365
	}
	if _, ok := jsonObject["Kubelet"]; !ok {
M
Medya Gh 已提交
366
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "Kubelet")
J
Josh Woodcock 已提交
367 368
	}
	if _, ok := jsonObject["APIServer"]; !ok {
M
Medya Gh 已提交
369
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "APIServer")
J
Josh Woodcock 已提交
370 371
	}
	if _, ok := jsonObject["Kubeconfig"]; !ok {
M
Medya Gh 已提交
372
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "Kubeconfig")
J
Josh Woodcock 已提交
373 374 375
	}
}

376 377
// validateDashboardCmd asserts that the dashboard command works
func validateDashboardCmd(ctx context.Context, t *testing.T, profile string) {
378 379
	defer PostMortemLogs(t, profile)

380 381 382
	args := []string{"dashboard", "--url", "-p", profile, "--alsologtostderr", "-v=1"}
	ss, err := Start(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
383
		t.Errorf("failed to run minikube dashboard. args %q : %v", args, err)
384 385 386 387 388 389
	}
	defer func() {
		ss.Stop(t)
	}()

	start := time.Now()
M
Medya Gh 已提交
390
	s, err := ReadLineWithTimeout(ss.Stdout, Seconds(300))
391
	if err != nil {
T
tstromberg 已提交
392 393 394 395
		if runtime.GOOS == "windows" {
			t.Skipf("failed to read url within %s: %v\noutput: %q\n", time.Since(start), err, s)
		}
		t.Fatalf("failed to read url within %s: %v\noutput: %q\n", time.Since(start), err, s)
396 397 398 399 400 401 402 403 404
	}

	u, err := url.Parse(strings.TrimSpace(s))
	if err != nil {
		t.Fatalf("failed to parse %q: %v", s, err)
	}

	resp, err := retryablehttp.Get(u.String())
	if err != nil {
405
		t.Fatalf("failed to http get %q: %v\nresponse: %+v", u.String(), err, resp)
406
	}
407

408 409 410
	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
411
			t.Errorf("failed to read http response body from dashboard %q: %v", u.String(), err)
412 413 414 415
		}
		t.Errorf("%s returned status code %d, expected %d.\nbody:\n%s", u, resp.StatusCode, http.StatusOK, body)
	}
}
M
Medya Gh 已提交
416

417 418
// validateDNS asserts that all Kubernetes DNS is healthy
func validateDNS(ctx context.Context, t *testing.T, profile string) {
419 420
	defer PostMortemLogs(t, profile)

421 422
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "replace", "--force", "-f", filepath.Join(*testdataDir, "busybox.yaml")))
	if err != nil {
M
Medya Gh 已提交
423
		t.Fatalf("failed to kubectl replace busybox : args %q: %v", rr.Command(), err)
424 425
	}

M
typo  
Medya Gh 已提交
426
	names, err := PodWait(ctx, t, profile, "default", "integration-test=busybox", Minutes(4))
427
	if err != nil {
428
		t.Fatalf("failed waiting for busybox pod : %v", err)
429 430
	}

431 432 433 434 435 436
	nslookup := func() error {
		rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "exec", names[0], "nslookup", "kubernetes.default"))
		return err
	}

	// If the coredns process was stable, this retry wouldn't be necessary.
437
	if err = retry.Expo(nslookup, 1*time.Second, Minutes(1)); err != nil {
438
		t.Errorf("failed to do nslookup on kubernetes.default: %v", err)
439 440 441 442
	}

	want := []byte("10.96.0.1")
	if !bytes.Contains(rr.Stdout.Bytes(), want) {
443
		t.Errorf("failed nslookup: got=%q, want=*%q*", rr.Stdout.Bytes(), want)
444 445 446
	}
}

T
Thomas Stromberg 已提交
447 448
// validateDryRun asserts that the dry-run mode quickly exits with the right code
func validateDryRun(ctx context.Context, t *testing.T, profile string) {
449
	// dry-run mode should always be able to finish quickly (<5s)
M
Medya Gh 已提交
450
	mctx, cancel := context.WithTimeout(ctx, Seconds(5))
T
Thomas Stromberg 已提交
451 452 453
	defer cancel()

	// Too little memory!
454
	startArgs := append([]string{"start", "-p", profile, "--dry-run", "--memory", "250MB", "--alsologtostderr"}, StartArgs()...)
T
Thomas Stromberg 已提交
455 456 457 458 459 460 461 462
	c := exec.CommandContext(mctx, Target(), startArgs...)
	rr, err := Run(t, c)

	wantCode := 78 // exit.Config
	if rr.ExitCode != wantCode {
		t.Errorf("dry-run(250MB) exit code = %d, wanted = %d: %v", rr.ExitCode, wantCode, err)
	}

M
Medya Gh 已提交
463
	dctx, cancel := context.WithTimeout(ctx, Seconds(5))
T
Thomas Stromberg 已提交
464
	defer cancel()
465
	startArgs = append([]string{"start", "-p", profile, "--dry-run", "--alsologtostderr", "-v=1"}, StartArgs()...)
T
Thomas Stromberg 已提交
466 467 468 469 470 471 472
	c = exec.CommandContext(dctx, Target(), startArgs...)
	rr, err = Run(t, c)
	if rr.ExitCode != 0 || err != nil {
		t.Errorf("dry-run exit code = %d, wanted = %d: %v", rr.ExitCode, 0, err)
	}
}

M
Medya Gh 已提交
473
// validateCacheCmd tests functionality of cache command (cache add, delete, list)
474
func validateCacheCmd(ctx context.Context, t *testing.T, profile string) {
475 476
	defer PostMortemLogs(t, profile)

477 478 479
	if NoneDriver() {
		t.Skipf("skipping: cache unsupported by none")
	}
M
Medya Gh 已提交
480 481
	t.Run("cache", func(t *testing.T) {
		t.Run("add", func(t *testing.T) {
482
			for _, img := range []string{"busybox:latest", "busybox:1.28.4-glibc", "k8s.gcr.io/pause:latest"} {
483
				rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cache", "add", img))
M
Medya Gh 已提交
484
				if err != nil {
M
Medya Gh 已提交
485
					t.Errorf("failed to cache add image %q. args %q err %v", img, rr.Command(), err)
M
Medya Gh 已提交
486 487 488
				}
			}
		})
M
Medya Ghazizadeh 已提交
489
		t.Run("delete_busybox:1.28.4-glibc", func(t *testing.T) {
490
			rr, err := Run(t, exec.CommandContext(ctx, Target(), "cache", "delete", "busybox:1.28.4-glibc"))
M
Medya Gh 已提交
491
			if err != nil {
M
Medya Gh 已提交
492
				t.Errorf("failed to delete image busybox:1.28.4-glibc from cache. args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
493 494
			}
		})
M
Medya Gh 已提交
495

M
Medya Gh 已提交
496 497 498
		t.Run("list", func(t *testing.T) {
			rr, err := Run(t, exec.CommandContext(ctx, Target(), "cache", "list"))
			if err != nil {
M
Medya Gh 已提交
499
				t.Errorf("failed to do cache list. args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
500 501
			}
			if !strings.Contains(rr.Output(), "k8s.gcr.io/pause") {
M
Medya Gh 已提交
502
				t.Errorf("expected 'cache list' output to include 'k8s.gcr.io/pause' but got:\n ***%s***", rr.Output())
M
Medya Gh 已提交
503 504
			}
			if strings.Contains(rr.Output(), "busybox:1.28.4-glibc") {
M
Medya Gh 已提交
505
				t.Errorf("expected 'cache list' output not to include busybox:1.28.4-glibc but got:\n ***%s***", rr.Output())
M
Medya Gh 已提交
506 507
			}
		})
M
Medya Gh 已提交
508

M
Medya Ghazizadeh 已提交
509
		t.Run("verify_cache_inside_node", func(t *testing.T) {
M
Medya Gh 已提交
510
			rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo", "crictl", "images"))
M
Medya Gh 已提交
511
			if err != nil {
M
Medya Gh 已提交
512
				t.Errorf("failed to get images by %q ssh %v", rr.Command(), err)
M
Medya Gh 已提交
513
			}
M
Medya Gh 已提交
514
			if !strings.Contains(rr.Output(), "1.28.4-glibc") {
M
Medya Gh 已提交
515
				t.Errorf("expected '1.28.4-glibc' to be in the output but got *%s*", rr.Output())
M
Medya Gh 已提交
516 517 518
			}

		})
M
Medya Gh 已提交
519

M
Medya Ghazizadeh 已提交
520
		t.Run("cache_reload", func(t *testing.T) { // deleting image inside minikube node manually and expecting reload to bring it back
M
Medya Gh 已提交
521 522
			img := "busybox:latest"
			// deleting image inside minikube node manually
M
Medya Gh 已提交
523
			rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo", "docker", "rmi", img)) // for some reason crictl rmi doesn't work
M
Medya Gh 已提交
524 525 526 527 528 529
			if err != nil {
				t.Errorf("failed to delete inside the node %q : %v", rr.Command(), err)
			}
			// make sure the image is deleted.
			rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo", "crictl", "inspecti", img))
			if err == nil {
530
				t.Errorf("expected an error. because image should not exist. but got *nil error* ! cmd: %q", rr.Command())
M
Medya Gh 已提交
531
			}
M
Medya Gh 已提交
532
			// minikube cache reload.
M
Medya Gh 已提交
533 534
			rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cache", "reload"))
			if err != nil {
535
				t.Errorf("expected %q to run successfully but got error: %v", rr.Command(), err)
M
Medya Gh 已提交
536
			}
M
Medya Gh 已提交
537
			// make sure 'cache reload' brought back the manually deleted image.
M
Medya Gh 已提交
538 539
			rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo", "crictl", "inspecti", img))
			if err != nil {
540
				t.Errorf("expected %q to run successfully but got error: %v", rr.Command(), err)
M
Medya Gh 已提交
541 542 543
			}
		})

M
Medya Gh 已提交
544
	})
545 546 547 548 549 550 551 552 553 554 555
}

// validateConfigCmd asserts basic "config" command functionality
func validateConfigCmd(ctx context.Context, t *testing.T, profile string) {
	tests := []struct {
		args    []string
		wantOut string
		wantErr string
	}{
		{[]string{"unset", "cpus"}, "", ""},
		{[]string{"get", "cpus"}, "", "Error: specified key could not be found in config"},
T
Thomas Stromberg 已提交
556
		{[]string{"set", "cpus", "2"}, "", "! These changes will take effect upon a minikube delete and then a minikube start"},
557 558 559 560 561 562 563 564 565
		{[]string{"get", "cpus"}, "2", ""},
		{[]string{"unset", "cpus"}, "", ""},
		{[]string{"get", "cpus"}, "", "Error: specified key could not be found in config"},
	}

	for _, tc := range tests {
		args := append([]string{"-p", profile, "config"}, tc.args...)
		rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
		if err != nil && tc.wantErr == "" {
M
Medya Gh 已提交
566
			t.Errorf("failed to config minikube. args %q : %v", rr.Command(), err)
567 568 569 570
		}

		got := strings.TrimSpace(rr.Stdout.String())
		if got != tc.wantOut {
571
			t.Errorf("expected config output for %q to be -%q- but got *%q*", rr.Command(), tc.wantOut, got)
572 573 574
		}
		got = strings.TrimSpace(rr.Stderr.String())
		if got != tc.wantErr {
575
			t.Errorf("expected config error for %q to be -%q- but got *%q*", rr.Command(), tc.wantErr, got)
576 577 578 579 580 581 582 583
		}
	}
}

// validateLogsCmd asserts basic "logs" command functionality
func validateLogsCmd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "logs"))
	if err != nil {
M
Medya Gh 已提交
584
		t.Errorf("%s failed: %v", rr.Command(), err)
585 586 587
	}
	for _, word := range []string{"Docker", "apiserver", "Linux", "kubelet"} {
		if !strings.Contains(rr.Stdout.String(), word) {
M
Medya Gh 已提交
588
			t.Errorf("excpeted minikube logs to include word: -%q- but got \n***%s***\n", word, rr.Output())
589 590 591 592
		}
	}
}

J
Josh Woodcock 已提交
593
// validateProfileCmd asserts "profile" command functionality
594
func validateProfileCmd(ctx context.Context, t *testing.T, profile string) {
M
Medya Gh 已提交
595 596 597 598 599
	t.Run("profile_not_create", func(t *testing.T) {
		// Profile command should not create a nonexistent profile
		nonexistentProfile := "lis"
		rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", nonexistentProfile))
		if err != nil {
M
Medya Gh 已提交
600
			t.Errorf("%s failed: %v", rr.Command(), err)
M
Medya Gh 已提交
601 602 603
		}
		rr, err = Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json"))
		if err != nil {
M
Medya Gh 已提交
604
			t.Errorf("%s failed: %v", rr.Command(), err)
M
Medya Gh 已提交
605 606 607 608
		}
		var profileJSON map[string][]map[string]interface{}
		err = json.Unmarshal(rr.Stdout.Bytes(), &profileJSON)
		if err != nil {
M
Medya Gh 已提交
609
			t.Errorf("%s failed: %v", rr.Command(), err)
M
Medya Gh 已提交
610 611 612 613 614 615 616
		}
		for profileK := range profileJSON {
			for _, p := range profileJSON[profileK] {
				var name = p["Name"]
				if name == nonexistentProfile {
					t.Errorf("minikube profile %s should not exist", nonexistentProfile)
				}
617
			}
618
		}
M
Medya Gh 已提交
619
	})
620

M
Medya Gh 已提交
621 622
	t.Run("profile_list", func(t *testing.T) {
		// List profiles
M
lint  
Medya Gh 已提交
623
		rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list"))
M
Medya Gh 已提交
624
		if err != nil {
M
Medya Gh 已提交
625
			t.Errorf("failed to list profiles: args %q : %v", rr.Command(), err)
M
Medya Gh 已提交
626
		}
J
Josh Woodcock 已提交
627

M
Medya Gh 已提交
628 629 630 631 632 633 634 635 636 637 638
		// Table output
		listLines := strings.Split(strings.TrimSpace(rr.Stdout.String()), "\n")
		profileExists := false
		for i := 3; i < (len(listLines) - 1); i++ {
			profileLine := listLines[i]
			if strings.Contains(profileLine, profile) {
				profileExists = true
				break
			}
		}
		if !profileExists {
M
Medya Gh 已提交
639
			t.Errorf("expected 'profile list' output to include %q but got *%q*. args: %q", profile, rr.Stdout.String(), rr.Command())
J
Josh Woodcock 已提交
640
		}
M
Medya Gh 已提交
641 642 643 644
	})

	t.Run("profile_json_output", func(t *testing.T) {
		// Json output
M
lint  
Medya Gh 已提交
645
		rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json"))
M
Medya Gh 已提交
646
		if err != nil {
M
Medya Gh 已提交
647
			t.Errorf("failed to list profiles with json format. args %q: %v", rr.Command(), err)
J
Josh Woodcock 已提交
648
		}
M
Medya Gh 已提交
649 650 651
		var jsonObject map[string][]map[string]interface{}
		err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
		if err != nil {
M
Medya Gh 已提交
652
			t.Errorf("failed to decode json from profile list: args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
653 654
		}
		validProfiles := jsonObject["valid"]
M
lint  
Medya Gh 已提交
655
		profileExists := false
M
Medya Gh 已提交
656 657 658 659 660 661 662
		for _, profileObject := range validProfiles {
			if profileObject["Name"] == profile {
				profileExists = true
				break
			}
		}
		if !profileExists {
M
Medya Gh 已提交
663
			t.Errorf("expected the json of 'profile list' to include %q but got *%q*. args: %q", profile, rr.Stdout.String(), rr.Command())
M
Medya Gh 已提交
664 665 666
		}

	})
667 668 669
}

// validateServiceCmd asserts basic "service" command functionality
670
func validateServiceCmd(ctx context.Context, t *testing.T, profile string) {
671 672
	defer PostMortemLogs(t, profile)

673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
	defer func() {
		if t.Failed() {
			t.Logf("service test failed - dumping debug information")

			rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "describe", "po", "hello-node"))
			if err != nil {
				t.Logf("%q failed: %v", rr.Command(), err)
			}
			t.Logf("hello-node pod describe:\n%s", rr.Stdout)

			rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "logs", "-l", "app=hello-node"))
			if err != nil {
				t.Logf("%q failed: %v", rr.Command(), err)
			}
			t.Logf("hello-node logs:\n%s", rr.Stdout)

			rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "describe", "svc", "hello-node"))
			if err != nil {
				t.Logf("%q failed: %v", rr.Command(), err)
			}
			t.Logf("hello-node svc describe:\n%s", rr.Stdout)
		}
	}()

697
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "create", "deployment", "hello-node", "--image=k8s.gcr.io/echoserver:1.4"))
698
	if err != nil {
M
Medya Gh 已提交
699
		t.Logf("%q failed: %v (may not be an error).", rr.Command(), err)
700
	}
701
	rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "expose", "deployment", "hello-node", "--type=NodePort", "--port=8080"))
702
	if err != nil {
M
Medya Gh 已提交
703
		t.Logf("%q failed: %v (may not be an error)", rr.Command(), err)
704 705
	}

706
	if _, err := PodWait(ctx, t, profile, "default", "app=hello-node", Minutes(10)); err != nil {
707
		t.Fatalf("failed waiting for hello-node pod: %v", err)
708 709
	}

710
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "list"))
711
	if err != nil {
M
Medya Gh 已提交
712
		t.Errorf("failed to do service list. args %q : %v", rr.Command(), err)
713
	}
714
	if !strings.Contains(rr.Stdout.String(), "hello-node") {
715
		t.Errorf("expected 'service list' to contain *hello-node* but got -%q-", rr.Stdout.String())
716 717
	}

718 719 720 721
	if NeedsPortForward() {
		t.Skipf("test is broken for port-forwarded drivers: https://github.com/kubernetes/minikube/issues/7383")
	}

T
Thomas Stromberg 已提交
722
	// Test --https --url mode
723
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "--namespace=default", "--https", "--url", "hello-node"))
724
	if err != nil {
M
Medya Gh 已提交
725
		t.Fatalf("failed to get service url. args %q : %v", rr.Command(), err)
726 727
	}
	if rr.Stderr.String() != "" {
M
Medya Gh 已提交
728
		t.Errorf("expected stderr to be empty but got *%q* . args %q", rr.Stderr, rr.Command())
729
	}
730

T
Thomas Stromberg 已提交
731
	endpoint := strings.TrimSpace(rr.Stdout.String())
732
	t.Logf("found endpoint: %s", endpoint)
T
Thomas Stromberg 已提交
733

734 735
	u, err := url.Parse(endpoint)
	if err != nil {
736
		t.Fatalf("failed to parse service url endpoint %q: %v", endpoint, err)
737 738
	}
	if u.Scheme != "https" {
739
		t.Errorf("expected scheme for %s to be 'https' but got %q", endpoint, u.Scheme)
740 741 742
	}

	// Test --format=IP
743
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "hello-node", "--url", "--format={{.IP}}"))
744
	if err != nil {
M
Medya Gh 已提交
745
		t.Errorf("failed to get service url with custom format. args %q: %v", rr.Command(), err)
746
	}
747
	if strings.TrimSpace(rr.Stdout.String()) != u.Hostname() {
M
Medya Gh 已提交
748
		t.Errorf("expected 'service --format={{.IP}}' output to be -%q- but got *%q* . args %q.", u.Hostname(), rr.Stdout.String(), rr.Command())
749 750
	}

751
	// Test a regular URL
752
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "hello-node", "--url"))
753
	if err != nil {
M
Medya Gh 已提交
754
		t.Errorf("failed to get service url. args: %q: %v", rr.Command(), err)
755
	}
756 757

	endpoint = strings.TrimSpace(rr.Stdout.String())
758 759
	t.Logf("found endpoint for hello-node: %s", endpoint)

760 761 762 763
	u, err = url.Parse(endpoint)
	if err != nil {
		t.Fatalf("failed to parse %q: %v", endpoint, err)
	}
764

765
	if u.Scheme != "http" {
M
lint  
Medya Gh 已提交
766
		t.Fatalf("expected scheme to be -%q- got scheme: *%q*", "http", u.Scheme)
767 768
	}

769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
	t.Logf("Attempting to fetch %s ...", endpoint)

	fetch := func() error {
		resp, err := http.Get(endpoint)
		if err != nil {
			t.Logf("error fetching %s: %v", endpoint, err)
			return err
		}

		defer resp.Body.Close()

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Logf("error reading body from %s: %v", endpoint, err)
			return err
		}
		if resp.StatusCode != http.StatusOK {
			t.Logf("%s: unexpected status code %d - body:\n%s", endpoint, resp.StatusCode, body)
		} else {
			t.Logf("%s: success! body:\n%s", endpoint, body)
		}
		return nil
791
	}
792 793 794

	if err = retry.Expo(fetch, 1*time.Second, Seconds(30)); err != nil {
		t.Errorf("failed to fetch %s: %v", endpoint, err)
795 796 797
	}
}

798 799
// validateAddonsCmd asserts basic "addon" command functionality
func validateAddonsCmd(ctx context.Context, t *testing.T, profile string) {
800 801
	defer PostMortemLogs(t, profile)

M
Medya Gh 已提交
802
	// Table output
803 804
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list"))
	if err != nil {
M
Medya Gh 已提交
805
		t.Errorf("failed to do addon list: args %q : %v", rr.Command(), err)
806
	}
M
Medya Gh 已提交
807 808
	for _, a := range []string{"dashboard", "ingress", "ingress-dns"} {
		if !strings.Contains(rr.Output(), a) {
M
Medya Gh 已提交
809
			t.Errorf("expected 'addon list' output to include -%q- but got *%s*", a, rr.Output())
810 811
		}
	}
J
Josh Woodcock 已提交
812 813 814 815

	// Json output
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list", "-o", "json"))
	if err != nil {
M
Medya Gh 已提交
816
		t.Errorf("failed to do addon list with json output. args %q: %v", rr.Command(), err)
J
Josh Woodcock 已提交
817 818 819 820
	}
	var jsonObject map[string]interface{}
	err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
	if err != nil {
821
		t.Errorf("failed to decode addon list json output : %v", err)
J
Josh Woodcock 已提交
822
	}
823 824
}

825 826
// validateSSHCmd asserts basic "ssh" command functionality
func validateSSHCmd(ctx context.Context, t *testing.T, profile string) {
827 828
	defer PostMortemLogs(t, profile)

829 830 831
	if NoneDriver() {
		t.Skipf("skipping: ssh unsupported by none")
	}
P
Priya Wadhwa 已提交
832
	want := "hello\n"
833 834
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", fmt.Sprintf("echo hello")))
	if err != nil {
M
Medya Gh 已提交
835
		t.Errorf("failed to run an ssh command. args %q : %v", rr.Command(), err)
836 837
	}
	if rr.Stdout.String() != want {
M
Medya Gh 已提交
838
		t.Errorf("expected minikube ssh command output to be -%q- but got *%q*. args %q", want, rr.Stdout.String(), rr.Command())
839 840 841
	}
}

842 843
// validateMySQL validates a minimalist MySQL deployment
func validateMySQL(ctx context.Context, t *testing.T, profile string) {
844 845
	defer PostMortemLogs(t, profile)

846 847
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "replace", "--force", "-f", filepath.Join(*testdataDir, "mysql.yaml")))
	if err != nil {
M
Medya Gh 已提交
848
		t.Fatalf("failed to kubectl replace mysql: args %q failed: %v", rr.Command(), err)
849 850
	}

851
	names, err := PodWait(ctx, t, profile, "default", "app=mysql", Minutes(10))
852
	if err != nil {
853
		t.Fatalf("failed waiting for mysql pod: %v", err)
854 855
	}

856 857 858 859
	// Retry, as mysqld first comes up without users configured. Scan for names in case of a reschedule.
	mysql := func() error {
		rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "exec", names[0], "--", "mysql", "-ppassword", "-e", "show databases;"))
		return err
860
	}
M
Medya Gh 已提交
861
	if err = retry.Expo(mysql, 1*time.Second, Minutes(5)); err != nil {
862
		t.Errorf("failed to exec 'mysql -ppassword -e show databases;': %v", err)
863 864 865
	}
}

866 867 868 869 870 871 872 873 874 875
// vmSyncTestPath is where the test file will be synced into the VM
func vmSyncTestPath() string {
	return fmt.Sprintf("/etc/test/nested/copy/%d/hosts", os.Getpid())
}

// localSyncTestPath is where the test file will be synced into the VM
func localSyncTestPath() string {
	return filepath.Join(localpath.MiniPath(), "/files", vmSyncTestPath())
}

876 877 878 879 880 881 882 883 884 885
// testCert is name of the test certificate installed
func testCert() string {
	return fmt.Sprintf("%d.pem", os.Getpid())
}

// localTestCertPath is where the test file will be synced into the VM
func localTestCertPath() string {
	return filepath.Join(localpath.MiniPath(), "/certs", testCert())
}

886 887 888 889 890
// localEmptyCertPath is where the test file will be synced into the VM
func localEmptyCertPath() string {
	return filepath.Join(localpath.MiniPath(), "/certs", fmt.Sprintf("%d_empty.pem", os.Getpid()))
}

891 892
// Copy extra file into minikube home folder for file sync test
func setupFileSync(ctx context.Context, t *testing.T, profile string) {
893 894 895
	p := localSyncTestPath()
	t.Logf("local sync path: %s", p)
	err := copy.Copy("./testdata/sync.test", p)
896
	if err != nil {
897
		t.Fatalf("failed to copy ./testdata/sync.test: %v", err)
898
	}
899

900 901
	testPem := "./testdata/minikube_test.pem"

902 903 904
	// Write to a temp file for an atomic write
	tmpPem := localTestCertPath() + ".pem"
	if err := copy.Copy(testPem, tmpPem); err != nil {
905 906 907
		t.Fatalf("failed to copy %s: %v", testPem, err)
	}

908 909 910 911
	if err := os.Rename(tmpPem, localTestCertPath()); err != nil {
		t.Fatalf("failed to rename %s: %v", tmpPem, err)
	}

912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
	want, err := os.Stat(testPem)
	if err != nil {
		t.Fatalf("stat failed: %v", err)
	}

	got, err := os.Stat(localTestCertPath())
	if err != nil {
		t.Fatalf("stat failed: %v", err)
	}

	if want.Size() != got.Size() {
		t.Errorf("%s size=%d, want %d", localTestCertPath(), got.Size(), want.Size())
	}

	// Create an empty file just to mess with people
	if _, err := os.Create(localEmptyCertPath()); err != nil {
		t.Fatalf("create failed: %v", err)
929
	}
930 931 932 933
}

// validateFileSync to check existence of the test file
func validateFileSync(ctx context.Context, t *testing.T, profile string) {
934 935
	defer PostMortemLogs(t, profile)

936 937 938
	if NoneDriver() {
		t.Skipf("skipping: ssh unsupported by none")
	}
939 940 941

	vp := vmSyncTestPath()
	t.Logf("Checking for existence of %s within VM", vp)
942
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", fmt.Sprintf("sudo cat %s", vp)))
943
	if err != nil {
M
Medya Gh 已提交
944
		t.Errorf("%s failed: %v", rr.Command(), err)
945
	}
946 947
	got := rr.Stdout.String()
	t.Logf("file sync test content: %s", got)
948 949 950

	expected, err := ioutil.ReadFile("./testdata/sync.test")
	if err != nil {
951
		t.Errorf("failed to read test file '/testdata/sync.test' : %v", err)
952 953
	}

954
	if diff := cmp.Diff(string(expected), got); diff != "" {
955 956 957 958
		t.Errorf("/etc/sync.test content mismatch (-want +got):\n%s", diff)
	}
}

959 960
// validateCertSync to check existence of the test certificate
func validateCertSync(ctx context.Context, t *testing.T, profile string) {
961 962
	defer PostMortemLogs(t, profile)

963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
	if NoneDriver() {
		t.Skipf("skipping: ssh unsupported by none")
	}

	want, err := ioutil.ReadFile("./testdata/minikube_test.pem")
	if err != nil {
		t.Errorf("test file not found: %v", err)
	}

	// Check both the installed & reference certs (they should be symlinked)
	paths := []string{
		path.Join("/etc/ssl/certs", testCert()),
		path.Join("/usr/share/ca-certificates", testCert()),
		// hashed path generated by: 'openssl x509 -hash -noout -in testCert()'
		"/etc/ssl/certs/51391683.0",
	}
	for _, vp := range paths {
		t.Logf("Checking for existence of %s within VM", vp)
981
		rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", fmt.Sprintf("sudo cat %s", vp)))
982
		if err != nil {
M
Medya Gh 已提交
983
			t.Errorf("failed to check existence of %q inside minikube. args %q: %v", vp, rr.Command(), err)
984 985 986 987 988
		}

		// Strip carriage returned by ssh
		got := strings.Replace(rr.Stdout.String(), "\r", "", -1)
		if diff := cmp.Diff(string(want), got); diff != "" {
989
			t.Errorf("failed verify pem file. minikube_test.pem -> %s mismatch (-want +got):\n%s", vp, diff)
990 991 992 993
		}
	}
}

994 995
// validateUpdateContextCmd asserts basic "update-context" command functionality
func validateUpdateContextCmd(ctx context.Context, t *testing.T, profile string) {
996 997
	defer PostMortemLogs(t, profile)

998 999
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "update-context", "--alsologtostderr", "-v=2"))
	if err != nil {
M
Medya Gh 已提交
1000
		t.Errorf("failed to run minikube update-context: args %q: %v", rr.Command(), err)
1001 1002
	}

T
Thomas Stromberg 已提交
1003
	want := []byte("No changes")
1004 1005 1006 1007 1008
	if !bytes.Contains(rr.Stdout.Bytes(), want) {
		t.Errorf("update-context: got=%q, want=*%q*", rr.Stdout.Bytes(), want)
	}
}

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
// startHTTPProxy runs a local http proxy and sets the env vars for it.
func startHTTPProxy(t *testing.T) (*http.Server, error) {
	port, err := freeport.GetFreePort()
	if err != nil {
		return nil, errors.Wrap(err, "Failed to get an open port")
	}

	addr := fmt.Sprintf("localhost:%d", port)
	proxy := goproxy.NewProxyHttpServer()
	srv := &http.Server{Addr: addr, Handler: proxy}
	go func(s *http.Server, t *testing.T) {
		if err := s.ListenAndServe(); err != http.ErrServerClosed {
			t.Errorf("Failed to start http server for proxy mock")
		}
	}(srv, t)
	return srv, nil
1025
}