From d45471ca8d2a42aa7f248e46b85486936943a484 Mon Sep 17 00:00:00 2001
From: Thomas Stromberg <tstromberg@google.com>
Date: Wed, 4 Mar 2020 14:48:40 -0800
Subject: [PATCH] Add certificate sync integration test

---
 pkg/minikube/command/ssh_runner.go          |  2 +
 test/integration/functional_test.go         | 57 +++++++++++++++++++++
 test/integration/testdata/minikube_test.pem | 22 ++++++++
 3 files changed, 81 insertions(+)
 create mode 100644 test/integration/testdata/minikube_test.pem

diff --git a/pkg/minikube/command/ssh_runner.go b/pkg/minikube/command/ssh_runner.go
index 66473fd312..9d1f03a04d 100644
--- a/pkg/minikube/command/ssh_runner.go
+++ b/pkg/minikube/command/ssh_runner.go
@@ -241,11 +241,13 @@ func (s *SSHRunner) sameFileExists(f assets.CopyableFile, dst string) (bool, err
 	if err != nil {
 		return false, err
 	}
+	glog.Infof("found %s: %d bytes, modified at %s", dst, dstSize, dstModTime)
 
 	// compare sizes and modtimes
 	if srcSize != dstSize {
 		return false, errors.New("source file and destination file are different sizes")
 	}
+
 	return srcModTime.Equal(dstModTime), nil
 }
 
diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go
index 1534abffde..3ff2514714 100644
--- a/test/integration/functional_test.go
+++ b/test/integration/functional_test.go
@@ -28,6 +28,7 @@ import (
 	"net/url"
 	"os"
 	"os/exec"
+	"path"
 	"path/filepath"
 	"regexp"
 	"runtime"
@@ -57,10 +58,17 @@ func TestFunctional(t *testing.T) {
 	profile := UniqueProfileName("functional")
 	ctx, cancel := context.WithTimeout(context.Background(), 40*time.Minute)
 	defer func() {
+		if !*cleanup {
+			return
+		}
 		p := localSyncTestPath()
 		if err := os.Remove(p); err != nil {
 			t.Logf("unable to remove %s: %v", p, err)
 		}
+		p = localTestCertPath()
+		if err := os.Remove(p); err != nil {
+			t.Logf("unable to remove %s: %v", p, err)
+		}
 		CleanupWithLogs(t, profile, cancel)
 	}()
 
@@ -110,6 +118,7 @@ func TestFunctional(t *testing.T) {
 			{"SSHCmd", validateSSHCmd},
 			{"MySQL", validateMySQL},
 			{"FileSync", validateFileSync},
+			{"CertSync", validateCertSync},
 			{"UpdateContextCmd", validateUpdateContextCmd},
 			{"DockerEnv", validateDockerEnv},
 		}
@@ -716,6 +725,16 @@ func localSyncTestPath() string {
 	return filepath.Join(localpath.MiniPath(), "/files", vmSyncTestPath())
 }
 
+// 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())
+}
+
 // Copy extra file into minikube home folder for file sync test
 func setupFileSync(ctx context.Context, t *testing.T, profile string) {
 	p := localSyncTestPath()
@@ -724,6 +743,11 @@ func setupFileSync(ctx context.Context, t *testing.T, profile string) {
 	if err != nil {
 		t.Fatalf("copy: %v", err)
 	}
+
+	err = copy.Copy("./testdata/minikube_test.pem", localTestCertPath())
+	if err != nil {
+		t.Fatalf("copy: %v", err)
+	}
 }
 
 // validateFileSync to check existence of the test file
@@ -751,6 +775,39 @@ func validateFileSync(ctx context.Context, t *testing.T, profile string) {
 	}
 }
 
+// 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)
+		rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", fmt.Sprintf("cat %s", vp)))
+		if err != nil {
+			t.Errorf("%s failed: %v", rr.Args, err)
+		}
+
+		// Strip carriage returned by ssh
+		got := strings.Replace(rr.Stdout.String(), "\r", "", -1)
+		if diff := cmp.Diff(string(want), got); diff != "" {
+			t.Errorf("minikube_test.pem -> %s mismatch (-want +got):\n%s", vp, diff)
+		}
+	}
+}
+
 // 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"))
diff --git a/test/integration/testdata/minikube_test.pem b/test/integration/testdata/minikube_test.pem
new file mode 100644
index 0000000000..5f022a853e
--- /dev/null
+++ b/test/integration/testdata/minikube_test.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDsDCCApgCCQD5n0OIsOYIjDANBgkqhkiG9w0BAQsFADCBmTELMAkGA1UEBhMC
+VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x
+ETAPBgNVBAoMCG1pbmlrdWJlMRYwFAYDVQQLDA1QYXJ0eSBQYXJyb3RzMREwDwYD
+VQQDDAhtaW5pa3ViZTEfMB0GCSqGSIb3DQEJARYQbWluaWt1YmVAY25jZi5pbzAe
+Fw0yMDAzMDQyMTU2MjZaFw0yMTAzMDQyMTU2MjZaMIGZMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzERMA8G
+A1UECgwIbWluaWt1YmUxFjAUBgNVBAsMDVBhcnR5IFBhcnJvdHMxETAPBgNVBAMM
+CG1pbmlrdWJlMR8wHQYJKoZIhvcNAQkBFhBtaW5pa3ViZUBjbmNmLmlvMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/qVMQ/234ul5yWI1yaHvV4pZ5Ffy
+M0bSMjzZUwlsvzerXzF3WrdpeZs5GzBNBWL/Db9KziGHCtfX9j5okJqPvB2lxdL5
+d5hFIYSORSemYLX2kdlnlykY5fzmFLKIUO9xXs0YNF4JUMEBgGK6n/BdLvXDUULZ
+26QOKs6+iH7TAL4RtozxQ8YXKQArdmpeAvxy2PSZGvVk1htKtyuKQsiFqH3oRleK
+3mljXfC5LsoIJHqd/8lAsckH87+IfwYnJ1CNJM2gueaCf+HmudVrvXfHaszh1Wh1
+9HKPE95Azi6CKoBGlRGFxt8UR72YIcTjC/lYxzbHeCpU7RCiXfsC0iMTlQIDAQAB
+MA0GCSqGSIb3DQEBCwUAA4IBAQBhsKnghyBki4NOnK5gHm7ow+7S+xvkjJhXBQ6i
+/xQD4/GCZ1tH5iFHXmo+bt4jB9hvKLyN5M5a8TlDwwqTLIoPDQJh37UpSCwbY/6z
+nE2aP3N2ue1/DeY60tgAh1c1uJDMeTiFbalJqSkneaHpNfvEQhUORFoN4yQSVEYg
++T9mzTAWQ55TeBgbRevmA25nXHdPAae1MvJWDeG+aJfhq1I2hCwaitJ3iSwgn2ew
+637It/aBkMLhsCKTHxlXDGUX401ddbc0ZiC308cyMbis3iBeh4RBjkFxP8eIWFmK
+sos/dyqdua742L1cOKYFbLJfjA1VyxJQUxQvWKkbaq0xi7ao
+-----END CERTIFICATE-----