diff --git a/cli/cli.go b/cli/cli.go index 8da0130..12892b2 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -13,6 +13,7 @@ import ( "fmt" "os" "reflect" + "strings" "github.com/notaryproject/notation-plugin-framework-go/internal/slices" "github.com/notaryproject/notation-plugin-framework-go/log" @@ -26,17 +27,21 @@ type CLI struct { } // New creates a new CLI using given plugin -func New(executableName string, pl plugin.Plugin) *CLI { +func New(executableName string, pl plugin.Plugin) (*CLI, error) { return NewWithLogger(executableName, pl, &discardLogger{}) } // NewWithLogger creates a new CLI using given plugin and logger -func NewWithLogger(executableName string, pl plugin.Plugin, l log.Logger) *CLI { +func NewWithLogger(executableName string, pl plugin.Plugin, l log.Logger) (*CLI, error) { + if strings.HasPrefix(executableName, plugin.BinaryPrefix) { + return nil, fmt.Errorf("executable name must start with prefix: %s", plugin.BinaryPrefix) + } + return &CLI{ name: executableName, pl: pl, logger: l, - } + }, nil } // Execute is main controller that reads/validates commands, parses input, executes relevant plugin functions diff --git a/cli/cli_test.go b/cli/cli_test.go index 3892db0..409111a 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -36,13 +36,13 @@ func TestMarshalResponse(t *testing.T) { func TestMarshalResponseError(t *testing.T) { _, err := cli.marshalResponse(nil, fmt.Errorf("expected error thrown")) - assertErr(t, err, plugin.CodeGeneric) + assertErr(t, err, plugin.ErrorCodeGeneric) _, err = cli.marshalResponse(nil, plugin.NewValidationError("expected validation error thrown")) - assertErr(t, err, plugin.CodeValidation) + assertErr(t, err, plugin.ErrorCodeValidation) _, err = cli.marshalResponse(make(chan int), nil) - assertErr(t, err, plugin.CodeGeneric) + assertErr(t, err, plugin.ErrorCodeGeneric) } func TestUnmarshalRequest(t *testing.T) { @@ -129,7 +129,7 @@ func setupReader(content string) func() { } } -func assertErr(t *testing.T, err error, code plugin.Code) { +func assertErr(t *testing.T, err error, code plugin.ErrorCode) { if plgErr, ok := err.(*plugin.Error); ok { if reflect.DeepEqual(code, plgErr.ErrCode) { return diff --git a/example/main.go b/example/main.go index 76df3cb..ed2c90f 100644 --- a/example/main.go +++ b/example/main.go @@ -13,10 +13,15 @@ func main() { // Initialize plugin plugin, err := NewExamplePlugin() if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "Failed to initialize plugin") + _, _ = fmt.Fprintf(os.Stderr, "failed to initialize plugin: %v", err) os.Exit(2) } // Create executable - cli.New("example", plugin).Execute(ctx, os.Args) + pluginCli, err := cli.New("notation-example", plugin) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "failed to create executable: %v", err) + os.Exit(3) + } + pluginCli.Execute(ctx, os.Args) } diff --git a/plugin/errors.go b/plugin/errors.go index 01459cf..0048ab7 100644 --- a/plugin/errors.go +++ b/plugin/errors.go @@ -5,14 +5,15 @@ import ( "fmt" ) -type Code string +type ErrorCode string const ( - CodeValidation Code = "VALIDATION_ERROR" - CodeUnsupportedContractVersion Code = "UNSUPPORTED_CONTRACT_VERSION" - CodeAccessDenied Code = "ACCESS_DENIED" - CodeThrottled Code = "THROTTLED" - CodeGeneric Code = "ERROR" + ErrorCodeValidation ErrorCode = "VALIDATION_ERROR" + ErrorCodeUnsupportedContractVersion ErrorCode = "UNSUPPORTED_CONTRACT_VERSION" + ErrorCodeAccessDenied ErrorCode = "ACCESS_DENIED" + ErrorCodeTimeout ErrorCode = "TIMEOUT" + ErrorCodeThrottled ErrorCode = "THROTTLED" + ErrorCodeGeneric ErrorCode = "ERROR" ) const ( @@ -23,39 +24,40 @@ const ( // Error is used when the signature associated is no longer // valid. type Error struct { - ErrCode Code `json:"errorCode"` - Msg string `json:"errorMessage"` + ErrCode ErrorCode `json:"errorCode"` + Message string `json:"errorMessage,omitempty"` + Metadata map[string]string `json:"errorMetadata,omitempty"` } -func NewError(code Code, msg string) *Error { +func NewError(code ErrorCode, msg string) *Error { return &Error{ ErrCode: code, - Msg: msg, + Message: msg, } } func NewGenericError(msg string) *Error { - return NewError(CodeGeneric, msg) + return NewError(ErrorCodeGeneric, msg) } func NewGenericErrorf(format string, msg string) *Error { - return NewError(CodeGeneric, fmt.Sprintf(format, msg)) + return NewError(ErrorCodeGeneric, fmt.Sprintf(format, msg)) } func NewUnsupportedError(msg string) *Error { - return NewError(CodeValidation, msg+" is not supported") + return NewError(ErrorCodeValidation, msg+" is not supported") } func NewValidationError(msg string) *Error { - return NewError(CodeValidation, msg) + return NewError(ErrorCodeValidation, msg) } func NewValidationErrorf(format string, msg string) *Error { - return NewError(CodeValidation, fmt.Sprintf(format, msg)) + return NewError(ErrorCodeValidation, fmt.Sprintf(format, msg)) } func NewUnsupportedContractVersionError(version string) *Error { - return NewError(CodeUnsupportedContractVersion, fmt.Sprintf("%q is not a supported notary plugin contract version", version)) + return NewError(ErrorCodeUnsupportedContractVersion, fmt.Sprintf("%q is not a supported notary plugin contract version", version)) } func NewJSONParsingError(msg string) *Error { diff --git a/plugin/proto.go b/plugin/proto.go index 2c046af..13065a9 100644 --- a/plugin/proto.go +++ b/plugin/proto.go @@ -2,6 +2,12 @@ // and notation external plugin. package plugin +// BinaryPrefix is the prefix required on all plugin binary names. +const BinaryPrefix = "notation-" + +// ContractVersion is the . version of the plugin contract. +const ContractVersion = "1.0" + // Capability is a feature available in the plugin contract. type Capability string @@ -26,6 +32,11 @@ const ( // Command is a CLI command available in the plugin contract. type Command string +// Request defines a plugin request, which is always associated to a command. +type Request interface { + Command() Command +} + const ( // CommandGetMetadata is the name of the plugin command // which must be supported by every plugin and returns the