functional_test.go 33.8 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
		p = localTestCertPath()
		if err := os.Remove(p); err != nil {
74
			t.Logf("unable to remove %q: %v", p, err)
75
		}
76 77 78 79 80
		p = localEmptyCertPath()
		if err := os.Remove(p); err != nil {
			t.Logf("unable to remove %q: %v", p, err)
		}

81 82
		CleanupWithLogs(t, profile, cancel)
	}()
83 84 85 86 87 88 89

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
		}{
90 91
			{"CopySyncFile", setupFileSync},                 // Set file for the file sync test case
			{"StartWithProxy", validateStartWithProxy},      // Set everything else up for success
M
Medya Gh 已提交
92
			{"SoftStart", validateSoftStart},                // do a soft start. ensure config didnt change.
93 94 95 96
			{"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
97 98 99 100
		}
		for _, tc := range tests {
			tc := tc
			t.Run(tc.name, func(t *testing.T) {
M
lint  
Medya Gh 已提交
101
				tc.validator(ctx, t, profile)
102
			})
M
Medya Gh 已提交
103
		}
104
	})
M
Medya Gh 已提交
105

106 107 108 109 110 111 112 113 114 115 116 117 118
	// 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 已提交
119
			{"DryRun", validateDryRun},
J
Josh Woodcock 已提交
120
			{"StatusCmd", validateStatusCmd},
121 122 123
			{"LogsCmd", validateLogsCmd},
			{"MountCmd", validateMountCmd},
			{"ProfileCmd", validateProfileCmd},
124
			{"ServiceCmd", validateServiceCmd},
125
			{"AddonsCmd", validateAddonsCmd},
126 127 128
			{"PersistentVolumeClaim", validatePersistentVolumeClaim},
			{"TunnelCmd", validateTunnelCmd},
			{"SSHCmd", validateSSHCmd},
129
			{"MySQL", validateMySQL},
130
			{"FileSync", validateFileSync},
131
			{"CertSync", validateCertSync},
132
			{"UpdateContextCmd", validateUpdateContextCmd},
133
			{"DockerEnv", validateDockerEnv},
M
Medya Gh 已提交
134
			{"NodeLabels", validateNodeLabels},
135 136 137 138 139 140 141 142
		}
		for _, tc := range tests {
			tc := tc
			t.Run(tc.name, func(t *testing.T) {
				MaybeParallel(t)
				tc.validator(ctx, t, profile)
			})
		}
M
Medya Gh 已提交
143
	})
144 145
}

M
Medya Gh 已提交
146 147
// validateNodeLabels checks if minikube cluster is created with correct kubernetes's node label
func validateNodeLabels(ctx context.Context, t *testing.T, profile string) {
M
Medya Gh 已提交
148
	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 已提交
149
	if err != nil {
M
Medya Gh 已提交
150
		t.Errorf("failed to 'kubectl get nodes' with args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
151 152 153
	}
	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 已提交
154
		if !strings.Contains(rr.Output(), el) {
M
Medya Gh 已提交
155
			t.Errorf("expected to have label %q in node labels but got : %s", el, rr.Output())
M
Medya Gh 已提交
156 157 158 159
		}
	}
}

160 161
// check functionality of minikube after evaling docker-env
func validateDockerEnv(ctx context.Context, t *testing.T, profile string) {
M
Medya Gh 已提交
162
	mctx, cancel := context.WithTimeout(ctx, Seconds(13))
163 164 165 166 167
	defer cancel()
	// we should be able to get minikube status with a bash which evaled docker-env
	c := exec.CommandContext(mctx, "/bin/bash", "-c", "eval $("+Target()+" -p "+profile+" docker-env) && "+Target()+" status -p "+profile)
	rr, err := Run(t, c)
	if err != nil {
168
		t.Fatalf("failed to do minikube status after eval-ing docker-env %s", err)
169 170
	}
	if !strings.Contains(rr.Output(), "Running") {
M
Medya Gh 已提交
171
		t.Fatalf("expected status output to include 'Running' after eval docker-env but got: *%s*", rr.Output())
172 173
	}

M
Medya Gh 已提交
174
	mctx, cancel = context.WithTimeout(ctx, Seconds(13))
175 176 177 178 179
	defer cancel()
	// do a eval $(minikube -p profile docker-env) and check if we are point to docker inside minikube
	c = exec.CommandContext(mctx, "/bin/bash", "-c", "eval $("+Target()+" -p "+profile+" docker-env) && docker images")
	rr, err = Run(t, c)
	if err != nil {
M
Medya Gh 已提交
180
		t.Fatalf("failed to run minikube docker-env. args %q : %v ", rr.Command(), err)
181 182 183 184
	}

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

}

M
lint  
Medya Gh 已提交
190
func validateStartWithProxy(ctx context.Context, t *testing.T, profile string) {
191 192
	srv, err := startHTTPProxy(t)
	if err != nil {
193
		t.Fatalf("failed to set up the test proxy: %s", err)
194
	}
195 196

	// Use more memory so that we may reliably fit MySQL and nginx
M
Medya Gh 已提交
197
	// changing api server so later in soft start we verify it didn't change
198
	startArgs := append([]string{"start", "-p", profile, "--memory=2800", fmt.Sprintf("--apiserver-port=%d", apiPortTest), "--wait=true"}, StartArgs()...)
199
	c := exec.CommandContext(ctx, Target(), startArgs...)
200 201 202 203 204 205
	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 已提交
206
		t.Errorf("failed minikube start. args %q: %v", rr.Command(), err)
207 208 209 210 211 212 213 214 215 216 217 218 219
	}

	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 已提交
220
// validateSoftStart validates that after minikube already started, a "minikube start" should not change the configs.
M
lint  
Medya Gh 已提交
221
func validateSoftStart(ctx context.Context, t *testing.T, profile string) {
M
Medya Gh 已提交
222
	start := time.Now()
M
Medya Gh 已提交
223
	// the test before this had been start with --apiserver-port=8441
M
lint  
Medya Gh 已提交
224 225 226 227
	beforeCfg, err := config.LoadProfile(profile)
	if err != nil {
		t.Errorf("error reading cluster config before soft start: %v", err)
	}
M
Medya Gh 已提交
228 229
	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 已提交
230 231
	}

M
lint  
Medya Gh 已提交
232
	softStartArgs := []string{"start", "-p", profile}
M
Medya Gh 已提交
233
	c := exec.CommandContext(ctx, Target(), softStartArgs...)
M
lint  
Medya Gh 已提交
234
	rr, err := Run(t, c)
M
Medya Gh 已提交
235 236 237
	if err != nil {
		t.Errorf("failed to soft start minikube. args %q: %v", rr.Command(), err)
	}
M
Medya Gh 已提交
238
	t.Logf("soft start took %s for %q cluster.", time.Since(start), profile)
M
Medya Gh 已提交
239

M
lint  
Medya Gh 已提交
240 241 242 243 244
	afterCfg, err := config.LoadProfile(profile)
	if err != nil {
		t.Errorf("error reading cluster config after soft start: %v", err)
	}

M
Medya Gh 已提交
245 246
	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 已提交
247
	}
M
Medya Gh 已提交
248

M
Medya Gh 已提交
249 250
}

251 252 253 254
// validateKubeContext asserts that kubectl is properly configured (race-condition prone!)
func validateKubeContext(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "config", "current-context"))
	if err != nil {
M
Medya Gh 已提交
255
		t.Errorf("failed to get current-context. args %q : %v", rr.Command(), err)
256 257
	}
	if !strings.Contains(rr.Stdout.String(), profile) {
258
		t.Errorf("expected current-context = %q, but got *%q*", profile, rr.Stdout.String())
259 260 261
	}
}

P
Priya Wadhwa 已提交
262 263
// validateKubectlGetPods asserts that `kubectl get pod -A` returns non-zero content
func validateKubectlGetPods(ctx context.Context, t *testing.T, profile string) {
264
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "po", "-A"))
P
Priya Wadhwa 已提交
265
	if err != nil {
M
Medya Gh 已提交
266
		t.Errorf("failed to get kubectl pods: args %q : %v", rr.Command(), err)
P
Priya Wadhwa 已提交
267
	}
268
	if rr.Stderr.String() != "" {
269
		t.Errorf("expected stderr to be empty but got *%q*: args %q", rr.Stderr, rr.Command())
270
	}
T
Thomas Stromberg 已提交
271
	if !strings.Contains(rr.Stdout.String(), "kube-system") {
272
		t.Errorf("expected stdout to include *kube-system* but got *%q*. args: %q", rr.Stdout, rr.Command())
P
Priya Wadhwa 已提交
273 274 275
	}
}

276 277
// validateMinikubeKubectl validates that the `minikube kubectl` command returns content
func validateMinikubeKubectl(ctx context.Context, t *testing.T, profile string) {
278 279
	// 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 已提交
280
	rr, err := Run(t, exec.CommandContext(ctx, Target(), kubectlArgs...))
281
	if err != nil {
M
Medya Gh 已提交
282
		t.Fatalf("failed to get pods. args %q: %v", rr.Command(), err)
283 284 285
	}
}

286 287 288 289
// validateComponentHealth asserts that all Kubernetes components are healthy
func validateComponentHealth(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "cs", "-o=json"))
	if err != nil {
M
Medya Gh 已提交
290
		t.Fatalf("failed to get components. args %q: %v", rr.Command(), err)
291 292 293 294
	}
	cs := api.ComponentStatusList{}
	d := json.NewDecoder(bytes.NewReader(rr.Stdout.Bytes()))
	if err := d.Decode(&cs); err != nil {
M
Medya Gh 已提交
295
		t.Fatalf("failed to decode kubectl json output: args %q : %v", rr.Command(), err)
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
	}

	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 已提交
312
func validateStatusCmd(ctx context.Context, t *testing.T, profile string) {
313
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status"))
J
Josh Woodcock 已提交
314
	if err != nil {
M
Medya Gh 已提交
315
		t.Errorf("failed to run minikube status. args %q : %v", rr.Command(), err)
J
Josh Woodcock 已提交
316 317 318
	}

	// Custom format
319
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-f", "host:{{.Host}},kublet:{{.Kubelet}},apiserver:{{.APIServer}},kubeconfig:{{.Kubeconfig}}"))
J
Josh Woodcock 已提交
320
	if err != nil {
M
Medya Gh 已提交
321
		t.Errorf("failed to run minikube status with custom format: args %q: %v", rr.Command(), err)
J
Josh Woodcock 已提交
322
	}
323 324
	re := `host:([A-z]+),kublet:([A-z]+),apiserver:([A-z]+),kubeconfig:([A-z]+)`
	match, _ := regexp.MatchString(re, rr.Stdout.String())
J
Josh Woodcock 已提交
325
	if !match {
M
Medya Gh 已提交
326
		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 已提交
327 328 329
	}

	// Json output
330
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "status", "-o", "json"))
J
Josh Woodcock 已提交
331
	if err != nil {
M
Medya Gh 已提交
332
		t.Errorf("failed to run minikube status with json output. args %q : %v", rr.Command(), err)
J
Josh Woodcock 已提交
333 334 335 336
	}
	var jsonObject map[string]interface{}
	err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
	if err != nil {
M
Medya Gh 已提交
337
		t.Errorf("failed to decode json from minikube status. args %q. %v", rr.Command(), err)
J
Josh Woodcock 已提交
338 339
	}
	if _, ok := jsonObject["Host"]; !ok {
M
Medya Gh 已提交
340
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "Host")
J
Josh Woodcock 已提交
341 342
	}
	if _, ok := jsonObject["Kubelet"]; !ok {
M
Medya Gh 已提交
343
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "Kubelet")
J
Josh Woodcock 已提交
344 345
	}
	if _, ok := jsonObject["APIServer"]; !ok {
M
Medya Gh 已提交
346
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "APIServer")
J
Josh Woodcock 已提交
347 348
	}
	if _, ok := jsonObject["Kubeconfig"]; !ok {
M
Medya Gh 已提交
349
		t.Errorf("%q failed: %v. Missing key %s in json object", rr.Command(), err, "Kubeconfig")
J
Josh Woodcock 已提交
350 351 352
	}
}

353 354 355 356 357
// validateDashboardCmd asserts that the dashboard command works
func validateDashboardCmd(ctx context.Context, t *testing.T, profile string) {
	args := []string{"dashboard", "--url", "-p", profile, "--alsologtostderr", "-v=1"}
	ss, err := Start(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
358
		t.Errorf("failed to run minikube dashboard. args %q : %v", args, err)
359 360 361 362 363 364
	}
	defer func() {
		ss.Stop(t)
	}()

	start := time.Now()
M
Medya Gh 已提交
365
	s, err := ReadLineWithTimeout(ss.Stdout, Seconds(300))
366
	if err != nil {
T
tstromberg 已提交
367 368 369 370
		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)
371 372 373 374 375 376 377 378 379
	}

	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 {
380
		t.Fatalf("failed to http get %q: %v\nresponse: %+v", u.String(), err, resp)
381
	}
382

383 384 385
	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
386
			t.Errorf("failed to read http response body from dashboard %q: %v", u.String(), err)
387 388 389 390
		}
		t.Errorf("%s returned status code %d, expected %d.\nbody:\n%s", u, resp.StatusCode, http.StatusOK, body)
	}
}
M
Medya Gh 已提交
391

392 393 394 395
// validateDNS asserts that all Kubernetes DNS is healthy
func validateDNS(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "replace", "--force", "-f", filepath.Join(*testdataDir, "busybox.yaml")))
	if err != nil {
M
Medya Gh 已提交
396
		t.Fatalf("failed to kubectl replace busybox : args %q: %v", rr.Command(), err)
397 398
	}

M
typo  
Medya Gh 已提交
399
	names, err := PodWait(ctx, t, profile, "default", "integration-test=busybox", Minutes(4))
400
	if err != nil {
401
		t.Fatalf("failed waiting for busybox pod : %v", err)
402 403
	}

404 405 406 407 408 409
	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.
410
	if err = retry.Expo(nslookup, 1*time.Second, Minutes(1)); err != nil {
411
		t.Errorf("failed to do nslookup on kubernetes.default: %v", err)
412 413 414 415
	}

	want := []byte("10.96.0.1")
	if !bytes.Contains(rr.Stdout.Bytes(), want) {
416
		t.Errorf("failed nslookup: got=%q, want=*%q*", rr.Stdout.Bytes(), want)
417 418 419
	}
}

T
Thomas Stromberg 已提交
420 421
// validateDryRun asserts that the dry-run mode quickly exits with the right code
func validateDryRun(ctx context.Context, t *testing.T, profile string) {
422
	// dry-run mode should always be able to finish quickly (<5s)
M
Medya Gh 已提交
423
	mctx, cancel := context.WithTimeout(ctx, Seconds(5))
T
Thomas Stromberg 已提交
424 425 426
	defer cancel()

	// Too little memory!
427
	startArgs := append([]string{"start", "-p", profile, "--dry-run", "--memory", "250MB", "--alsologtostderr", "-v=1"}, StartArgs()...)
T
Thomas Stromberg 已提交
428 429 430 431 432 433 434 435
	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 已提交
436
	dctx, cancel := context.WithTimeout(ctx, Seconds(5))
T
Thomas Stromberg 已提交
437
	defer cancel()
438
	startArgs = append([]string{"start", "-p", profile, "--dry-run", "--alsologtostderr", "-v=1"}, StartArgs()...)
T
Thomas Stromberg 已提交
439 440 441 442 443 444 445
	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 已提交
446
// validateCacheCmd tests functionality of cache command (cache add, delete, list)
447 448 449 450
func validateCacheCmd(ctx context.Context, t *testing.T, profile string) {
	if NoneDriver() {
		t.Skipf("skipping: cache unsupported by none")
	}
M
Medya Gh 已提交
451 452
	t.Run("cache", func(t *testing.T) {
		t.Run("add", func(t *testing.T) {
453
			for _, img := range []string{"busybox:latest", "busybox:1.28.4-glibc", "k8s.gcr.io/pause:latest"} {
454
				rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cache", "add", img))
M
Medya Gh 已提交
455
				if err != nil {
M
Medya Gh 已提交
456
					t.Errorf("failed to cache add image %q. args %q err %v", img, rr.Command(), err)
M
Medya Gh 已提交
457 458 459
				}
			}
		})
M
Medya Ghazizadeh 已提交
460
		t.Run("delete_busybox:1.28.4-glibc", func(t *testing.T) {
461
			rr, err := Run(t, exec.CommandContext(ctx, Target(), "cache", "delete", "busybox:1.28.4-glibc"))
M
Medya Gh 已提交
462
			if err != nil {
M
Medya Gh 已提交
463
				t.Errorf("failed to delete image busybox:1.28.4-glibc from cache. args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
464 465
			}
		})
M
Medya Gh 已提交
466

M
Medya Gh 已提交
467 468 469
		t.Run("list", func(t *testing.T) {
			rr, err := Run(t, exec.CommandContext(ctx, Target(), "cache", "list"))
			if err != nil {
M
Medya Gh 已提交
470
				t.Errorf("failed to do cache list. args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
471 472
			}
			if !strings.Contains(rr.Output(), "k8s.gcr.io/pause") {
M
Medya Gh 已提交
473
				t.Errorf("expected 'cache list' output to include 'k8s.gcr.io/pause' but got:\n ***%s***", rr.Output())
M
Medya Gh 已提交
474 475
			}
			if strings.Contains(rr.Output(), "busybox:1.28.4-glibc") {
M
Medya Gh 已提交
476
				t.Errorf("expected 'cache list' output not to include busybox:1.28.4-glibc but got:\n ***%s***", rr.Output())
M
Medya Gh 已提交
477 478
			}
		})
M
Medya Gh 已提交
479

M
Medya Ghazizadeh 已提交
480
		t.Run("verify_cache_inside_node", func(t *testing.T) {
M
Medya Gh 已提交
481
			rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo", "crictl", "images"))
M
Medya Gh 已提交
482
			if err != nil {
M
Medya Gh 已提交
483
				t.Errorf("failed to get images by %q ssh %v", rr.Command(), err)
M
Medya Gh 已提交
484
			}
M
Medya Gh 已提交
485
			if !strings.Contains(rr.Output(), "1.28.4-glibc") {
M
Medya Gh 已提交
486
				t.Errorf("expected '1.28.4-glibc' to be in the output but got *%s*", rr.Output())
M
Medya Gh 已提交
487 488 489
			}

		})
M
Medya Gh 已提交
490

M
Medya Ghazizadeh 已提交
491
		t.Run("cache_reload", func(t *testing.T) { // deleting image inside minikube node manually and expecting reload to bring it back
M
Medya Gh 已提交
492 493
			img := "busybox:latest"
			// deleting image inside minikube node manually
M
Medya Gh 已提交
494
			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 已提交
495 496 497 498 499 500
			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 {
501
				t.Errorf("expected an error. because image should not exist. but got *nil error* ! cmd: %q", rr.Command())
M
Medya Gh 已提交
502
			}
M
Medya Gh 已提交
503
			// minikube cache reload.
M
Medya Gh 已提交
504 505
			rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cache", "reload"))
			if err != nil {
506
				t.Errorf("expected %q to run successfully but got error: %v", rr.Command(), err)
M
Medya Gh 已提交
507
			}
M
Medya Gh 已提交
508
			// make sure 'cache reload' brought back the manually deleted image.
M
Medya Gh 已提交
509 510
			rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "sudo", "crictl", "inspecti", img))
			if err != nil {
511
				t.Errorf("expected %q to run successfully but got error: %v", rr.Command(), err)
M
Medya Gh 已提交
512 513 514
			}
		})

M
Medya Gh 已提交
515
	})
516 517 518 519 520 521 522 523 524 525 526
}

// 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 已提交
527
		{[]string{"set", "cpus", "2"}, "", "! These changes will take effect upon a minikube delete and then a minikube start"},
528 529 530 531 532 533 534 535 536
		{[]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 已提交
537
			t.Errorf("failed to config minikube. args %q : %v", rr.Command(), err)
538 539 540 541
		}

		got := strings.TrimSpace(rr.Stdout.String())
		if got != tc.wantOut {
542
			t.Errorf("expected config output for %q to be -%q- but got *%q*", rr.Command(), tc.wantOut, got)
543 544 545
		}
		got = strings.TrimSpace(rr.Stderr.String())
		if got != tc.wantErr {
546
			t.Errorf("expected config error for %q to be -%q- but got *%q*", rr.Command(), tc.wantErr, got)
547 548 549 550 551 552 553 554
		}
	}
}

// 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 已提交
555
		t.Errorf("%s failed: %v", rr.Command(), err)
556 557 558
	}
	for _, word := range []string{"Docker", "apiserver", "Linux", "kubelet"} {
		if !strings.Contains(rr.Stdout.String(), word) {
M
Medya Gh 已提交
559
			t.Errorf("excpeted minikube logs to include word: -%q- but got \n***%s***\n", word, rr.Output())
560 561 562 563
		}
	}
}

J
Josh Woodcock 已提交
564
// validateProfileCmd asserts "profile" command functionality
565
func validateProfileCmd(ctx context.Context, t *testing.T, profile string) {
M
Medya Gh 已提交
566 567 568 569 570
	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 已提交
571
			t.Errorf("%s failed: %v", rr.Command(), err)
M
Medya Gh 已提交
572 573 574
		}
		rr, err = Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json"))
		if err != nil {
M
Medya Gh 已提交
575
			t.Errorf("%s failed: %v", rr.Command(), err)
M
Medya Gh 已提交
576 577 578 579
		}
		var profileJSON map[string][]map[string]interface{}
		err = json.Unmarshal(rr.Stdout.Bytes(), &profileJSON)
		if err != nil {
M
Medya Gh 已提交
580
			t.Errorf("%s failed: %v", rr.Command(), err)
M
Medya Gh 已提交
581 582 583 584 585 586 587
		}
		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)
				}
588
			}
589
		}
M
Medya Gh 已提交
590
	})
591

M
Medya Gh 已提交
592 593
	t.Run("profile_list", func(t *testing.T) {
		// List profiles
M
lint  
Medya Gh 已提交
594
		rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list"))
M
Medya Gh 已提交
595
		if err != nil {
M
Medya Gh 已提交
596
			t.Errorf("failed to list profiles: args %q : %v", rr.Command(), err)
M
Medya Gh 已提交
597
		}
J
Josh Woodcock 已提交
598

M
Medya Gh 已提交
599 600 601 602 603 604 605 606 607 608 609
		// 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 已提交
610
			t.Errorf("expected 'profile list' output to include %q but got *%q*. args: %q", profile, rr.Stdout.String(), rr.Command())
J
Josh Woodcock 已提交
611
		}
M
Medya Gh 已提交
612 613 614 615
	})

	t.Run("profile_json_output", func(t *testing.T) {
		// Json output
M
lint  
Medya Gh 已提交
616
		rr, err := Run(t, exec.CommandContext(ctx, Target(), "profile", "list", "--output", "json"))
M
Medya Gh 已提交
617
		if err != nil {
M
Medya Gh 已提交
618
			t.Errorf("failed to list profiles with json format. args %q: %v", rr.Command(), err)
J
Josh Woodcock 已提交
619
		}
M
Medya Gh 已提交
620 621 622
		var jsonObject map[string][]map[string]interface{}
		err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
		if err != nil {
M
Medya Gh 已提交
623
			t.Errorf("failed to decode json from profile list: args %q: %v", rr.Command(), err)
M
Medya Gh 已提交
624 625
		}
		validProfiles := jsonObject["valid"]
M
lint  
Medya Gh 已提交
626
		profileExists := false
M
Medya Gh 已提交
627 628 629 630 631 632 633
		for _, profileObject := range validProfiles {
			if profileObject["Name"] == profile {
				profileExists = true
				break
			}
		}
		if !profileExists {
M
Medya Gh 已提交
634
			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 已提交
635 636 637
		}

	})
638 639 640
}

// validateServiceCmd asserts basic "service" command functionality
641
func validateServiceCmd(ctx context.Context, t *testing.T, profile string) {
642
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "create", "deployment", "hello-node", "--image=k8s.gcr.io/echoserver:1.4"))
643
	if err != nil {
M
Medya Gh 已提交
644
		t.Logf("%q failed: %v (may not be an error).", rr.Command(), err)
645
	}
646
	rr, err = Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "expose", "deployment", "hello-node", "--type=NodePort", "--port=8080"))
647
	if err != nil {
M
Medya Gh 已提交
648
		t.Logf("%q failed: %v (may not be an error)", rr.Command(), err)
649 650
	}

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

655
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "list"))
656
	if err != nil {
M
Medya Gh 已提交
657
		t.Errorf("failed to do service list. args %q : %v", rr.Command(), err)
658
	}
659
	if !strings.Contains(rr.Stdout.String(), "hello-node") {
660
		t.Errorf("expected 'service list' to contain *hello-node* but got -%q-", rr.Stdout.String())
661 662
	}

663 664 665 666
	if NeedsPortForward() {
		t.Skipf("test is broken for port-forwarded drivers: https://github.com/kubernetes/minikube/issues/7383")
	}

667
	// Test --https --url mode
668
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "--namespace=default", "--https", "--url", "hello-node"))
669
	if err != nil {
M
Medya Gh 已提交
670
		t.Fatalf("failed to get service url. args %q : %v", rr.Command(), err)
671 672
	}
	if rr.Stderr.String() != "" {
M
Medya Gh 已提交
673
		t.Errorf("expected stderr to be empty but got *%q* . args %q", rr.Stderr, rr.Command())
674
	}
675 676

	endpoint := strings.TrimSpace(rr.Stdout.String())
677 678
	u, err := url.Parse(endpoint)
	if err != nil {
679
		t.Fatalf("failed to parse service url endpoint %q: %v", endpoint, err)
680 681
	}
	if u.Scheme != "https" {
682
		t.Errorf("expected scheme to be 'https' but got %q", u.Scheme)
683 684 685
	}

	// Test --format=IP
686
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "hello-node", "--url", "--format={{.IP}}"))
687
	if err != nil {
M
Medya Gh 已提交
688
		t.Errorf("failed to get service url with custom format. args %q: %v", rr.Command(), err)
689
	}
690
	if strings.TrimSpace(rr.Stdout.String()) != u.Hostname() {
M
Medya Gh 已提交
691
		t.Errorf("expected 'service --format={{.IP}}' output to be -%q- but got *%q* . args %q.", u.Hostname(), rr.Stdout.String(), rr.Command())
692 693
	}

694 695
	// Test a regular URLminikube
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "service", "hello-node", "--url"))
696
	if err != nil {
M
Medya Gh 已提交
697
		t.Errorf("failed to get service url. args: %q: %v", rr.Command(), err)
698
	}
699 700 701 702 703 704 705

	endpoint = strings.TrimSpace(rr.Stdout.String())
	u, err = url.Parse(endpoint)
	if err != nil {
		t.Fatalf("failed to parse %q: %v", endpoint, err)
	}
	if u.Scheme != "http" {
M
lint  
Medya Gh 已提交
706
		t.Fatalf("expected scheme to be -%q- got scheme: *%q*", "http", u.Scheme)
707 708 709 710
	}

	t.Logf("url: %s", endpoint)
	resp, err := retryablehttp.Get(endpoint)
711
	if err != nil {
712
		t.Fatalf("get failed: %v\nresp: %v", err, resp)
713 714
	}
	if resp.StatusCode != http.StatusOK {
M
lint  
Medya Gh 已提交
715
		t.Fatalf("expected status code for %q to be -%q- but got *%q*", endpoint, http.StatusOK, resp.StatusCode)
716 717 718
	}
}

719 720
// validateAddonsCmd asserts basic "addon" command functionality
func validateAddonsCmd(ctx context.Context, t *testing.T, profile string) {
M
Medya Gh 已提交
721
	// Table output
722 723
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list"))
	if err != nil {
M
Medya Gh 已提交
724
		t.Errorf("failed to do addon list: args %q : %v", rr.Command(), err)
725
	}
M
Medya Gh 已提交
726 727
	for _, a := range []string{"dashboard", "ingress", "ingress-dns"} {
		if !strings.Contains(rr.Output(), a) {
M
Medya Gh 已提交
728
			t.Errorf("expected 'addon list' output to include -%q- but got *%s*", a, rr.Output())
729 730
		}
	}
J
Josh Woodcock 已提交
731 732 733 734

	// Json output
	rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "addons", "list", "-o", "json"))
	if err != nil {
M
Medya Gh 已提交
735
		t.Errorf("failed to do addon list with json output. args %q: %v", rr.Command(), err)
J
Josh Woodcock 已提交
736 737 738 739
	}
	var jsonObject map[string]interface{}
	err = json.Unmarshal(rr.Stdout.Bytes(), &jsonObject)
	if err != nil {
740
		t.Errorf("failed to decode addon list json output : %v", err)
J
Josh Woodcock 已提交
741
	}
742 743
}

744 745 746 747 748
// validateSSHCmd asserts basic "ssh" command functionality
func validateSSHCmd(ctx context.Context, t *testing.T, profile string) {
	if NoneDriver() {
		t.Skipf("skipping: ssh unsupported by none")
	}
P
Priya Wadhwa 已提交
749
	want := "hello\n"
750 751
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", fmt.Sprintf("echo hello")))
	if err != nil {
M
Medya Gh 已提交
752
		t.Errorf("failed to run an ssh command. args %q : %v", rr.Command(), err)
753 754
	}
	if rr.Stdout.String() != want {
M
Medya Gh 已提交
755
		t.Errorf("expected minikube ssh command output to be -%q- but got *%q*. args %q", want, rr.Stdout.String(), rr.Command())
756 757 758
	}
}

759 760 761 762
// validateMySQL validates a minimalist MySQL deployment
func validateMySQL(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "replace", "--force", "-f", filepath.Join(*testdataDir, "mysql.yaml")))
	if err != nil {
M
Medya Gh 已提交
763
		t.Fatalf("failed to kubectl replace mysql: args %q failed: %v", rr.Command(), err)
764 765
	}

766
	names, err := PodWait(ctx, t, profile, "default", "app=mysql", Minutes(10))
767
	if err != nil {
768
		t.Fatalf("failed waiting for mysql pod: %v", err)
769 770
	}

771 772 773 774
	// 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
775
	}
M
Medya Gh 已提交
776
	if err = retry.Expo(mysql, 1*time.Second, Minutes(5)); err != nil {
777
		t.Errorf("failed to exec 'mysql -ppassword -e show databases;': %v", err)
778 779 780
	}
}

781 782 783 784 785 786 787 788 789 790
// 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())
}

791 792 793 794 795 796 797 798 799 800
// 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())
}

801 802 803 804 805
// 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()))
}

806 807
// Copy extra file into minikube home folder for file sync test
func setupFileSync(ctx context.Context, t *testing.T, profile string) {
808 809 810
	p := localSyncTestPath()
	t.Logf("local sync path: %s", p)
	err := copy.Copy("./testdata/sync.test", p)
811
	if err != nil {
812
		t.Fatalf("failed to copy ./testdata/sync.test: %v", err)
813
	}
814

815 816
	testPem := "./testdata/minikube_test.pem"

817 818 819
	// Write to a temp file for an atomic write
	tmpPem := localTestCertPath() + ".pem"
	if err := copy.Copy(testPem, tmpPem); err != nil {
820 821 822
		t.Fatalf("failed to copy %s: %v", testPem, err)
	}

823 824 825 826
	if err := os.Rename(tmpPem, localTestCertPath()); err != nil {
		t.Fatalf("failed to rename %s: %v", tmpPem, err)
	}

827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
	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)
844
	}
845 846 847 848 849 850 851
}

// validateFileSync to check existence of the test file
func validateFileSync(ctx context.Context, t *testing.T, profile string) {
	if NoneDriver() {
		t.Skipf("skipping: ssh unsupported by none")
	}
852 853 854

	vp := vmSyncTestPath()
	t.Logf("Checking for existence of %s within VM", vp)
855
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", fmt.Sprintf("sudo cat %s", vp)))
856
	if err != nil {
M
Medya Gh 已提交
857
		t.Errorf("%s failed: %v", rr.Command(), err)
858
	}
859 860
	got := rr.Stdout.String()
	t.Logf("file sync test content: %s", got)
861 862 863

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

867
	if diff := cmp.Diff(string(expected), got); diff != "" {
868 869 870 871
		t.Errorf("/etc/sync.test content mismatch (-want +got):\n%s", diff)
	}
}

872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
// validateCertSync to check existence of the test certificate
func validateCertSync(ctx context.Context, t *testing.T, profile string) {
	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)
892
		rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", fmt.Sprintf("sudo cat %s", vp)))
893
		if err != nil {
M
Medya Gh 已提交
894
			t.Errorf("failed to check existence of %q inside minikube. args %q: %v", vp, rr.Command(), err)
895 896 897 898 899
		}

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

905 906 907 908
// validateUpdateContextCmd asserts basic "update-context" command functionality
func validateUpdateContextCmd(ctx context.Context, t *testing.T, profile string) {
	rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "update-context", "--alsologtostderr", "-v=2"))
	if err != nil {
M
Medya Gh 已提交
909
		t.Errorf("failed to run minikube update-context: args %q: %v", rr.Command(), err)
910 911
	}

T
Thomas Stromberg 已提交
912
	want := []byte("No changes")
913 914 915 916 917
	if !bytes.Contains(rr.Stdout.Bytes(), want) {
		t.Errorf("update-context: got=%q, want=*%q*", rr.Stdout.Bytes(), want)
	}
}

918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
// 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
934
}