From a46c35886fe6fc35d8f739fc818fc04f40336ca1 Mon Sep 17 00:00:00 2001 From: Vladimir Vivien Date: Wed, 1 Jul 2020 19:10:58 -0400 Subject: [PATCH] Implementation of the Starlark run_local() function This patch implements the Go code for starlark builtin function run_local(). This function allows Crashd script to run arbitrary command from on the local machine. This patch does the followings: - Adds Go function to support starlark builtin func for run_local - Adds and updates tests for run_local Signed-off-by: Vladimir Vivien --- starlark/run_local.go | 32 +++++++++++++++ starlark/run_local_test.go | 84 ++++++++++++++++++++++++++++++++++++++ starlark/starlark_exec.go | 1 + starlark/support.go | 2 + 4 files changed, 119 insertions(+) create mode 100644 starlark/run_local.go create mode 100644 starlark/run_local_test.go diff --git a/starlark/run_local.go b/starlark/run_local.go new file mode 100644 index 00000000..65622f16 --- /dev/null +++ b/starlark/run_local.go @@ -0,0 +1,32 @@ +// Copyright (c) 2020 VMware, Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package starlark + +import ( + "fmt" + + "github.com/vladimirvivien/echo" + "go.starlark.net/starlark" +) + +// runLocalFunc is a built-in starlark function that runs a provided command on the local machine. +// It returns the result of the command as struct containing information about the executed command. +// Starlark format: run_local() +func runLocalFunc(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var cmdStr string + if args != nil && args.Len() == 1 { + cmd, ok := args.Index(0).(starlark.String) + if !ok { + return starlark.None, fmt.Errorf("%s: command must be a string", identifiers.runLocal) + } + cmdStr = string(cmd) + } + + p := echo.New().RunProc(cmdStr) + if p.Err() != nil { + return starlark.None, fmt.Errorf("%s: %s", identifiers.runLocal, p.Err()) + } + + return starlark.String(p.Result()), nil +} \ No newline at end of file diff --git a/starlark/run_local_test.go b/starlark/run_local_test.go new file mode 100644 index 00000000..426bec1e --- /dev/null +++ b/starlark/run_local_test.go @@ -0,0 +1,84 @@ +// Copyright (c) 2020 VMware, Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package starlark + +import ( + "strings" + "testing" + + "go.starlark.net/starlark" +) + +func TestRunLocalFunc(t *testing.T) { + tests := []struct { + name string + args func(t *testing.T) starlark.Tuple + eval func(t *testing.T, args starlark.Tuple) + }{ + { + name: "simple command", + args: func(t *testing.T) starlark.Tuple { return starlark.Tuple{starlark.String("echo 'Hello World!'")} }, + eval: func(t *testing.T, args starlark.Tuple) { + val, err := runLocalFunc(newTestThreadLocal(t), nil, args, nil) + if err != nil { + t.Fatal(err) + } + result := "" + if r, ok := val.(starlark.String); ok { + result = string(r) + } + if result != "Hello World!" { + t.Errorf("unexpected result: %s", result) + } + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.eval(t, test.args(t)) + }) + } +} + +func TestRunLocalScript(t *testing.T) { + tests := []struct { + name string + script string + eval func(t *testing.T, script string) + }{ + { + name: "run local", + script: ` +result = run_local("""echo 'Hello World!'""") +`, + eval: func(t *testing.T, script string) { + exe := New() + if err := exe.Exec("test.star", strings.NewReader(script)); err != nil { + t.Fatal(err) + } + + resultVal := exe.result["result"] + if resultVal == nil { + t.Fatal("run_local() should be assigned to a variable for test") + } + result, ok := resultVal.(starlark.String) + if !ok { + t.Fatal("run_local() should return a string") + } + + if string(result) != "Hello World!" { + t.Fatalf("uneexpected result %s", result) + } + + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.eval(t, test.script) + }) + } +} diff --git a/starlark/starlark_exec.go b/starlark/starlark_exec.go index 9c4b5a4f..08f627d6 100644 --- a/starlark/starlark_exec.go +++ b/starlark/starlark_exec.go @@ -75,6 +75,7 @@ func newPredeclareds() starlark.StringDict { identifiers.hostListProvider: starlark.NewBuiltin(identifiers.hostListProvider, hostListProvider), identifiers.resources: starlark.NewBuiltin(identifiers.resources, resourcesFunc), identifiers.run: starlark.NewBuiltin(identifiers.run, runFunc), + identifiers.runLocal: starlark.NewBuiltin(identifiers.runLocal, runLocalFunc), identifiers.capture: starlark.NewBuiltin(identifiers.capture, captureFunc), identifiers.copyFrom: starlark.NewBuiltin(identifiers.copyFrom, copyFromFunc), identifiers.kubeCfg: starlark.NewBuiltin(identifiers.kubeCfg, kubeConfigFn), diff --git a/starlark/support.go b/starlark/support.go index bdc9f427..da324f68 100644 --- a/starlark/support.go +++ b/starlark/support.go @@ -34,6 +34,7 @@ var ( hostResource string resources string run string + runLocal string capture string copyFrom string @@ -55,6 +56,7 @@ var ( hostResource: "host_resource", resources: "resources", run: "run", + runLocal: "run_local", capture: "capture", copyFrom: "copy_from",