Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation of run starlark function
This patch implements the Go code for starlark builtin function run(). The function allows Crashd script to execute commands on specified compute resources. This patch also does the followings: - Add tests for run - Updates test harness for e2e tests - Disable tests for packages exec, script, and parser Signed-off-by: Vladimir Vivien <vivienv@vmware.com>
- Loading branch information
1 parent
f639bed
commit 1dda5e6
Showing
16 changed files
with
1,079 additions
and
237 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// Copyright (c) 2020 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package ssh | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/sirupsen/logrus" | ||
"github.com/vladimirvivien/echo" | ||
"k8s.io/apimachinery/pkg/util/wait" | ||
) | ||
|
||
type JumpProxyArg struct { | ||
User string | ||
Host string | ||
} | ||
|
||
type SSHArgs struct { | ||
User string | ||
Host string | ||
PrivateKeyPath string | ||
Port string | ||
MaxRetries int | ||
JumpProxy *JumpProxyArg | ||
} | ||
|
||
func Run(args SSHArgs, cmd string) (string, error) { | ||
e := echo.New() | ||
sshCmd, err := makeSSHCmdStr(args) | ||
if err != nil { | ||
return "", err | ||
} | ||
effectiveCmd := fmt.Sprintf(`%s "%s"`, sshCmd, cmd) | ||
logrus.Debug("ssh.Run: ", effectiveCmd) | ||
|
||
var result string | ||
maxRetries := args.MaxRetries | ||
if maxRetries == 0 { | ||
maxRetries = 10 | ||
} | ||
retries := wait.Backoff{Steps: maxRetries, Duration: time.Millisecond * 80, Jitter: 0.1} | ||
if err := wait.ExponentialBackoff(retries, func() (bool, error) { | ||
p := e.RunProc(effectiveCmd) | ||
if p.Err() != nil { | ||
logrus.Warn(fmt.Sprintf("unable to connect: %s", p.Err())) | ||
return false, nil | ||
} | ||
result = p.Result() | ||
return true, nil // worked | ||
}); err != nil { | ||
logrus.Debugf("ssh.Run failed after %d tries", maxRetries) | ||
return "", err | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
func SSHCapture(args SSHArgs, cmd string, path string) error { | ||
return nil | ||
} | ||
|
||
func makeSSHCmdStr(args SSHArgs) (string, error) { | ||
if args.User == "" { | ||
return "", fmt.Errorf("SSH: user is required") | ||
} | ||
if args.Host == "" { | ||
return "", fmt.Errorf("SSH: host is required") | ||
} | ||
|
||
if args.JumpProxy != nil { | ||
if args.JumpProxy.User == "" || args.JumpProxy.Host == "" { | ||
return "", fmt.Errorf("SSH: jump user and host are required") | ||
} | ||
} | ||
|
||
sshCmdPrefix := func() string { | ||
if logrus.GetLevel() == logrus.DebugLevel { | ||
return "ssh -q -o StrictHostKeyChecking=no -v" | ||
} | ||
return "ssh -q -o StrictHostKeyChecking=no" | ||
} | ||
|
||
pkPath := func() string { | ||
if args.PrivateKeyPath != "" { | ||
return fmt.Sprintf("-i %s", args.PrivateKeyPath) | ||
} | ||
return "" | ||
} | ||
|
||
port := func() string { | ||
if args.Port == "" { | ||
return "-p 22" | ||
} | ||
return fmt.Sprintf("-p %s", args.Port) | ||
} | ||
|
||
jumpProxy := func() string { | ||
if args.JumpProxy != nil { | ||
return fmt.Sprintf("-J %s@%s", args.JumpProxy.User, args.JumpProxy.Host) | ||
} | ||
return "" | ||
} | ||
// build command as | ||
// ssh -i <pkpath> -P <port> -J <jumpproxy> user@host | ||
cmd := fmt.Sprintf( | ||
`%s %s %s %s %s@%s`, | ||
sshCmdPrefix(), pkPath(), port(), jumpProxy(), args.User, args.Host, | ||
) | ||
return cmd, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Copyright (c) 2020 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package ssh | ||
|
||
import ( | ||
"os" | ||
"os/user" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestSSHRun(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 | ||
cmd string | ||
result string | ||
}{ | ||
{ | ||
name: "simple cmd", | ||
args: SSHArgs{User: usr.Username, PrivateKeyPath: pkPath, Host: "127.0.0.1", Port: testSSHPort, MaxRetries: 100}, | ||
cmd: "echo 'Hello World!'", | ||
result: "Hello World!", | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
expected, err := Run(test.args, test.cmd) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if test.result != expected { | ||
t.Fatalf("unexpected result %s", expected) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestSSHRunMakeCmdStr(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
args SSHArgs | ||
cmdStr string | ||
shouldFail bool | ||
}{ | ||
{ | ||
name: "user and host", | ||
args: SSHArgs{User: "sshuser", Host: "local.host"}, | ||
cmdStr: "ssh -q -o StrictHostKeyChecking=no -p 22 sshuser@local.host", | ||
}, | ||
{ | ||
name: "user host and pkpath", | ||
args: SSHArgs{User: "sshuser", Host: "local.host", PrivateKeyPath: "/pk/path"}, | ||
cmdStr: "ssh -q -o StrictHostKeyChecking=no -i /pk/path -p 22 sshuser@local.host", | ||
}, | ||
{ | ||
name: "user host pkpath and proxy", | ||
args: SSHArgs{User: "sshuser", Host: "local.host", PrivateKeyPath: "/pk/path", JumpProxy: &JumpProxyArg{User: "juser", Host: "jhost"}}, | ||
cmdStr: "ssh -q -o StrictHostKeyChecking=no -i /pk/path -p 22 -J juser@jhost sshuser@local.host", | ||
}, | ||
{ | ||
name: "missing host", | ||
args: SSHArgs{User: "sshuser"}, | ||
shouldFail: true, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
result, err := makeSSHCmdStr(test.args) | ||
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) | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright (c) 2020 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package starlark | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"go.starlark.net/starlark" | ||
"go.starlark.net/starlarkstruct" | ||
|
||
testcrashd "github.com/vmware-tanzu/crash-diagnostics/testing" | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
testcrashd.Init() | ||
os.Exit(m.Run()) | ||
} | ||
|
||
func makeTestSSHConfig(pkPath, port string) *starlarkstruct.Struct { | ||
return starlarkstruct.FromStringDict(starlarkstruct.Default, starlark.StringDict{ | ||
identifiers.username: starlark.String(getUsername()), | ||
identifiers.port: starlark.String(port), | ||
identifiers.privateKeyPath: starlark.String(pkPath), | ||
}) | ||
} | ||
|
||
func makeTestSSHHostResource(addr string, sshCfg *starlarkstruct.Struct) *starlarkstruct.Struct { | ||
return starlarkstruct.FromStringDict( | ||
starlarkstruct.Default, | ||
starlark.StringDict{ | ||
"kind": starlark.String(identifiers.hostResource), | ||
"provider": starlark.String(identifiers.hostListProvider), | ||
"host": starlark.String(addr), | ||
"transport": starlark.String("ssh"), | ||
"ssh_config": sshCfg, | ||
}, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright (c) 2020 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package starlark | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"runtime" | ||
|
||
"go.starlark.net/starlark" | ||
"go.starlark.net/starlarkstruct" | ||
) | ||
|
||
func setupOSStruct() *starlarkstruct.Struct { | ||
return starlarkstruct.FromStringDict(starlarkstruct.Default, | ||
starlark.StringDict{ | ||
"name": starlark.String(runtime.GOOS), | ||
"username": starlark.String(getUsername()), | ||
"homedir": starlark.String(os.Getenv("HOME")), | ||
"getenv": starlark.NewBuiltin("getenv", getEnvFunc), | ||
}, | ||
) | ||
} | ||
|
||
func getEnvFunc(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { | ||
if args == nil || args.Len() == 0 { | ||
return starlark.None, nil | ||
} | ||
key, ok := args.Index(0).(starlark.String) | ||
if !ok { | ||
return starlark.None, fmt.Errorf("os.getenv: invalid env key") | ||
} | ||
|
||
return starlark.String(os.Getenv(string(key))), nil | ||
} |
Oops, something went wrong.