From d46aae6938f3f73e17aed040b37df159d668bf39 Mon Sep 17 00:00:00 2001 From: Benjamin DeCoste Date: Wed, 22 Mar 2023 16:23:33 -0300 Subject: [PATCH] fix -o for cape run (#255) this previously always output plain output --- cape.go | 6 ++++++ cmd/cape/cmd/list.go | 4 ++-- cmd/cape/cmd/run.go | 33 +++++++++++++++++++++++++++++++-- cmd/cape/cmd/test_test.go | 29 +++++++++++++++-------------- entities/entities.go | 5 ----- protocol/protocol.go | 8 +++++--- sdk/deploy.go | 4 +++- sdk/run.go | 8 +++++--- sdk/test.go | 4 +++- sdk/test_test.go | 10 ++++++---- 10 files changed, 76 insertions(+), 35 deletions(-) create mode 100644 cape.go diff --git a/cape.go b/cape.go new file mode 100644 index 00000000..f549884e --- /dev/null +++ b/cape.go @@ -0,0 +1,6 @@ +package cli + +type RunResult struct { + Type string `json:"type"` + Message []byte `json:"message"` +} diff --git a/cmd/cape/cmd/list.go b/cmd/cape/cmd/list.go index a6763139..c91a00fa 100644 --- a/cmd/cape/cmd/list.go +++ b/cmd/cape/cmd/list.go @@ -135,14 +135,14 @@ func doList(url string, insecure bool, auth entities.FunctionAuth, limit int, of return nil } - if f, ok := formatters[format]; ok { + if f, ok := listFormatters[format]; ok { return f(deploymentNames) } return printTable(deploymentNames) } -var formatters = map[string]func([]entities.Deployment) error{ +var listFormatters = map[string]func([]entities.Deployment) error{ "plain": printTable, "json": printJSON, } diff --git a/cmd/cape/cmd/run.go b/cmd/cape/cmd/run.go index 1845600a..2464ae66 100644 --- a/cmd/cape/cmd/run.go +++ b/cmd/cape/cmd/run.go @@ -3,6 +3,7 @@ package cmd import ( "bytes" "encoding/hex" + "encoding/json" "errors" "fmt" "io" @@ -12,6 +13,8 @@ import ( "github.com/spf13/cobra" + "github.com/capeprivacy/cli" + "github.com/capeprivacy/cli/entities" "github.com/capeprivacy/cli/sdk" ) @@ -78,6 +81,15 @@ func init() { func run(cmd *cobra.Command, args []string) error { u := C.EnclaveHost insecure := C.Insecure + output, err := cmd.Flags().GetString("output") + if err != nil { + return err + } + + formatter, ok := runFormatters[output] + if !ok { + return fmt.Errorf("unknown output option: %s", output) + } if len(args) < 1 { return UserError{Msg: "you must pass a function ID or a function name", Err: fmt.Errorf("invalid number of input arguments")} @@ -155,8 +167,7 @@ func run(cmd *cobra.Command, args []string) error { return fmt.Errorf("run request failed: %w", err) } - fmt.Println(string(results)) - return nil + return formatter(*results) } func isValidFunctionID(functionID string) bool { @@ -204,3 +215,21 @@ func getInput(cmd *cobra.Command, args []string, file string) ([]byte, error) { return nil, UserError{Msg: "invalid input", Err: errors.New("please provide input as a string, input file or stdin")} } } + +var runFormatters = map[string]func(result cli.RunResult) error{ + "plain": runPlain, + "json": runJSON, +} + +func runPlain(result cli.RunResult) error { + fmt.Println(string(result.Message)) + return nil +} + +func runJSON(result cli.RunResult) error { + return json.NewEncoder(os.Stdout).Encode(struct { + Output string `json:"output"` + }{ + Output: string(result.Message), + }) +} diff --git a/cmd/cape/cmd/test_test.go b/cmd/cape/cmd/test_test.go index 1c2994dc..0ec03f47 100644 --- a/cmd/cape/cmd/test_test.go +++ b/cmd/cape/cmd/test_test.go @@ -16,7 +16,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/capeprivacy/cli/entities" + "github.com/capeprivacy/cli" + "github.com/capeprivacy/cli/sdk" czip "github.com/capeprivacy/cli/zip" ) @@ -83,7 +84,7 @@ func TestServerError(t *testing.T) { cmd.SetArgs([]string{"test", "testdata/my_fn", "hello world"}) errMsg := "something went wrong" - test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { + test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { return nil, errors.New(errMsg) } authToken = func() (string, error) { @@ -114,9 +115,9 @@ func TestSuccess(t *testing.T) { results := "success!" var gotFn []byte var gotInput []byte - test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { + test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { gotFn, gotInput = testReq.Function, testReq.Input - return &entities.RunResults{Message: []byte(results)}, nil + return &cli.RunResult{Message: []byte(results)}, nil } authToken = func() (string, error) { return "so logged in", nil @@ -158,9 +159,9 @@ func TestSuccessStdin(t *testing.T) { results := "success!" var gotFn []byte var gotInput []byte - test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { + test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { gotFn, gotInput = testReq.Function, testReq.Input - return &entities.RunResults{Message: []byte(results)}, nil + return &cli.RunResult{Message: []byte(results)}, nil } authToken = func() (string, error) { return "so logged in", nil @@ -202,7 +203,7 @@ func TestWSConnection(t *testing.T) { type msg struct { Message []byte `json:"msg"` } - test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { + test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { c, _, err := websocket.DefaultDialer.Dial(endpoint, nil) if err != nil { return nil, err @@ -214,7 +215,7 @@ func TestWSConnection(t *testing.T) { return nil, err } - return &entities.RunResults{Message: m.Message}, nil + return &cli.RunResult{Message: m.Message}, nil } authToken = func() (string, error) { return "so logged in", nil @@ -260,9 +261,9 @@ func TestWSConnection(t *testing.T) { func TestEndpoint(t *testing.T) { // ensure that `cape test` hits the `/v1/test` endpoint endpointHit := "" - test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { + test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { endpointHit = endpoint - return &entities.RunResults{Message: []byte("good job")}, nil + return &cli.RunResult{Message: []byte("good job")}, nil } authToken = func() (string, error) { return "so logged in", nil @@ -288,9 +289,9 @@ func TestEndpoint(t *testing.T) { func TestEnvVarConfigEndpoint(t *testing.T) { // ensure that env var overrides work for hostname endpointHit := "" - test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { + test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { endpointHit = endpoint - return &entities.RunResults{Message: []byte("good job")}, nil + return &cli.RunResult{Message: []byte("good job")}, nil } authToken = func() (string, error) { return "so logged in", nil @@ -322,9 +323,9 @@ func TestFileConfigEndpoint(t *testing.T) { fileEndpoint := "https://foo_file.capeprivacy.com" oldEndpoint := os.Getenv("CAPE_ENCLAVE_HOST") - test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { + test = func(testReq sdk.TestRequest, verifier sdk.Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { endpointHit = endpoint - return &entities.RunResults{Message: []byte("good job")}, nil + return &cli.RunResult{Message: []byte("good job")}, nil } authToken = func() (string, error) { return "so logged in", nil diff --git a/entities/entities.go b/entities/entities.go index 6a031ad4..274a0884 100644 --- a/entities/entities.go +++ b/entities/entities.go @@ -54,11 +54,6 @@ type AttestationWrapper struct { Message []byte `json:"message"` } -type RunResults struct { - Type string `json:"type"` - Message []byte `json:"message"` -} - type AuthenticationType string func (a AuthenticationType) Validate() error { diff --git a/protocol/protocol.go b/protocol/protocol.go index 5f7080c0..e1637b83 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -5,6 +5,8 @@ import ( "github.com/gorilla/websocket" + "github.com/capeprivacy/cli" + "github.com/capeprivacy/cli/entities" ) @@ -78,11 +80,11 @@ func (p Protocol) ReadDeploymentResults() (*entities.SetDeploymentIDRequest, err return readMsg[entities.SetDeploymentIDRequest](p.Websocket) } -func (p Protocol) ReadRunResults() (*entities.RunResults, error) { - return readMsg[entities.RunResults](p.Websocket) +func (p Protocol) ReadRunResults() (*cli.RunResult, error) { + return readMsg[cli.RunResult](p.Websocket) } -func (p Protocol) WriteRunResults(results entities.RunResults) error { +func (p Protocol) WriteRunResults(results cli.RunResult) error { return writeMsg(p.Websocket, results) } diff --git a/sdk/deploy.go b/sdk/deploy.go index 15e4758f..38a141df 100644 --- a/sdk/deploy.go +++ b/sdk/deploy.go @@ -9,6 +9,8 @@ import ( "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" + "github.com/capeprivacy/cli" + "github.com/capeprivacy/attest/attest" "github.com/capeprivacy/cli/crypto" "github.com/capeprivacy/cli/entities" @@ -19,7 +21,7 @@ import ( type protocol interface { WriteStart(request entities.StartRequest) error ReadAttestationDoc() ([]byte, error) - ReadRunResults() (*entities.RunResults, error) + ReadRunResults() (*cli.RunResult, error) WriteBinary([]byte) error WriteFunctionInfo(name string, public bool) error ReadDeploymentResults() (*entities.SetDeploymentIDRequest, error) diff --git a/sdk/run.go b/sdk/run.go index a12f9924..c4eb02f4 100644 --- a/sdk/run.go +++ b/sdk/run.go @@ -9,6 +9,8 @@ import ( "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "github.com/capeprivacy/cli" + "github.com/capeprivacy/attest/attest" "github.com/capeprivacy/cli/crypto" "github.com/capeprivacy/cli/entities" @@ -29,7 +31,7 @@ type RunRequest struct { } // Run loads the given function into a secure enclave and invokes it on the given data, then returns the result. -func Run(req RunRequest) ([]byte, error) { +func Run(req RunRequest) (*cli.RunResult, error) { conn, doc, err := connect(req.URL, req.FunctionID, req.FunctionAuth, req.FuncChecksum, req.KeyChecksum, req.PcrSlice, req.Insecure) if err != nil { return nil, err @@ -115,7 +117,7 @@ func connect(url string, functionID string, functionAuth entities.FunctionAuth, return conn, doc, nil } -func invoke(doc *attest.AttestationDoc, conn *websocket.Conn, data []byte) ([]byte, error) { +func invoke(doc *attest.AttestationDoc, conn *websocket.Conn, data []byte) (*cli.RunResult, error) { if doc == nil { log.Error("missing attestation document, you may need to run cape.Connect()") return nil, errors.New("missing attestation document") @@ -145,7 +147,7 @@ func invoke(doc *attest.AttestationDoc, conn *websocket.Conn, data []byte) ([]by } log.Debugf("< Received Function Results.") - return resData.Message, nil + return resData, nil } func writeData(conn *websocket.Conn, data []byte) error { diff --git a/sdk/test.go b/sdk/test.go index 3be4d460..09165e10 100644 --- a/sdk/test.go +++ b/sdk/test.go @@ -12,6 +12,8 @@ import ( "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" + "github.com/capeprivacy/cli" + "github.com/capeprivacy/attest/attest" "github.com/capeprivacy/cli/crypto" "github.com/capeprivacy/cli/entities" @@ -38,7 +40,7 @@ type ErrorMsg struct { // Test simulates the workflow of Deploy and Run, without storing the function. // It loads the given function into an enclave, runs it on the given data, and returns the result. // Use Test to verify that your function will work before storing it via Deploy. -func Test(testReq TestRequest, verifier Verifier, endpoint string, pcrSlice []string) (*entities.RunResults, error) { +func Test(testReq TestRequest, verifier Verifier, endpoint string, pcrSlice []string) (*cli.RunResult, error) { conn, err := doDial(endpoint, testReq.Insecure, "cape.runtime", testReq.AuthToken) if err != nil { return nil, err diff --git a/sdk/test_test.go b/sdk/test_test.go index 31335e92..12b90c2f 100644 --- a/sdk/test_test.go +++ b/sdk/test_test.go @@ -8,6 +8,8 @@ import ( "github.com/gorilla/websocket" + "github.com/capeprivacy/cli" + "github.com/capeprivacy/attest/attest" "github.com/capeprivacy/cli/entities" "github.com/capeprivacy/cli/mocks" @@ -16,7 +18,7 @@ import ( type testProtocol struct { start func(req entities.StartRequest) error attest func() ([]byte, error) - results func() (*entities.RunResults, error) + results func() (*cli.RunResult, error) binary func(b []byte) error } @@ -32,7 +34,7 @@ func (t testProtocol) WriteStart(request entities.StartRequest) error { return t.start(request) } func (t testProtocol) ReadAttestationDoc() ([]byte, error) { return t.attest() } -func (t testProtocol) ReadRunResults() (*entities.RunResults, error) { +func (t testProtocol) ReadRunResults() (*cli.RunResult, error) { return t.results() } func (t testProtocol) WriteBinary(bytes []byte) error { return t.binary(bytes) } @@ -57,8 +59,8 @@ func TestCapeTest(t *testing.T) { return testProtocol{ start: func(req entities.StartRequest) error { return nil }, attest: func() ([]byte, error) { return []byte{}, nil }, - results: func() (*entities.RunResults, error) { - return &entities.RunResults{Message: []byte("good job")}, nil + results: func() (*cli.RunResult, error) { + return &cli.RunResult{Message: []byte("good job")}, nil }, binary: func(b []byte) error { return nil }, }