diff --git a/.github/workflows/compile-test.yaml b/.github/workflows/compile-test.yaml index 07f3a9d2..873f1e3b 100644 --- a/.github/workflows/compile-test.yaml +++ b/.github/workflows/compile-test.yaml @@ -20,8 +20,5 @@ jobs: sudo ufw allow 2200:2300/tcp sudo ufw enable sudo ufw status verbose - mkdir -p ~/.ssh - chmod 765 ~/.ssh - cp testing/keys/* ~/.ssh/ GO111MODULE=on go get sigs.k8s.io/kind@v0.7.0 GO111MODULE=on go test -timeout 600s -v -p 1 ./... \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1e654e5e..232452d4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ dist .idea .DS_Store +.testing \ No newline at end of file diff --git a/examples/host-list-provider.crsh b/examples/host-list-provider.crsh index dd8f1f3f..de3f9d6e 100644 --- a/examples/host-list-provider.crsh +++ b/examples/host-list-provider.crsh @@ -11,9 +11,9 @@ # setup and configuration ssh=ssh_config( username=os.username, - private_key_path="{0}/.ssh/id_rsa".format(os.home), + private_key_path=args.ssh_pk_path, port=args.ssh_port, - max_retries=5, + max_retries=50, ) provider=host_list_provider(hosts=["localhost", "127.0.0.1"], ssh_config=ssh) diff --git a/examples/kube-nodes-provider.crsh b/examples/kube-nodes-provider.crsh index fa51e351..dbd5990b 100644 --- a/examples/kube-nodes-provider.crsh +++ b/examples/kube-nodes-provider.crsh @@ -11,9 +11,9 @@ # setup and configuration ssh=ssh_config( username=os.username, - private_key_path="{0}/.ssh/id_rsa".format(os.home), + private_key_path=args.ssh_pk_path, port=args.ssh_port, - max_retries=5, + max_retries=50, ) hosts=resources( diff --git a/exec/executor_test.go b/exec/executor_test.go index c4f778ca..e537255d 100644 --- a/exec/executor_test.go +++ b/exec/executor_test.go @@ -4,11 +4,9 @@ package exec import ( - "io/ioutil" "os" "strings" "testing" - "time" "github.com/sirupsen/logrus" @@ -16,60 +14,36 @@ import ( ) var ( - testSSHPort = testcrashd.NextPortValue() - testServerName = testcrashd.NextResourceName() - testClusterName = testcrashd.NextResourceName() - getTestKubeConf func() string + support *testcrashd.TestSupport ) func TestMain(m *testing.M) { - testcrashd.Init() - - sshSvr := testcrashd.NewSSHServer(testServerName, testSSHPort) - logrus.Debug("Attempting to start SSH server") - if err := sshSvr.Start(); err != nil { - logrus.Error(err) - os.Exit(1) + test, err := testcrashd.Init() + if err != nil { + logrus.Fatal(err) } + support = test - kind := testcrashd.NewKindCluster("../testing/kind-cluster-docker.yaml", testClusterName) - if err := kind.Create(); err != nil { - logrus.Error(err) - os.Exit(1) + if err := support.SetupSSHServer(); err != nil { + logrus.Fatal(err) } - // attempt to wait for cluster up - time.Sleep(time.Second * 10) + if err := support.SetupKindCluster(); err != nil { + logrus.Fatal(err) + } - tmpFile, err := ioutil.TempFile(os.TempDir(), testClusterName) + _, err = support.SetupKindKubeConfig() if err != nil { - logrus.Error(err) - os.Exit(1) + logrus.Fatal(err) } - defer func() { - logrus.Debug("Stopping SSH server...") - if err := sshSvr.Stop(); err != nil { - logrus.Error(err) - os.Exit(1) - } - - if err := kind.Destroy(); err != nil { - logrus.Error(err) - os.Exit(1) - } - }() - - getTestKubeConf = func() string { - return tmpFile.Name() - } + result := m.Run() - if err := kind.MakeKubeConfigFile(getTestKubeConf()); err != nil { - logrus.Error(err) - os.Exit(1) + if err := support.TearDown(); err != nil { + logrus.Fatal(err) } - os.Exit(m.Run()) + os.Exit(result) } func TestKindScript(t *testing.T) { @@ -81,26 +55,30 @@ func TestKindScript(t *testing.T) { { name: "api objects", scriptPath: "../examples/kind-api-objects.crsh", - args: ArgMap{"kubecfg": getTestKubeConf()}, + args: ArgMap{"kubecfg": support.KindKubeConfigFile()}, }, { name: "pod logs", scriptPath: "../examples/pod-logs.crsh", - args: ArgMap{"kubecfg": getTestKubeConf()}, + args: ArgMap{"kubecfg": support.KindKubeConfigFile()}, }, { name: "script with args", scriptPath: "../examples/script-args.crsh", args: ArgMap{ "workdir": "/tmp/crashargs", - "kubecfg": getTestKubeConf(), + "kubecfg": support.KindKubeConfigFile(), "output": "/tmp/craslogs.tar.gz", }, }, { name: "host-list provider", scriptPath: "../examples/host-list-provider.crsh", - args: ArgMap{"kubecfg": getTestKubeConf(), "ssh_port": testSSHPort}, + args: ArgMap{ + "kubecfg": support.KindKubeConfigFile(), + "ssh_pk_path": support.PrivateKeyPath(), + "ssh_port": support.PortValue(), + }, }, //{ // name: "kube-nodes provider", @@ -108,14 +86,14 @@ func TestKindScript(t *testing.T) { // args: ArgMap{ // "kubecfg": getTestKubeConf(), // "ssh_port": testSSHPort, - // "username": testcrashd.GetSSHUsername(), - // "key_path": testcrashd.GetSSHPrivateKey(), + // "username": getUsername(), + // "key_path": getPrivateKey(), // }, //}, { name: "kind-capi-bootstrap", scriptPath: "../examples/kind-capi-bootstrap.crsh", - args: ArgMap{"cluster_name": testClusterName}, + args: ArgMap{"cluster_name": support.ResourceName()}, }, } diff --git a/k8s/main_test.go b/k8s/main_test.go new file mode 100644 index 00000000..515dd21a --- /dev/null +++ b/k8s/main_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2019 VMware, Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package k8s + +import ( + "os" + "testing" + + "github.com/sirupsen/logrus" + + testcrashd "github.com/vmware-tanzu/crash-diagnostics/testing" +) + +var ( + support *testcrashd.TestSupport +) + +func TestMain(m *testing.M) { + test, err := testcrashd.Init() + if err != nil { + logrus.Fatal("failed to initialize test support:", err) + } + + support = test + + if err := support.SetupKindCluster(); err != nil { + logrus.Fatal(err) + } + _, err = support.SetupKindKubeConfig() + if err != nil { + logrus.Fatal(err) + } + + result := m.Run() + + if err := support.TearDown(); err != nil { + logrus.Fatal(err) + } + + os.Exit(result) +} diff --git a/ssh/client.go b/ssh/client.go deleted file mode 100644 index 4a206e19..00000000 --- a/ssh/client.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2019 VMware, Inc. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package ssh - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "time" - - "github.com/sirupsen/logrus" - "golang.org/x/crypto/ssh" - "k8s.io/apimachinery/pkg/util/wait" -) - -// SSHClient represents a client used to connect to an SSH server -type SSHClient struct { - user string - privateKey string - insecure bool - cfg *ssh.ClientConfig - sshc *ssh.Client - hostKey ssh.PublicKey - connMaxRetries int -} - -// New creates uses the user and privateKeyPath to create an *SSHClient -func New(user string, privateKeyPath string, maxRetries int) *SSHClient { - if maxRetries <= 0 { - maxRetries = 30 - } - client := &SSHClient{ - user: user, - privateKey: privateKeyPath, - insecure: false, - connMaxRetries: maxRetries, - } - return client -} - -// NewInsecure -func NewInsecure(user string) *SSHClient { - client := &SSHClient{ - user: user, - insecure: true, - } - return client -} - -// Dial connects a remote SSH host at address addr -func (c *SSHClient) Dial(addr string) error { - logrus.Debug("SSH dialing server", addr) - - if c.user == "" { - return fmt.Errorf("Missing SSH user") - } - - if !c.insecure { - logrus.Debugf("Connecting using private key file %s@%s", c.user, c.privateKey) - cfg, err := c.privateKeyConfig() - if err != nil { - return err - } - c.cfg = cfg - } else { - c.cfg = &ssh.ClientConfig{ - User: c.user, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - } - } - - // SSH connections with retries - maxRetries := 30 - retries := wait.Backoff{Steps: maxRetries, Duration: time.Millisecond * 80, Jitter: 0.1} - if err := wait.ExponentialBackoff(retries, func() (bool, error) { - sshc, err := ssh.Dial("tcp", addr, c.cfg) - if err != nil { - logrus.Errorf("Failed to dial %s (ssh): %s: will retry connection again", addr, err) - return false, nil - } - logrus.Debug("SSH connection establised") - c.sshc = sshc - return true, nil - }); err != nil { - logrus.Debugf("SSH connection failed after %d tries", maxRetries) - return err - } - - return nil -} - -// SSHRun executes the specified command on a remote host over SSH -func (c *SSHClient) SSHRun(cmdStr string) (io.Reader, error) { - logrus.Debug("SSHRun: ", cmdStr) - session, err := c.sshc.NewSession() - if err != nil { - return nil, err - } - defer session.Close() - - stdout := new(bytes.Buffer) - stderr := new(bytes.Buffer) - session.Stdout = stdout - session.Stderr = stderr - output := io.MultiReader(stdout, stderr) - - if err := session.Start(cmdStr); err != nil { - return output, err - } - - if err := session.Wait(); err != nil { - os.Setenv("CMD_EXITCODE", fmt.Sprintf("%d", 1)) - os.Setenv("CMD_SUCCESS", "false") - return output, fmt.Errorf("SSH: error waiting for response: %s", err) - } - - os.Setenv("CMD_EXITCODE", fmt.Sprintf("%d", 0)) - os.Setenv("CMD_SUCCESS", "true") - - logrus.Debugf("Remote command succeeded: %s", cmdStr) - return output, nil -} - -// Hangup closes the established SSH connection -func (c *SSHClient) Hangup() error { - return c.sshc.Close() -} - -func (c *SSHClient) privateKeyConfig() (*ssh.ClientConfig, error) { - if _, err := os.Stat(c.privateKey); err != nil { - return nil, err - } - - logrus.Debug("Configuring SSH connection with ", c.privateKey) - key, err := ioutil.ReadFile(c.privateKey) - if err != nil { - return nil, err - } - - signer, err := ssh.ParsePrivateKey(key) - if err != nil { - return nil, err - } - logrus.Debug("Found SSH private key ", c.privateKey) - - return &ssh.ClientConfig{ - User: c.user, - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(signer), - }, - // not authenticating host - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - }, nil -} diff --git a/ssh/client_test.go b/ssh/client_test.go deleted file mode 100644 index 63c871b5..00000000 --- a/ssh/client_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2019 VMware, Inc. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package ssh - -import ( - "bytes" - "fmt" - "io" - "os" - "os/user" - "path/filepath" - "strings" - "testing" -) - -func TestSSHClient(t *testing.T) { - t.Skip("Skipping ssh client tests") - sshHost := fmt.Sprintf("127.0.0.1:%s", testSSHPort) - homeDir, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } - privKey := filepath.Join(homeDir, ".ssh/id_rsa") - - usr, err := user.Current() - if err != nil { - t.Fatal(err) - } - - tests := []struct { - name string - prepare func() (*SSHClient, error) - run func(*SSHClient) error - shouldFail bool - }{ - { - name: "dial 127.0.0.1", - prepare: func() (*SSHClient, error) { - return New(usr.Username, privKey, 10), nil - }, - run: func(sshClient *SSHClient) error { - if err := sshClient.Dial(sshHost); err != nil { - return err - } - defer sshClient.Hangup() - - return nil - }, - }, - - { - name: "ssh run echo hello", - prepare: func() (*SSHClient, error) { - return New(usr.Username, privKey, 10), nil - }, - run: func(sshClient *SSHClient) error { - if err := sshClient.Dial(sshHost); err != nil { - return err - } - defer sshClient.Hangup() - - reader, err := sshClient.SSHRun("echo 'Hello World!'") - if err != nil { - return err - } - buff := new(bytes.Buffer) - if _, err := io.Copy(buff, reader); err != nil { - return err - } - - if strings.TrimSpace(buff.String()) != "Hello World!" { - t.Fatal("SSHRun unexpected result: ", buff.String()) - } - return nil - }, - }, - { - name: "ssh run bad command", - prepare: func() (*SSHClient, error) { - return New(usr.Username, privKey, 10), nil - }, - run: func(sshClient *SSHClient) error { - if err := sshClient.Dial(sshHost); err != nil { - return err - } - defer sshClient.Hangup() - - if _, err := sshClient.SSHRun("foo bar"); err != nil { - return err - } - - return nil - }, - shouldFail: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - c, err := test.prepare() - - if err != nil { - t.Fatal(err) - } - - if err := test.run(c); err != nil { - if !test.shouldFail { - t.Fatal(err) - } - t.Log(err) - } - }) - } -} diff --git a/ssh/main_test.go b/ssh/main_test.go index 397928da..71a46f88 100644 --- a/ssh/main_test.go +++ b/ssh/main_test.go @@ -13,26 +13,34 @@ import ( ) var ( - testSSHPort = testcrashd.NextPortValue() - testMaxRetries = 30 + support *testcrashd.TestSupport + testSSHArgs SSHArgs ) func TestMain(m *testing.M) { - testcrashd.Init() + test, err := testcrashd.Init() + if err != nil { + logrus.Fatal(err) + } + support = test + + if err := support.SetupSSHServer(); err != nil { + logrus.Fatal(err) + } - sshSvr := testcrashd.NewSSHServer(testcrashd.NextResourceName(), testSSHPort) - logrus.Debug("Attempting to start SSH server") - if err := sshSvr.Start(); err != nil { - logrus.Error(err) - os.Exit(1) + testSSHArgs = SSHArgs{ + User: support.CurrentUsername(), + PrivateKeyPath: support.PrivateKeyPath(), + Host: "127.0.0.1", + Port: support.PortValue(), + MaxRetries: support.MaxConnectionRetries(), } testResult := m.Run() - logrus.Debug("Stopping SSH server...") - if err := sshSvr.Stop(); err != nil { - logrus.Error(err) - os.Exit(1) + logrus.Debug("Shutting down test...") + if err := support.TearDown(); err != nil { + logrus.Fatal(err) } os.Exit(testResult) diff --git a/ssh/scp.go b/ssh/scp.go index b977ca53..c4781b94 100644 --- a/ssh/scp.go +++ b/ssh/scp.go @@ -41,7 +41,7 @@ func CopyFrom(args SSHArgs, rootDir string, sourcePath string) error { return fmt.Errorf("scp: failed to build command string: %s", err) } - effectiveCmd := fmt.Sprintf(`%s "%s"`, sshCmd, targetPath) + effectiveCmd := fmt.Sprintf(`%s %s`, sshCmd, targetPath) logrus.Debug("scp: ", effectiveCmd) maxRetries := args.MaxRetries diff --git a/ssh/scp_test.go b/ssh/scp_test.go index a24f4cb2..84d790d3 100644 --- a/ssh/scp_test.go +++ b/ssh/scp_test.go @@ -6,52 +6,35 @@ package ssh import ( "io/ioutil" "os" - "os/user" "path/filepath" - "strings" "testing" ) func TestCopy(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } - - usr, err := user.Current() - if err != nil { - t.Fatal(err) - } - pkPath := filepath.Join(homeDir, ".ssh/id_rsa") - sshArgs := SSHArgs{User: usr.Username, PrivateKeyPath: pkPath, Host: "127.0.0.1", Port: testSSHPort, MaxRetries: testMaxRetries} tests := []struct { name string sshArgs SSHArgs - rootDir string remoteFiles map[string]string srcFile string fileContent string }{ { name: "copy single file", - sshArgs: sshArgs, - rootDir: "/tmp/crashd", + sshArgs: testSSHArgs, remoteFiles: map[string]string{"foo.txt": "FooBar"}, srcFile: "foo.txt", fileContent: "FooBar", }, { name: "copy single file in dir", - sshArgs: sshArgs, - rootDir: "/tmp/crashd", + sshArgs: testSSHArgs, remoteFiles: map[string]string{"foo/bar.txt": "FooBar"}, srcFile: "foo/bar.txt", fileContent: "FooBar", }, { name: "copy dir", - sshArgs: sshArgs, - rootDir: "/tmp/crashd", + sshArgs: testSSHArgs, remoteFiles: map[string]string{"bar/foo.csv": "FooBar", "bar/bar.txt": "BarBar"}, srcFile: "bar/", fileContent: "FooBar", @@ -64,22 +47,19 @@ func TestCopy(t *testing.T) { for file, _ := range test.remoteFiles { RemoveTestSSHFile(t, test.sshArgs, file) } - - if err := os.RemoveAll(test.rootDir); err != nil { - t.Fatal(err) - } }() - // setup remote files + // setup fake remote files for file, content := range test.remoteFiles { MakeTestSSHFile(t, test.sshArgs, file, content) } - if err := CopyFrom(test.sshArgs, test.rootDir, test.srcFile); err != nil { + if err := CopyFrom(test.sshArgs, support.TmpDirRoot(), test.srcFile); err != nil { t.Fatal(err) } - expectedPath := filepath.Join(test.rootDir, test.srcFile) + // validate copied files/dir + expectedPath := filepath.Join(support.TmpDirRoot(), test.srcFile) finfo, err := os.Stat(expectedPath) if err != nil { t.Fatal(err) @@ -98,58 +78,58 @@ func TestCopy(t *testing.T) { t.Error("unexpected file content") } } - }) } } -func TestMakeSCPCmdStr(t *testing.T) { - tests := []struct { - name string - args SSHArgs - cmdStr string - source string - shouldFail bool - }{ - { - name: "user and host", - args: SSHArgs{User: "sshuser", Host: "local.host"}, - source: "/tmp/any", - cmdStr: "scp -rpq -o StrictHostKeyChecking=no -P 22 sshuser@local.host:/tmp/any", - }, - { - name: "user host and pkpath", - args: SSHArgs{User: "sshuser", Host: "local.host", PrivateKeyPath: "/pk/path"}, - source: "/foo/bar", - cmdStr: "scp -rpq -o StrictHostKeyChecking=no -i /pk/path -P 22 sshuser@local.host:/foo/bar", - }, - { - name: "user host pkpath and proxy", - args: SSHArgs{User: "sshuser", Host: "local.host", PrivateKeyPath: "/pk/path", ProxyJump: &ProxyJumpArgs{User: "juser", Host: "jhost"}}, - source: "userFile", - cmdStr: "scp -rpq -o StrictHostKeyChecking=no -i /pk/path -P 22 -J juser@jhost sshuser@local.host:userFile", - }, - { - name: "missing host", - args: SSHArgs{User: "sshuser"}, - shouldFail: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - result, err := makeSCPCmdStr("scp", test.args, test.source) - if err != nil && !test.shouldFail { - t.Fatal(err) - } - cmdFields := strings.Fields(test.cmdStr) - resultFields := strings.Fields(result) - - for i := range cmdFields { - if cmdFields[i] != resultFields[i] { - t.Fatalf("unexpected command string element: %s vs. %s", cmdFields, resultFields) - } - } - }) - } -} +// +//func TestMakeSCPCmdStr(t *testing.T) { +// tests := []struct { +// name string +// args SSHArgs +// cmdStr string +// source string +// shouldFail bool +// }{ +// { +// name: "user and host", +// args: SSHArgs{User: "sshuser", Host: "local.host"}, +// source: "/tmp/any", +// cmdStr: "scp -rpq -o StrictHostKeyChecking=no -P 22 sshuser@local.host:/tmp/any", +// }, +// { +// name: "user host and pkpath", +// args: SSHArgs{User: "sshuser", Host: "local.host", PrivateKeyPath: "/pk/path"}, +// source: "/foo/bar", +// cmdStr: "scp -rpq -o StrictHostKeyChecking=no -i /pk/path -P 22 sshuser@local.host:/foo/bar", +// }, +// { +// name: "user host pkpath and proxy", +// args: SSHArgs{User: "sshuser", Host: "local.host", PrivateKeyPath: "/pk/path", ProxyJump: &ProxyJumpArgs{User: "juser", Host: "jhost"}}, +// source: "userFile", +// cmdStr: "scp -rpq -o StrictHostKeyChecking=no -i /pk/path -P 22 -J juser@jhost sshuser@local.host:userFile", +// }, +// { +// name: "missing host", +// args: SSHArgs{User: "sshuser"}, +// shouldFail: true, +// }, +// } +// +// for _, test := range tests { +// t.Run(test.name, func(t *testing.T) { +// result, err := makeSCPCmdStr("scp", test.args, test.source) +// if err != nil && !test.shouldFail { +// t.Fatal(err) +// } +// cmdFields := strings.Fields(test.cmdStr) +// resultFields := strings.Fields(result) +// +// for i := range cmdFields { +// if cmdFields[i] != resultFields[i] { +// t.Fatalf("unexpected command string element: %s vs. %s", cmdFields, resultFields) +// } +// } +// }) +// } +//} diff --git a/ssh/ssh_test.go b/ssh/ssh_test.go index 9da5ceb1..8e209fb7 100644 --- a/ssh/ssh_test.go +++ b/ssh/ssh_test.go @@ -5,25 +5,11 @@ package ssh import ( "bytes" - "os" - "os/user" - "path/filepath" "strings" "testing" ) func TestRun(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } - - usr, err := user.Current() - if err != nil { - t.Fatal(err) - } - pkPath := filepath.Join(homeDir, ".ssh/id_rsa") - tests := []struct { name string args SSHArgs @@ -32,7 +18,7 @@ func TestRun(t *testing.T) { }{ { name: "simple cmd", - args: SSHArgs{User: usr.Username, PrivateKeyPath: pkPath, Host: "127.0.0.1", Port: testSSHPort, MaxRetries: testMaxRetries}, + args: testSSHArgs, cmd: "echo 'Hello World!'", result: "Hello World!", }, @@ -52,17 +38,6 @@ func TestRun(t *testing.T) { } func TestRunRead(t *testing.T) { - homeDir, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } - - usr, err := user.Current() - if err != nil { - t.Fatal(err) - } - pkPath := filepath.Join(homeDir, ".ssh/id_rsa") - tests := []struct { name string args SSHArgs @@ -71,7 +46,7 @@ func TestRunRead(t *testing.T) { }{ { name: "simple cmd", - args: SSHArgs{User: usr.Username, PrivateKeyPath: pkPath, Host: "127.0.0.1", Port: testSSHPort, MaxRetries: testMaxRetries}, + args: testSSHArgs, cmd: "echo 'Hello World!'", result: "Hello World!", }, diff --git a/ssh/test_support.go b/ssh/test_support.go index 9cfdd300..22f986e9 100644 --- a/ssh/test_support.go +++ b/ssh/test_support.go @@ -12,7 +12,36 @@ import ( "testing" ) -func MakeTestSSHDir(t *testing.T, args SSHArgs, dir string) { +//////func mountTestSSHFile(t *testing.T, mountDir, fileName, content string) { +////// srcDir := filepath.Dir(fileName) +////// if len(srcDir) > 0 && srcDir != "." { +////// mountTestSSHDir(t, mountDir, srcDir) +////// } +////// +////// filePath := filepath.Join(mountDir, fileName) +////// t.Logf("mounting test file in SSH: %s", filePath) +////// if err := ioutil.WriteFile(filePath, []byte(content), 0644); err != nil { +////// t.Fatal(err) +////// } +//////} +//// +////func mountTestSSHDir(t *testing.T, mountDir, dir string) { +//// t.Logf("mounting dir in SSH: %s", dir) +//// mountPath := filepath.Join(mountDir, dir) +//// if err := os.MkdirAll(mountPath, 0754); err != nil && !os.IsExist(err) { +//// t.Fatal(err) +//// } +////} +// +//func removeTestSSHFile(t *testing.T, mountDir, fileName string) { +// t.Logf("removing file mounted in SSH: %s", fileName) +// filePath := filepath.Join(mountDir, fileName) +// if err := os.RemoveAll(filePath); err != nil && !os.IsNotExist(err) { +// t.Fatal(err) +// } +//} + +func makeTestSSHDir(t *testing.T, args SSHArgs, dir string) { t.Logf("creating test dir over SSH: %s", dir) _, err := Run(args, fmt.Sprintf(`mkdir -p %s`, dir)) if err != nil { @@ -23,19 +52,19 @@ func MakeTestSSHDir(t *testing.T, args SSHArgs, dir string) { t.Logf("dir created: %s", result) } -func MakeTestSSHFile(t *testing.T, args SSHArgs, fileName, content string) { - srcDir := filepath.Dir(fileName) +func MakeTestSSHFile(t *testing.T, args SSHArgs, filePath, content string) { + srcDir := filepath.Dir(filePath) if len(srcDir) > 0 && srcDir != "." { - MakeTestSSHDir(t, args, srcDir) + makeTestSSHDir(t, args, srcDir) } - t.Logf("creating test file over SSH: %s", fileName) - _, err := Run(args, fmt.Sprintf(`echo '%s' > %s`, content, fileName)) + t.Logf("creating test file over SSH: %s", filePath) + _, err := Run(args, fmt.Sprintf(`echo '%s' > %s`, content, filePath)) if err != nil { t.Fatal(err) } - result, _ := Run(args, fmt.Sprintf(`ls %s`, fileName)) + result, _ := Run(args, fmt.Sprintf(`ls %s`, filePath)) t.Logf("file created: %s", result) } diff --git a/starlark/capture_test.go b/starlark/capture_test.go index 992be96a..1b970075 100644 --- a/starlark/capture_test.go +++ b/starlark/capture_test.go @@ -12,14 +12,11 @@ import ( "strings" "testing" - "github.com/sirupsen/logrus" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - - testcrashd "github.com/vmware-tanzu/crash-diagnostics/testing" ) -func testCaptureFuncForHostResources(t *testing.T, port string) { +func testCaptureFuncForHostResources(t *testing.T, port, privateKey, username string) { tests := []struct { name string args func(t *testing.T) starlark.Tuple @@ -30,7 +27,7 @@ func testCaptureFuncForHostResources(t *testing.T, port string) { name: "default args single machine", args: func(t *testing.T) starlark.Tuple { return starlark.Tuple{starlark.String("echo 'Hello World!'")} }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{makeTestSSHHostResource("127.0.0.1", sshCfg)}) return []starlark.Tuple{[]starlark.Value{starlark.String("resources"), resources}} }, @@ -76,7 +73,7 @@ func testCaptureFuncForHostResources(t *testing.T, port string) { name: "kwargs single machine", args: func(t *testing.T) starlark.Tuple { return nil }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{makeTestSSHHostResource("127.0.0.1", sshCfg)}) return []starlark.Tuple{ []starlark.Value{starlark.String("cmd"), starlark.String("echo 'Hello World!'")}, @@ -127,7 +124,7 @@ func testCaptureFuncForHostResources(t *testing.T, port string) { name: "multiple machines", args: func(t *testing.T) starlark.Tuple { return nil }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{ makeTestSSHHostResource("localhost", sshCfg), makeTestSSHHostResource("127.0.0.1", sshCfg), @@ -173,7 +170,7 @@ func testCaptureFuncForHostResources(t *testing.T, port string) { } } -func testCaptureFuncScriptForHostResources(t *testing.T, port string) { +func testCaptureFuncScriptForHostResources(t *testing.T, port, privateKey, username string) { tests := []struct { name string script string @@ -182,8 +179,8 @@ func testCaptureFuncScriptForHostResources(t *testing.T, port string) { { name: "default cmd multiple machines", script: fmt.Sprintf(` -set_defaults(resources(provider = host_list_provider(hosts=["127.0.0.1","localhost"], ssh_config = ssh_config(username=os.username, port="%s")))) -result = capture("echo 'Hello World!'")`, port), +set_defaults(resources(provider = host_list_provider(hosts=["127.0.0.1","localhost"], ssh_config = ssh_config(username="%s", port="%s", private_key_path="%s", max_retries=50)))) +result = capture("echo 'Hello World!'")`, username, port, privateKey), eval: func(t *testing.T, script string) { exe := New() if err := exe.Exec("test.star", strings.NewReader(script)); err != nil { @@ -228,9 +225,9 @@ def exec(hosts): return result # configuration -set_defaults(ssh_config(username=os.username, port="%s")) +set_defaults(ssh_config(username="%s", port="%s", private_key_path="%s")) hosts = resources(provider=host_list_provider(hosts=["127.0.0.1","localhost"])) -result = exec(hosts)`, port), +result = exec(hosts)`, username, port, privateKey), eval: func(t *testing.T, script string) { exe := New() if err := exe.Exec("test.star", strings.NewReader(script)); err != nil { @@ -259,7 +256,7 @@ result = exec(hosts)`, port), if _, err := os.Stat(result); err != nil { t.Fatalf("captured command file not found: %s", err) } - //os.RemoveAll(result) + os.RemoveAll(result) } }, }, @@ -273,18 +270,16 @@ result = exec(hosts)`, port), } func TestCaptureFuncSSHAll(t *testing.T) { - port := testcrashd.NextPortValue() - sshSvr := testcrashd.NewSSHServer(testcrashd.NextResourceName(), port) - - logrus.Debug("Attempting to start SSH server") - if err := sshSvr.Start(); err != nil { - logrus.Error(err) - os.Exit(1) + if err := testSupport.SetupSSHServer(); err != nil { + t.Fatalf("failed to start SSH server: %s", err) } + port := testSupport.PortValue() + privateKey := testSupport.PrivateKeyPath() + username := testSupport.CurrentUsername() tests := []struct { name string - test func(t *testing.T, port string) + test func(t *testing.T, port, key, username string) }{ {name: "capture func for host resources", test: testCaptureFuncForHostResources}, {name: "capture script for host resources", test: testCaptureFuncScriptForHostResources}, @@ -292,15 +287,8 @@ func TestCaptureFuncSSHAll(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - test.test(t, port) + test.test(t, port, privateKey, username) defer os.RemoveAll(defaults.workdir) }) } - - logrus.Debug("Stopping SSH server...") - if err := sshSvr.Stop(); err != nil { - logrus.Error(err) - os.Exit(1) - } - } diff --git a/starlark/copy_from_test.go b/starlark/copy_from_test.go index a225def0..9c365c4d 100644 --- a/starlark/copy_from_test.go +++ b/starlark/copy_from_test.go @@ -11,15 +11,13 @@ import ( "strings" "testing" - "github.com/sirupsen/logrus" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" "github.com/vmware-tanzu/crash-diagnostics/ssh" - testcrashd "github.com/vmware-tanzu/crash-diagnostics/testing" ) -func testCopyFuncForHostResources(t *testing.T, port string) { +func testCopyFuncForHostResources(t *testing.T, port, privateKey, username string) { tests := []struct { name string remoteFiles map[string]string @@ -32,7 +30,7 @@ func testCopyFuncForHostResources(t *testing.T, port string) { remoteFiles: map[string]string{"foo.txt": "FooBar"}, args: func(t *testing.T) starlark.Tuple { return starlark.Tuple{starlark.String("foo.txt")} }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{makeTestSSHHostResource("127.0.0.1", sshCfg)}) return []starlark.Tuple{[]starlark.Value{starlark.String("resources"), resources}} }, @@ -82,7 +80,7 @@ func testCopyFuncForHostResources(t *testing.T, port string) { remoteFiles: map[string]string{"bar/bar.txt": "BarBar", "bar/foo.txt": "FooBar", "baz.txt": "BazBuz"}, args: func(t *testing.T) starlark.Tuple { return nil }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{ makeTestSSHHostResource("localhost", sshCfg), makeTestSSHHostResource("127.0.0.1", sshCfg), @@ -143,7 +141,7 @@ func testCopyFuncForHostResources(t *testing.T, port string) { remoteFiles: map[string]string{"bar/bar.txt": "BarBar", "bar/foo.txt": "FooBar", "bar/baz.csv": "BizzBuzz"}, args: func(t *testing.T) starlark.Tuple { return nil }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{ makeTestSSHHostResource("localhost", sshCfg), makeTestSSHHostResource("127.0.0.1", sshCfg), @@ -205,7 +203,7 @@ func testCopyFuncForHostResources(t *testing.T, port string) { }, } - sshArgs := ssh.SSHArgs{User: getUsername(), Host: "127.0.0.1", Port: port} + sshArgs := ssh.SSHArgs{User: username, Host: "127.0.0.1", Port: port, PrivateKeyPath: privateKey} for _, test := range tests { t.Run(test.name, func(t *testing.T) { for file, content := range test.remoteFiles { @@ -222,7 +220,7 @@ func testCopyFuncForHostResources(t *testing.T, port string) { } } -func testCopyFuncScriptForHostResources(t *testing.T, port string) { +func testCopyFuncScriptForHostResources(t *testing.T, port, privateKey, username string) { tests := []struct { name string remoteFiles map[string]string @@ -233,8 +231,8 @@ func testCopyFuncScriptForHostResources(t *testing.T, port string) { name: "multiple machines single copyFrom", remoteFiles: map[string]string{"foobar.c": "footext", "bar/bar.txt": "BarBar", "bar/foo.txt": "FooBar", "bar/baz.csv": "BizzBuzz"}, script: fmt.Sprintf(` -set_defaults(resources(provider = host_list_provider(hosts=["127.0.0.1","localhost"], ssh_config = ssh_config(username=os.username, port="%s")))) -result = copy_from("bar/foo.txt")`, port), +set_defaults(resources(provider = host_list_provider(hosts=["127.0.0.1","localhost"], ssh_config = ssh_config(username="%s", port="%s", private_key_path="%s")))) +result = copy_from("bar/foo.txt")`, username, port, privateKey), eval: func(t *testing.T, script string) { exe := New() if err := exe.Exec("test.star", strings.NewReader(script)); err != nil { @@ -295,11 +293,11 @@ def cp(hosts): for host in hosts: result.append(copy_from(path="bar/*.txt", resources=[host])) return result - + # configuration -set_defaults(ssh_config(username=os.username, port="%s")) +set_defaults(ssh_config(username="%s", port="%s", private_key_path="%s")) hosts = resources(provider=host_list_provider(hosts=["127.0.0.1","localhost"])) -result = cp(hosts)`, port), +result = cp(hosts)`, username, port, privateKey), eval: func(t *testing.T, script string) { exe := New() if err := exe.Exec("test.star", strings.NewReader(script)); err != nil { @@ -356,7 +354,7 @@ result = cp(hosts)`, port), }, } - sshArgs := ssh.SSHArgs{User: getUsername(), Host: "127.0.0.1", Port: port} + sshArgs := ssh.SSHArgs{User: username, Host: "127.0.0.1", Port: port, PrivateKeyPath: privateKey} for _, test := range tests { for file, content := range test.remoteFiles { ssh.MakeTestSSHFile(t, sshArgs, file, content) @@ -374,18 +372,13 @@ result = cp(hosts)`, port), } func TestCopyFuncSSHAll(t *testing.T) { - port := testcrashd.NextPortValue() - sshSvr := testcrashd.NewSSHServer(testcrashd.NextResourceName(), port) - - logrus.Debug("Attempting to start SSH server") - if err := sshSvr.Start(); err != nil { - logrus.Error(err) - os.Exit(1) - } + port := testSupport.PortValue() + username := testSupport.CurrentUsername() + privateKey := testSupport.PrivateKeyPath() tests := []struct { name string - test func(t *testing.T, port string) + test func(t *testing.T, port, privateKey, username string) }{ {name: "copyFrom func for host resources", test: testCopyFuncForHostResources}, {name: "copy_from script for host resources", test: testCopyFuncScriptForHostResources}, @@ -393,14 +386,8 @@ func TestCopyFuncSSHAll(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - test.test(t, port) + test.test(t, port, privateKey, username) defer os.RemoveAll(defaults.workdir) }) } - - logrus.Debug("Stopping SSH server...") - if err := sshSvr.Stop(); err != nil { - logrus.Error(err) - os.Exit(1) - } } diff --git a/starlark/kube_capture_test.go b/starlark/kube_capture_test.go index 0293bf0e..a30d1887 100644 --- a/starlark/kube_capture_test.go +++ b/starlark/kube_capture_test.go @@ -6,7 +6,6 @@ package starlark import ( "fmt" "io/ioutil" - "os" "path/filepath" "strings" @@ -21,7 +20,6 @@ import ( var _ = Describe("kube_capture", func() { var ( - workdir string executor *Executor err error ) @@ -31,15 +29,6 @@ var _ = Describe("kube_capture", func() { err = executor.Exec("test.kube.capture", strings.NewReader(crashdScript)) } - BeforeEach(func() { - workdir, err = ioutil.TempDir(os.TempDir(), "test") - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - os.RemoveAll(workdir) - }) - It("creates a directory and files for namespaced objects", func() { crashdScript := fmt.Sprintf(` crashd_config(workdir="%s") diff --git a/starlark/main_test.go b/starlark/main_test.go index 27b18a60..a15eb1cd 100644 --- a/starlark/main_test.go +++ b/starlark/main_test.go @@ -4,26 +4,56 @@ package starlark import ( + "fmt" "os" "testing" + "github.com/sirupsen/logrus" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" testcrashd "github.com/vmware-tanzu/crash-diagnostics/testing" ) +var ( + testSupport *testcrashd.TestSupport +) + func TestMain(m *testing.M) { - testcrashd.Init() - os.Exit(m.Run()) + test, err := testcrashd.Init() + if err != nil { + logrus.Fatal(err) + } + testSupport = test + + if err := testSupport.SetupSSHServer(); err != nil { + logrus.Fatal(err) + } + + if err := testSupport.SetupKindCluster(); err != nil { + logrus.Fatal(err) + } + + // precaution + if testSupport == nil { + logrus.Fatal("failed to setup test support") + } + + result := m.Run() + + if err := testSupport.TearDown(); err != nil { + logrus.Fatal(err) + } + + os.Exit(result) } -func makeTestSSHConfig(pkPath, port string) *starlarkstruct.Struct { +func makeTestSSHConfig(pkPath, port, username string) *starlarkstruct.Struct { return starlarkstruct.FromStringDict(starlarkstruct.Default, starlark.StringDict{ - identifiers.username: starlark.String(getUsername()), + identifiers.username: starlark.String(username), identifiers.port: starlark.String(port), identifiers.privateKeyPath: starlark.String(pkPath), - identifiers.maxRetries: starlark.String(defaults.connRetries), + identifiers.maxRetries: starlark.String(fmt.Sprintf("%d", testSupport.MaxConnectionRetries())), }) } diff --git a/starlark/run_test.go b/starlark/run_test.go index e1705d5a..62a0fdd8 100644 --- a/starlark/run_test.go +++ b/starlark/run_test.go @@ -5,18 +5,14 @@ package starlark import ( "fmt" - "os" "strings" "testing" - "github.com/sirupsen/logrus" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - - testcrashd "github.com/vmware-tanzu/crash-diagnostics/testing" ) -func testRunFuncHostResources(t *testing.T, port string) { +func testRunFuncHostResources(t *testing.T, port, privateKey, username string) { tests := []struct { name string args func(t *testing.T) starlark.Tuple @@ -27,7 +23,7 @@ func testRunFuncHostResources(t *testing.T, port string) { name: "default arg single machine", args: func(t *testing.T) starlark.Tuple { return starlark.Tuple{starlark.String("echo 'Hello World!'")} }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{makeTestSSHHostResource("127.0.0.1", sshCfg)}) return []starlark.Tuple{[]starlark.Value{starlark.String("resources"), resources}} }, @@ -55,7 +51,7 @@ func testRunFuncHostResources(t *testing.T, port string) { name: "kwargs single machine", args: func(t *testing.T) starlark.Tuple { return nil }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{makeTestSSHHostResource("127.0.0.1", sshCfg)}) return []starlark.Tuple{ []starlark.Value{starlark.String("cmd"), starlark.String("echo 'Hello World!'")}, @@ -86,7 +82,7 @@ func testRunFuncHostResources(t *testing.T, port string) { name: "multiple machines", args: func(t *testing.T) starlark.Tuple { return nil }, kwargs: func(t *testing.T) []starlark.Tuple { - sshCfg := makeTestSSHConfig(defaults.pkPath, port) + sshCfg := makeTestSSHConfig(privateKey, port, username) resources := starlark.NewList([]starlark.Value{ makeTestSSHHostResource("localhost", sshCfg), makeTestSSHHostResource("127.0.0.1", sshCfg), @@ -132,7 +128,7 @@ func testRunFuncHostResources(t *testing.T, port string) { } } -func testRunFuncScriptHostResources(t *testing.T, port string) { +func testRunFuncScriptHostResources(t *testing.T, port, privateKey, username string) { tests := []struct { name string script string @@ -141,9 +137,9 @@ func testRunFuncScriptHostResources(t *testing.T, port string) { { name: "default cmd multiple machines", script: fmt.Sprintf(` -set_defaults(ssh_config(username=os.username, port="%s")) +set_defaults(ssh_config(username="%s", port="%s", private_key_path="%s")) set_defaults(resources(hosts=["127.0.0.1","localhost"])) -result = run("echo 'Hello World!'")`, port), +result = run("echo 'Hello World!'")`, username, port, privateKey), eval: func(t *testing.T, script string) { exe := New() if err := exe.Exec("test.star", strings.NewReader(script)); err != nil { @@ -187,9 +183,8 @@ def exec(hosts): return result # configuration -ssh_config(username=os.username, port="%s") -hosts = resources(provider=host_list_provider(hosts=["127.0.0.1","localhost"])) -result = exec(hosts)`, port), +hosts = resources(provider=host_list_provider(hosts=["127.0.0.1","localhost"], ssh_config = ssh_config(username="%s", port="%s", private_key_path="%s"))) +result = exec(hosts)`, username, port, privateKey), eval: func(t *testing.T, script string) { exe := New() if err := exe.Exec("test.star", strings.NewReader(script)); err != nil { @@ -231,30 +226,19 @@ result = exec(hosts)`, port), } func TestRunFuncSSHAll(t *testing.T) { - port := testcrashd.NextPortValue() - sshSvr := testcrashd.NewSSHServer(testcrashd.NextResourceName(), port) - - logrus.Debug("Attempting to start SSH server") - if err := sshSvr.Start(); err != nil { - logrus.Error(err) - os.Exit(1) - } + port := testSupport.PortValue() + username := testSupport.CurrentUsername() + privateKey := testSupport.PrivateKeyPath() tests := []struct { name string - test func(t *testing.T, port string) + test func(t *testing.T, port, key, username string) }{ {name: "testRunFuncWithHostResources", test: testRunFuncHostResources}, - {name: "testRunFuncScriptWithHostResources", test: testRunFuncHostResources}, + {name: "testRunFuncScriptWithHostResources", test: testRunFuncScriptHostResources}, } for _, test := range tests { - t.Run(test.name, func(t *testing.T) { test.test(t, port) }) - } - - logrus.Debug("Stopping SSH server...") - if err := sshSvr.Stop(); err != nil { - logrus.Error(err) - os.Exit(1) + t.Run(test.name, func(t *testing.T) { test.test(t, port, privateKey, username) }) } } diff --git a/starlark/starlark_suite_test.go b/starlark/starlark_suite_test.go index 763bb9a0..b1c9fc1d 100644 --- a/starlark/starlark_suite_test.go +++ b/starlark/starlark_suite_test.go @@ -4,48 +4,30 @@ package starlark import ( - "io/ioutil" - "os" "testing" - "time" - - "github.com/sirupsen/logrus" - testcrashd "github.com/vmware-tanzu/crash-diagnostics/testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var ( - kind *testcrashd.KindCluster - waitTime = time.Second * 11 k8sconfig string + workdir string ) -func TestStarlark(t *testing.T) { +func TestStarlarkSuite(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Starlark Suite") } var _ = BeforeSuite(func() { - clusterName := "crashd-test-cluster" - tmpFile, err := ioutil.TempFile(os.TempDir(), clusterName) - Expect(err).NotTo(HaveOccurred()) - k8sconfig = tmpFile.Name() - - // create kind cluster - kind = testcrashd.NewKindCluster("../testing/kind-cluster-docker.yaml", clusterName) - err = kind.Create() + // setup (if necessary) and retrieve kind's kubecfg + k8sCfg, err := testSupport.SetupKindKubeConfig() Expect(err).NotTo(HaveOccurred()) - - err = kind.MakeKubeConfigFile(k8sconfig) - Expect(err).NotTo(HaveOccurred()) - - logrus.Infof("Sleeping %v ... waiting for pods", waitTime) - time.Sleep(waitTime) + k8sconfig = k8sCfg + workdir = testSupport.TmpDirRoot() }) var _ = AfterSuite(func() { - kind.Destroy() - os.RemoveAll(k8sconfig) + // clean up is done in main_test.go }) diff --git a/testing/id_rsa b/testing/id_rsa new file mode 100644 index 00000000..378ae73e --- /dev/null +++ b/testing/id_rsa @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/testing/id_rsa.pub b/testing/id_rsa.pub new file mode 100644 index 00000000..c22cc548 --- /dev/null +++ b/testing/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDG5ob1agkTUJVc1sAGG9Ey+YEJiYzai5K0QFy7fRTp590pUwDOdatd9zTA7tE3fhhHqZOx+5YKNAheBco3jUu9SY5MaFpw/raXZblKgqULmq6JDQ1Xs4sT8FcPpD+TGNfEKQZEBGtZgZK3WMilUw9JmOMn25w858lLonz9vH5OPvrPh/MS8Gk7//pb/6SgHAKBlfhiSMiCqVxWhsK6IQua4kT8hsGV1gIFYNXEtKPPRUom07mp/CzJSxKbJbGJqpN4sSuSD8FA1LefNnDarL7JcartoR47WHm2ZuXaARZlbYVDFVIbsBR4u0gCzoOMlsPpwa2+RUSesBPh2+xtBZiGEKcmkWjn/gq48LwxRChtJB9aOgQ4a6msea+nVRWSnVl4c9kk6k/0QoWwQ5V3UmCxal8bg8b+wKWnA2bWWIJlNPxhvhSfmYGpOIyEIiPlFGZPEsZUzQaULrXQe3zuKHGLgoE9eKhKh1yG/sHDxzwy5iKEDKmluiPuNi0kL344ElxtL52C8ovP0HtcTg/5wvM+hNjqe7WdhYrEe8XScvxWojhrBvFDbBlWS14mfpejrcQGLcIqOZ6UDUUnasFEfGoacCSe8mSoj+l4mDx9ZX7a9IHbn8xZn6uEPIV7feY8Q7luVldGGeZfHOW1omwl+Ibc7Msd6Pvgp6ler30VKojlNQ== diff --git a/testing/key.go b/testing/key.go new file mode 100644 index 00000000..6caba54a --- /dev/null +++ b/testing/key.go @@ -0,0 +1,145 @@ +package testing + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" + "github.com/vladimirvivien/echo" + "golang.org/x/crypto/ssh" + + "github.com/pkg/errors" +) + +// GenerateRSAKeyFiles generates a public/private key pair and stores it in the directory passed as the input. +func GenerateRSAKeyFiles(directory, privFileName string) error { + priv, err := generatePrivateKey() + if err != nil { + return errors.Wrap(err, "could not generate private key") + } + rsaFile := filepath.Join(directory, privFileName) + err = ioutil.WriteFile(rsaFile, encodePrivateKeyToPEM(priv), 0600) + if err != nil { + return errors.Wrap(err, "could not write private key to file") + } + logrus.Info("Created private key PEM file:", rsaFile) + + pub, err := generatePublicKey(&priv.PublicKey) + if err != nil { + return errors.Wrap(err, "could not generate public key") + } + + pubFileName := fmt.Sprintf("%s.pub", privFileName) + rsaPubFile := filepath.Join(directory, pubFileName) + err = ioutil.WriteFile(rsaPubFile, pub, 0600) + if err != nil { + return errors.Wrap(err, "could not write public key to file") + } + logrus.Info("Created public key file:", rsaPubFile) + + return nil +} + +func generatePrivateKey() (*rsa.PrivateKey, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return nil, err + } + + // Validate Private Key + err = privateKey.Validate() + if err != nil { + return nil, err + } + + return privateKey, nil +} + +// encodePrivateKeyToPEM encodes Private Key from RSA to PEM format +func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte { + // Get ASN.1 DER format + privDER := x509.MarshalPKCS1PrivateKey(privateKey) + + // pem.Block + privBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Headers: nil, + Bytes: privDER, + } + + // Private key in PEM format + privatePEM := pem.EncodeToMemory(&privBlock) + + return privatePEM +} + +// generatePublicKey take a rsa.PublicKey and return bytes suitable for writing to .pub file +// returns in the format "ssh-rsa ..." +func generatePublicKey(privatekey *rsa.PublicKey) ([]byte, error) { + publicRsaKey, err := ssh.NewPublicKey(privatekey) + if err != nil { + return nil, err + } + + pubKeyBytes := ssh.MarshalAuthorizedKey(publicRsaKey) + return pubKeyBytes, nil +} + +func AddKeyToAgent(keyPath string) error { + e := echo.New() + + logrus.Info("Starting ssh-agent if needed...") + sshAgentCmd := e.Prog.Avail("ssh-agent") + if len(sshAgentCmd) == 0 { + return fmt.Errorf("ssh-agent not found") + } + var agentPID string + if aid := e.Eval("$SSH_AGENT_PID"); len(agentPID) == 0 { + proc := e.RunProc(fmt.Sprintf(`/bin/sh -c 'eval "$(%s)"'`, sshAgentCmd)) + if proc.Err() != nil { + return fmt.Errorf("ssh-agent failed: %s: %s", proc.Err(), proc.Result()) + } + result := proc.Result() + logrus.Infof("ssh-agent started: %s", result) + agentPID = strings.Split(result, " ")[2] + } else { + agentPID = aid + logrus.Infof("ssh-agent pid found: %s", aid) + } + + sshAddCmd := e.Prog.Avail("ssh-add") + if len(sshAddCmd) == 0 { + return fmt.Errorf("ssh-add not found") + } + + logrus.Debugf("adding key to ssh-agent (pid %s): %s", agentPID, keyPath) + + e.SetVar("ssh_agent_pid", agentPID) + p := e.RunProc(fmt.Sprintf(`/bin/sh -c 'SSH_AGENT_PID=%s %s %s'`, agentPID, sshAddCmd, keyPath)) + if p.Err() != nil { + return fmt.Errorf("failed to add SSH key to agent: %s: %s", p.Err(), p.Result()) + } + logrus.Infof("ssh-add result: %s", p.Result()) + return nil +} + +func RemoveKeyFromAgent(keyPath string) error { + e := echo.New() + sshAddCmd := e.Prog.Avail("ssh-add") + if len(sshAddCmd) == 0 { + return fmt.Errorf("ssh-add not found") + } + logrus.Debugf("removing key from ssh-agent: %s", keyPath) + p := e.RunProc(fmt.Sprintf("%s -d %s", sshAddCmd, keyPath)) + if p.Err() != nil { + return fmt.Errorf("failed to remove SSH key from agent: %s: %s", p.Err(), p.Result()) + } + logrus.Infof("removal key result: %s", p.Result()) + return nil +} diff --git a/testing/keys/id_rsa b/testing/keys/id_rsa deleted file mode 100644 index c8299b2d..00000000 --- a/testing/keys/id_rsa +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn -NhAAAAAwEAAQAAAQEAs0+ffHpJC8f6EDHOMo4HmGcR/XMspVZfeD1GDytThvBB2IcjGffG -RZFNVyZx46SlscqX0aSuS9/Vp3KgMX3YgybFttKn+Y8YQUaVH0Bww3kb4HFhV+I+DR6c5v -P5+ld3VPmwTLPU15x+WNq/s/h+tRznIX3NfCG0qftjzv5ytI2bymSVcVP6qJbTY2M6bUkM -DcgvWvg5YhU0A6qTA3/SGxkJW9FGwTk1kpRlxQ81RPGSq9Yn7KZ5KnJobGSDmJDo+e6QSc -7FDSFbWqIQs//kmeQWoYBuVMpu1G02RuhmPDIooWui8DP9S9XmXD5VJhqp6xtJQIK/EFxM -d0c2SbjulwAAA9gQ58J7EOfCewAAAAdzc2gtcnNhAAABAQCzT598ekkLx/oQMc4yjgeYZx -H9cyylVl94PUYPK1OG8EHYhyMZ98ZFkU1XJnHjpKWxypfRpK5L39WncqAxfdiDJsW20qf5 -jxhBRpUfQHDDeRvgcWFX4j4NHpzm8/n6V3dU+bBMs9TXnH5Y2r+z+H61HOchfc18IbSp+2 -PO/nK0jZvKZJVxU/qoltNjYzptSQwNyC9a+DliFTQDqpMDf9IbGQlb0UbBOTWSlGXFDzVE -8ZKr1ifspnkqcmhsZIOYkOj57pBJzsUNIVtaohCz/+SZ5BahgG5Uym7UbTZG6GY8Miiha6 -LwM/1L1eZcPlUmGqnrG0lAgr8QXEx3RzZJuO6XAAAAAwEAAQAAAQAUIx0GHbWWXR74Mp+1 -jb3Mn8alcAnTh5+xITB9A6Cdxt2eM479m5Xouii1YNvpdNQm41mpcZUhcEHOTFExPbDTCc -eqgH3cyPUwX3zfxZzkVvWKfzEvbXkKgCWeykeIlcoRAPmLo6aDkE+gKvDchUu1i0lpuXca -Oa7QaCsNVAYNwKlcEsb5mtwr+ob3ytmw8b4HWf3EQjboXdAPFiTmg7NkfY3Ad9tzARRklw -UBsUo/7rXLEYPnjQ/Xvwr19j36w10vM+8TxGvmiwkvkruymOkHTzggV/TKDCM17+vG/mmB -yRxWDvCZCDu98ARkF+NOVkcFhA+d5FGxDmlme0uEMvABAAAAgDcbxwmRTF7VPZ0t5h9F18 -Tf2WT49A8dia8jG3Ihm+J/yTpN3U1jigVqiWfbcV4evO6bP4g6UGn4+uSzNU3ESGhyEybi -EfIWuzAHeAX7zorRx7BSocZ7xe5xp/05pz86pnAXg3l7yY4KeLtx0XJsASvUJfd97ZWi8U -DdSCVuRQU5AAAAgQDa3RnU5jCW3s0hK9A9bXL+NiJ5VXP6sEmEo6rAiCjOaFTSZ6L4wJuV -wuxm+IOVK6I7O7+H5hm6W7MXLWiADJdQOi7V1vuzID4KHl5uI7lPBeR7/ybvB3GYTDaykh -NSzRmvOpUDFgp8fa4RiUrvgal6YFdw3kdC78ffXy9D0qKFdwAAAIEA0bxz1CXJlsAYEDBG -sXVry0zLYGDJa/YVaM35j9C+vUqp+usU2MFLAHLgbT5p0ZY3NkNP+4lMfPiGdT7xTYd54H -N3cLhEnKyljYb65SkoryukWqY+DJbGkXODQT1h28Zg4yt0cfCsHqSQYF417Bql3m+qn4zy -mFKpxEw+RxL3p+EAAAAedml2aWVudkB2aXZpZW52LWEwMS52bXdhcmUuY29tAQIDBAU= ------END OPENSSH PRIVATE KEY----- diff --git a/testing/keys/id_rsa.pub b/testing/keys/id_rsa.pub deleted file mode 100644 index 1ba6cdef..00000000 --- a/testing/keys/id_rsa.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzT598ekkLx/oQMc4yjgeYZxH9cyylVl94PUYPK1OG8EHYhyMZ98ZFkU1XJnHjpKWxypfRpK5L39WncqAxfdiDJsW20qf5jxhBRpUfQHDDeRvgcWFX4j4NHpzm8/n6V3dU+bBMs9TXnH5Y2r+z+H61HOchfc18IbSp+2PO/nK0jZvKZJVxU/qoltNjYzptSQwNyC9a+DliFTQDqpMDf9IbGQlb0UbBOTWSlGXFDzVE8ZKr1ifspnkqcmhsZIOYkOj57pBJzsUNIVtaohCz/+SZ5BahgG5Uym7UbTZG6GY8Miiha6LwM/1L1eZcPlUmGqnrG0lAgr8QXEx3RzZJuO6X vivienv@vivienv-a01.vmware.com diff --git a/testing/kindcluster.go b/testing/kindcluster.go index 04fb1086..d3848811 100644 --- a/testing/kindcluster.go +++ b/testing/kindcluster.go @@ -54,6 +54,7 @@ func (k *KindCluster) Create() error { } func (k *KindCluster) GetKubeConfig() (io.Reader, error) { + logrus.Infof("Retrieving kind kubeconfig for cluster: %s", k.name) p := k.e.RunProc(fmt.Sprintf(`kind get kubeconfig --name %s`, k.name)) if p.Err() != nil { return nil, p.Err() @@ -62,17 +63,19 @@ func (k *KindCluster) GetKubeConfig() (io.Reader, error) { } func (k *KindCluster) MakeKubeConfigFile(path string) error { - logrus.Debugf("Creating kind kubeconfig file: %s", path) - f, err := os.Create(path) + logrus.Infof("Creating kind kubeconfig file: %s", path) + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { - return err + return fmt.Errorf("failed to initialize kind kubeconfig file: %s", err) } + defer f.Close() + reader, err := k.GetKubeConfig() if err != nil { - return err + return fmt.Errorf("failed to generate kind kubeconfig: %s", err) } if _, err := io.Copy(f, reader); err != nil { - return err + return fmt.Errorf("failed to write kind kubeconfig file: %s", err) } return nil } @@ -92,8 +95,10 @@ func (k *KindCluster) Destroy() error { return fmt.Errorf("failed to install kind: %s: %s", p.Err(), p.Result()) } + logrus.Info("Kind cluster destroyed") + clusters := k.e.Run("kind get clusters") - logrus.Infof("kind clusters available: %s", clusters) + logrus.Infof("Available kind clusters: %s", clusters) return nil } diff --git a/testing/setup.go b/testing/setup.go index 10710bac..ca3a8401 100644 --- a/testing/setup.go +++ b/testing/setup.go @@ -4,41 +4,274 @@ package testing import ( + "errors" "flag" "fmt" "math/rand" + "os" + "os/user" + "path/filepath" "time" "github.com/sirupsen/logrus" + "github.com/vladimirvivien/echo" ) -var ( - InfraSetupWait = time.Second * 11 +const charset = "abcdefghijklmnopqrstuvwxyz" +var ( + InfraSetupWait = time.Second * 11 rnd = rand.New(rand.NewSource(time.Now().Unix())) sshContainerName = "test-sshd" sshPort = NextPortValue() ) -// Init initializes testing -func Init() { +type TestSupport struct { + username string + portValue string + resourceName string + testingRoot string + workdirRoot string + tmpDirRoot string + sshPKFileName string + sshPKFilePath string + maxConnRetries int + sshServer *SSHServer + kindKubeCfg string + kindCluster *KindCluster +} + +// Init initializes and returns TestSupport instance +func Init() (*TestSupport, error) { debug := false flag.BoolVar(&debug, "debug", debug, "Enables debug level") flag.Parse() + e := echo.New() logLevel := logrus.InfoLevel if debug { logLevel = logrus.DebugLevel } logrus.SetLevel(logLevel) + + // get username + username, err := Username() + if err != nil { + return nil, err + } + + resource := NextResourceName() + + // setup workdir + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, err + } + testingRoot := filepath.Join(homeDir, ".crashd-testing", resource) + if err := os.MkdirAll(testingRoot, 0765); err != nil && !os.IsExist(err) { + return nil, err + } + logrus.Infof("Created testing root dir: %s", testingRoot) + + workDir := filepath.Join(testingRoot, "work") + if err := os.MkdirAll(workDir, 0765); err != nil && !os.IsExist(err) { + return nil, err + } + logrus.Infof("Created testing work dir: %s", workDir) + + sshKeyPath, err := filepath.Abs(filepath.Join("..", "testing")) + if err != nil { + return nil, err + } + cpCmd := fmt.Sprintf(`/bin/sh -c "cp %s/id_rsa* %s"`, sshKeyPath, workDir) + logrus.Infof("Copying SSH key files: %s", cpCmd) + proc := e.RunProc(cpCmd) + if proc.Err() != nil { + logrus.Errorf("Error copying key files: %s %s", proc.Err(), proc.Result()) + return nil, proc.Err() + } + + // setup tempDir + tmpDirRoot := filepath.Join(testingRoot, "tmp") + if err := os.MkdirAll(tmpDirRoot, 0765); err != nil && !os.IsExist(err) { + return nil, err + } + logrus.Infof("Created testing temp root dir: %s", tmpDirRoot) + + pkName := "id_rsa" + pkPath := filepath.Join(workDir, pkName) + return &TestSupport{ + username: username, + portValue: NextPortValue(), + resourceName: resource, + testingRoot: testingRoot, + workdirRoot: workDir, + tmpDirRoot: tmpDirRoot, + sshPKFileName: pkName, + sshPKFilePath: pkPath, + maxConnRetries: 100, + }, nil +} + +// PortValue returns a string with a random value that can be used as port +func (t *TestSupport) PortValue() string { + return t.portValue +} + +// ResourceName resturns string that can be used to name resource +func (t *TestSupport) ResourceName() string { + return t.resourceName +} + +// CurrentUsername returns the current username or error +func (t *TestSupport) CurrentUsername() string { + return t.username +} + +func (t *TestSupport) WorkDirRoot() string { + return t.workdirRoot +} + +func (t *TestSupport) TmpDirRoot() string { + return t.tmpDirRoot +} + +func (t *TestSupport) PrivateKeyPath() string { + return t.sshPKFilePath +} + +func (t *TestSupport) MaxConnectionRetries() int { + return t.maxConnRetries +} + +func (t *TestSupport) SetupSSHServer() error { + if t.sshServer == nil { + //privKeyPath := filepath.Join(t.workdirRoot, t.sshPKFileName) + //if err := GenerateRSAKeyFiles(t.workdirRoot, t.sshPKFileName); err != nil { + // return err + //} + // + //if err := AddKeyToAgent(privKeyPath); err != nil { + // logrus.Errorf("Failed to add private key to SSH agent: %s", err) + //} else { + // logrus.Infof("Added private key to ssh-agent: %s ", privKeyPath) + //} + + server, err := NewSSHServer(t.resourceName, t.username, t.portValue, t.workdirRoot) + if err != nil { + return err + } + + if err := server.Start(); err != nil { + return err + } + + t.sshServer = server + } + return nil +} + +func (t *TestSupport) SetupKindCluster() error { + if t.kindCluster == nil { + yamlPath, err := filepath.Abs(filepath.Join("..", "./testing", "/kind-cluster-docker.yaml")) + if err != nil { + return err + } + + kind := NewKindCluster(yamlPath, t.resourceName) + if err := kind.Create(); err != nil { + return err + } + logrus.Infof("kind cluster created") + + // stall to wait for kind pods initialization + waitTime := time.Second * 10 + logrus.Debugf("waiting %s for kind pods to initialize...", waitTime) + time.Sleep(waitTime) + + t.kindCluster = kind + } + return nil +} + +func (t *TestSupport) SetupKindKubeConfig() (string, error) { + if t.kindCluster == nil { + return "", fmt.Errorf("kind not set: call SetupKindCluster() first") + } + + if len(t.kindKubeCfg) > 0 { + return t.kindKubeCfg, nil + } + + kubeCfgFile := filepath.Join(t.tmpDirRoot, "kubeconfig") + if err := t.kindCluster.MakeKubeConfigFile(kubeCfgFile); err != nil { + return "", err + } + t.kindKubeCfg = kubeCfgFile + return kubeCfgFile, nil } -//NextPortValue returns a pseudo-rando test [2200 .. 2230] +func (t *TestSupport) KindKubeConfigFile() string { + return t.kindKubeCfg +} + +func (t *TestSupport) TearDown() error { + var errs []error + + if t.kindCluster != nil { + logrus.Infof("Destroying kind cluster...") + if err := t.kindCluster.Destroy(); err != nil { + logrus.Error(err) + errs = append(errs, err) + } + } + + //privKeyPath := filepath.Join(t.workdirRoot, t.sshPKFileName) + //logrus.Infof("Removing private key from agent: %s", privKeyPath) + //if err := RemoveKeyFromAgent(privKeyPath); err != nil { + // logrus.Errorf("Unable to remove private key from SSH agent: %s", err) + //} + + if t.sshServer != nil { + logrus.Infof("Stopping SSH server container....") + if err := t.sshServer.Stop(); err != nil { + logrus.Error(err) + errs = append(errs, err) + } + time.Sleep(time.Millisecond * 500) + } + + logrus.Infof("Removing dir: %s", t.testingRoot) + if err := os.RemoveAll(t.testingRoot); err != nil { + // do return err: + // ssh-server container does not cleanly release mounted dir + // workaround to GitHub Actions permission issue during tests + logrus.Errorf("Unable to remove testing root dir: %s", err) + } + + if errs != nil { + return errors.New(fmt.Sprintf("%v", errs)) + } + + return nil +} + +//NextPortValue returns a pseudo-rando test [2200 .. 2290] func NextPortValue() string { port := 2200 + rnd.Intn(90) return fmt.Sprintf("%d", port) } +// NextResourceName returns crashd-test-XXXX name func NextResourceName() string { return fmt.Sprintf("crashd-test-%x", rnd.Uint64()) } + +// Username returns current username +func Username() (string, error) { + usr, err := user.Current() + if err != nil { + return "", err + } + return usr.Username, nil +} diff --git a/testing/sshserver.go b/testing/sshserver.go index 1ea69713..4b519e8d 100644 --- a/testing/sshserver.go +++ b/testing/sshserver.go @@ -5,6 +5,7 @@ package testing import ( "fmt" + "path/filepath" "strings" "github.com/sirupsen/logrus" @@ -12,13 +13,21 @@ import ( ) type SSHServer struct { - name string - port string - e *echo.Echo + name string + port string + mountDir string + username string + e *echo.Echo } -func NewSSHServer(name, port string) *SSHServer { - return &SSHServer{name: name, port: port, e: echo.New()} +func NewSSHServer(serverName, username, port, sshMountDir string) (*SSHServer, error) { + return &SSHServer{ + name: serverName, + port: port, + mountDir: sshMountDir, + username: username, + e: echo.New(), + }, nil } // StartSSHServer starts starts sshd process using image linuxserver/openssh-server.DockerRunSSH @@ -31,7 +40,7 @@ docker create \ -e USER_NAME=$USER \ -e SUDO_ACCESS=true \ -p 2222:2222 \ - -v $HOME/.ssh:/config + -v ./testing/server-name:/config linuxserver/openssh-server */ @@ -47,9 +56,13 @@ func (s *SSHServer) Start() error { s.e.SetVar("CONTAINER_NAME", s.name) s.e.SetVar("SSH_PORT", fmt.Sprintf("%s:2222", s.port)) - s.e.SetVar("SSH_DOCKER_IMAGE", "vladimirvivien/openssh-server") - cmd := s.e.Eval("docker run --rm --detach --name=$CONTAINER_NAME -p $SSH_PORT -e PUBLIC_KEY_FILE=/config/id_rsa.pub -e USER_NAME=$USER -e SUDO_ACCESS=true -v $HOME/.ssh:/config $SSH_DOCKER_IMAGE") - logrus.Debugf("Starting SSH server: %s", cmd) + s.e.SetVar("SSH_DOCKER_IMAGE", "linuxserver/openssh-server") + s.e.SetVar("USERNAME", s.username) + s.e.SetVar("KEY_VOLUME_MOUNT", s.mountDir) + s.e.SetVar("DOCKER_MODS", "linuxserver/mods:openssh-server-openssh-client") + + cmd := s.e.Eval("docker run --rm --detach --name=$CONTAINER_NAME -p $SSH_PORT -e PUBLIC_KEY_FILE=/config/id_rsa.pub -e USER_NAME=$USERNAME -e DOCKER_MODS=$DOCKER_MODS -e SUDO_ACCESS=true -v $KEY_VOLUME_MOUNT:/config $SSH_DOCKER_IMAGE") + logrus.Infof("Starting SSH server: %s", cmd) proc := s.e.RunProc(cmd) result := proc.Result() if proc.Err() != nil { @@ -79,10 +92,9 @@ func (s *SSHServer) Stop() error { msg := fmt.Sprintf("failed to stop container: %s: %s", proc.Err(), result) return fmt.Errorf(msg) } - logrus.Info("SSH server stopped: ", result) // attempt to remove container if still lingering - if strings.Contains(s.e.Run("docker ps"), s.name) { + if strings.Contains(s.e.Run("docker ps -a"), s.name) { logrus.Info("Forcing container removal:", s.name) proc := s.e.RunProc("docker rm --force $CONTAINER_NAME") result := proc.Result() @@ -95,3 +107,11 @@ func (s *SSHServer) Stop() error { return nil } + +func (s *SSHServer) MountedDir() string { + return s.mountDir +} + +func (s *SSHServer) PrivateKey() string { + return filepath.Join(s.mountDir, "id_rsa") +}