diff --git a/plugin/manager.go b/plugin/manager.go index 65aea0bd..f43907cb 100644 --- a/plugin/manager.go +++ b/plugin/manager.go @@ -25,6 +25,7 @@ import ( "github.com/notaryproject/notation-go/dir" "github.com/notaryproject/notation-go/internal/file" "github.com/notaryproject/notation-go/internal/semver" + "github.com/notaryproject/notation-go/log" "github.com/notaryproject/notation-go/plugin/proto" ) @@ -114,35 +115,35 @@ type CLIInstallOptions struct { // plugin is malfunctioning, it will be overwritten. func (m *CLIManager) Install(ctx context.Context, installOpts CLIInstallOptions) (*proto.GetMetadataResponse, *proto.GetMetadataResponse, error) { // initialization + logger := log.GetLogger(ctx) overwrite := installOpts.Overwrite if installOpts.PluginPath == "" { return nil, nil, errors.New("plugin source path cannot be empty") } + logger.Debugf("Installing plugin from plugin path %s", installOpts.PluginPath) var installFromNonDir bool - pluginExecutableFile, pluginName, err := parsePluginFromDir(installOpts.PluginPath) + pluginExecutableFile, pluginName, err := parsePluginFromDir(ctx, installOpts.PluginPath) if err != nil { if !errors.Is(err, file.ErrNotDirectory) { - return nil, nil, fmt.Errorf("failed to read plugin from directory %s: %w", installOpts.PluginPath, err) + return nil, nil, fmt.Errorf("failed to read plugin from input directory: %w", err) } // input is not a dir, check if it's a single plugin executable file installFromNonDir = true pluginExecutableFile = installOpts.PluginPath - pluginName, err = ParsePluginName(filepath.Base(pluginExecutableFile)) + pluginExecutableFileName := filepath.Base(pluginExecutableFile) + pluginName, err = parsePluginName(pluginExecutableFileName) if err != nil { - return nil, nil, fmt.Errorf("failed to read plugin name from file path %s: %w", pluginExecutableFile, err) + return nil, nil, fmt.Errorf("failed to read plugin name from input file %s: %w", pluginExecutableFileName, err) } isExec, err := isExecutableFile(pluginExecutableFile) if err != nil { - return nil, nil, fmt.Errorf("failed to check if file %s is executable: %w", pluginExecutableFile, err) + return nil, nil, fmt.Errorf("failed to check if input file %s is executable: %w", pluginExecutableFileName, err) } if !isExec { - return nil, nil, fmt.Errorf("file %s is not executable", pluginExecutableFile) + return nil, nil, fmt.Errorf("input file %s is not executable", pluginExecutableFileName) } } // validate and get new plugin metadata - if err := validatePluginFileExtensionAgainstOS(filepath.Base(pluginExecutableFile), pluginName); err != nil { - return nil, nil, err - } newPlugin, err := NewCLIPlugin(ctx, pluginName, pluginExecutableFile) if err != nil { return nil, nil, fmt.Errorf("failed to create new CLI plugin: %w", err) @@ -215,12 +216,13 @@ func (m *CLIManager) Uninstall(ctx context.Context, name string) error { } // parsePluginFromDir checks if a dir is a valid plugin dir which contains -// one and only one plugin executable file. The dir may contain extra lib files -// and LICENSE files. Sub-directories are ignored. +// one and only one plugin executable file candidate. +// The dir may contain extra lib files and LICENSE files. +// Sub-directories are ignored. // // On success, the plugin executable file path, plugin name and // nil error are returned. -func parsePluginFromDir(path string) (string, string, error) { +func parsePluginFromDir(ctx context.Context, path string) (string, string, error) { // sanity check fi, err := os.Stat(path) if err != nil { @@ -229,9 +231,11 @@ func parsePluginFromDir(path string) (string, string, error) { if !fi.Mode().IsDir() { return "", "", file.ErrNotDirectory } + logger := log.GetLogger(ctx) // walk the path - var pluginExecutableFile, pluginName string + var pluginExecutableFile, pluginName, candidatePluginName string var foundPluginExecutableFile bool + var filesWithValidNameFormat []string if err := filepath.WalkDir(path, func(p string, d fs.DirEntry, err error) error { if err != nil { return err @@ -246,11 +250,12 @@ func parsePluginFromDir(path string) (string, string, error) { } // only take regular files if info.Mode().IsRegular() { - if pluginName, err = ParsePluginName(d.Name()); err != nil { + if candidatePluginName, err = parsePluginName(d.Name()); err != nil { // file name does not follow the notation-{plugin-name} format, // continue return nil } + filesWithValidNameFormat = append(filesWithValidNameFormat, p) isExec, err := isExecutableFile(p) if err != nil { return err @@ -263,12 +268,23 @@ func parsePluginFromDir(path string) (string, string, error) { } foundPluginExecutableFile = true pluginExecutableFile = p + pluginName = candidatePluginName } return nil }); err != nil { return "", "", err } if !foundPluginExecutableFile { + // if no executable file was found, but there's one and only one + // potential candidate, try install the candidate + if len(filesWithValidNameFormat) == 1 { + candidate := filesWithValidNameFormat[0] + if err := setExecutable(candidate); err != nil { + return "", "", fmt.Errorf("no plugin executable file was found: %w", err) + } + logger.Warnf("Found candidate plugin executable file %q without executable permission. Setting user executable bit and trying to install.", filepath.Base(candidate)) + return candidate, candidatePluginName, nil + } return "", "", errors.New("no plugin executable file was found") } return pluginExecutableFile, pluginName, nil diff --git a/plugin/manager_test.go b/plugin/manager_test.go index 8aa92f88..f0937cd6 100644 --- a/plugin/manager_test.go +++ b/plugin/manager_test.go @@ -80,6 +80,11 @@ var validMetadataBar = proto.GetMetadataResponse{ SupportedContractVersions: []string{"1.0"}, Capabilities: []proto.Capability{proto.CapabilitySignatureGenerator}, } +var validMetadataBarExample = proto.GetMetadataResponse{ + Name: "bar.example.plugin", Description: "friendly", Version: "1.0.0", URL: "example.com", + SupportedContractVersions: []string{"1.0"}, Capabilities: []proto.Capability{proto.CapabilitySignatureGenerator}, +} + var invalidMetadataName = proto.GetMetadataResponse{ Name: "foobar", Description: "friendly", Version: "1", URL: "example.com", SupportedContractVersions: []string{"1.0"}, Capabilities: []proto.Capability{proto.CapabilitySignatureGenerator}, @@ -214,6 +219,36 @@ func TestManager_Install(t *testing.T) { } }) + t.Run("success install with file extension", func(t *testing.T) { + newPluginFilePath := "testdata/bar/notation-bar.example.plugin" + newPluginDir := filepath.Dir(newPluginFilePath) + if err := os.MkdirAll(newPluginDir, 0777); err != nil { + t.Fatalf("failed to create %s: %v", newPluginDir, err) + } + defer os.RemoveAll(newPluginDir) + if err := createFileAndChmod(newPluginFilePath, 0700); err != nil { + t.Fatal(err) + } + executor = testInstallCommander{ + newPluginFilePath: newPluginFilePath, + newPluginStdout: metadataJSON(validMetadataBarExample), + } + defer mgr.Uninstall(context.Background(), "bar.example.plugin") + installOpts := CLIInstallOptions{ + PluginPath: newPluginFilePath, + } + existingPluginMetadata, newPluginMetadata, err := mgr.Install(context.Background(), installOpts) + if err != nil { + t.Fatalf("expecting error to be nil, but got %v", err) + } + if existingPluginMetadata != nil { + t.Fatalf("expecting existingPluginMetadata to be nil, but got %v", existingPluginMetadata) + } + if newPluginMetadata.Version != validMetadataBar.Version { + t.Fatalf("new plugin version mismatch, new plugin version: %s, but got: %s", validMetadataBar.Version, newPluginMetadata.Version) + } + }) + t.Run("fail to install due to equal version", func(t *testing.T) { executor = testInstallCommander{ existedPluginFilePath: existedPluginFilePath, @@ -265,21 +300,21 @@ func TestManager_Install(t *testing.T) { installOpts := CLIInstallOptions{ PluginPath: newPluginFilePath, } - expectedErrorMsg := "failed to read plugin name from file path testdata/bar/bar: invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got bar" + expectedErrorMsg := "failed to read plugin name from input file bar: invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got bar" _, _, err := mgr.Install(context.Background(), installOpts) if err == nil || err.Error() != expectedErrorMsg { t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) } }) - t.Run("fail to install due to wrong plugin file permission", func(t *testing.T) { - newPluginFilePath := "testdata/bar/notation-bar" + t.Run("fail to install due to plugin executable file name missing plugin name", func(t *testing.T) { + newPluginFilePath := "testdata/bar/notation-" newPluginDir := filepath.Dir(newPluginFilePath) if err := os.MkdirAll(newPluginDir, 0777); err != nil { t.Fatalf("failed to create %s: %v", newPluginDir, err) } defer os.RemoveAll(newPluginDir) - if err := createFileAndChmod(newPluginFilePath, 0600); err != nil { + if err := createFileAndChmod(newPluginFilePath, 0700); err != nil { t.Fatal(err) } executor = testInstallCommander{ @@ -289,21 +324,21 @@ func TestManager_Install(t *testing.T) { installOpts := CLIInstallOptions{ PluginPath: newPluginFilePath, } - expectedErrorMsg := "file testdata/bar/notation-bar is not executable" + expectedErrorMsg := "failed to read plugin name from input file notation-: invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got notation-" _, _, err := mgr.Install(context.Background(), installOpts) if err == nil || err.Error() != expectedErrorMsg { t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) } }) - t.Run("fail to install due to invalid new plugin file extension", func(t *testing.T) { - newPluginFilePath := "testdata/bar/notation-bar.exe" + t.Run("fail to install due to wrong plugin file permission", func(t *testing.T) { + newPluginFilePath := "testdata/bar/notation-bar" newPluginDir := filepath.Dir(newPluginFilePath) if err := os.MkdirAll(newPluginDir, 0777); err != nil { t.Fatalf("failed to create %s: %v", newPluginDir, err) } defer os.RemoveAll(newPluginDir) - if err := createFileAndChmod(newPluginFilePath, 0700); err != nil { + if err := createFileAndChmod(newPluginFilePath, 0600); err != nil { t.Fatal(err) } executor = testInstallCommander{ @@ -313,7 +348,7 @@ func TestManager_Install(t *testing.T) { installOpts := CLIInstallOptions{ PluginPath: newPluginFilePath, } - expectedErrorMsg := "invalid plugin file extension. Expecting file notation-bar, but got notation-bar.exe" + expectedErrorMsg := "input file notation-bar is not executable" _, _, err := mgr.Install(context.Background(), installOpts) if err == nil || err.Error() != expectedErrorMsg { t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) @@ -329,7 +364,7 @@ func TestManager_Install(t *testing.T) { installOpts := CLIInstallOptions{ PluginPath: newPluginFilePath, } - expectedErrorMsg := "failed to read plugin from directory testdata/bar/notation-bar: stat testdata/bar/notation-bar: no such file or directory" + expectedErrorMsg := "failed to read plugin from input directory: stat testdata/bar/notation-bar: no such file or directory" _, _, err := mgr.Install(context.Background(), installOpts) if err == nil || err.Error() != expectedErrorMsg { t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) @@ -397,7 +432,7 @@ func TestManager_Install(t *testing.T) { t.Run("success to install from plugin dir", func(t *testing.T) { existedPluginFilePath := "testdata/plugins/foo/notation-foo" newPluginFilePath := "testdata/foo/notation-foo" - newPluginLibPath := "testdata/foo/libfoo" + newPluginLibPath := "testdata/foo/notation-libfoo" newPluginDir := filepath.Dir(newPluginFilePath) if err := os.MkdirAll(newPluginDir, 0777); err != nil { t.Fatalf("failed to create %s: %v", newPluginDir, err) @@ -430,15 +465,55 @@ func TestManager_Install(t *testing.T) { } }) - t.Run("fail to install from plugin dir due to no plugin executable file", func(t *testing.T) { + t.Run("success to install from plugin dir with no executable file and one valid candidate file", func(t *testing.T) { existedPluginFilePath := "testdata/plugins/foo/notation-foo" - newPluginFilePath := "testdata/foo/foo" + newPluginFilePath := "testdata/foo/notation-foo" + newPluginLibPath := "testdata/foo/libfoo" newPluginDir := filepath.Dir(newPluginFilePath) if err := os.MkdirAll(newPluginDir, 0777); err != nil { t.Fatalf("failed to create %s: %v", newPluginDir, err) } defer os.RemoveAll(newPluginDir) - if err := createFileAndChmod(newPluginFilePath, 0700); err != nil { + if err := createFileAndChmod(newPluginFilePath, 0600); err != nil { + t.Fatal(err) + } + if err := createFileAndChmod(newPluginLibPath, 0600); err != nil { + t.Fatal(err) + } + executor = testInstallCommander{ + existedPluginFilePath: existedPluginFilePath, + newPluginFilePath: newPluginFilePath, + existedPluginStdout: metadataJSON(validMetadata), + newPluginStdout: metadataJSON(validMetadataHigherVersion), + } + installOpts := CLIInstallOptions{ + PluginPath: newPluginDir, + } + existingPluginMetadata, newPluginMetadata, err := mgr.Install(context.Background(), installOpts) + if err != nil { + t.Fatalf("expecting nil error, but got %v", err) + } + if existingPluginMetadata.Version != "1.0.0" { + t.Fatalf("expecting existing plugin metadata to be 1.0.0, but got %s", existingPluginMetadata.Version) + } + if newPluginMetadata.Version != "1.1.0" { + t.Fatalf("expecting new plugin metadata to be 1.1.0, but got %s", newPluginMetadata.Version) + } + }) + + t.Run("fail to install from plugin dir due to more than one candidate plugin executable files", func(t *testing.T) { + existedPluginFilePath := "testdata/plugins/foo/notation-foo" + newPluginFilePath := "testdata/foo/notation-foo1" + newPluginFilePath2 := "testdata/foo/notation-foo2" + newPluginDir := filepath.Dir(newPluginFilePath) + if err := os.MkdirAll(newPluginDir, 0777); err != nil { + t.Fatalf("failed to create %s: %v", newPluginDir, err) + } + defer os.RemoveAll(newPluginDir) + if err := createFileAndChmod(newPluginFilePath, 0600); err != nil { + t.Fatal(err) + } + if err := createFileAndChmod(newPluginFilePath2, 0600); err != nil { t.Fatal(err) } executor = testInstallCommander{ @@ -450,7 +525,7 @@ func TestManager_Install(t *testing.T) { installOpts := CLIInstallOptions{ PluginPath: newPluginDir, } - expectedErrorMsg := "failed to read plugin from directory testdata/foo: no plugin executable file was found" + expectedErrorMsg := "failed to read plugin from input directory: no plugin executable file was found" _, _, err := mgr.Install(context.Background(), installOpts) if err == nil || err.Error() != expectedErrorMsg { t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) @@ -481,7 +556,7 @@ func TestManager_Install(t *testing.T) { installOpts := CLIInstallOptions{ PluginPath: newPluginDir, } - expectedErrorMsg := "failed to read plugin from directory testdata/foo: found more than one plugin executable files" + expectedErrorMsg := "failed to read plugin from input directory: found more than one plugin executable files" _, _, err := mgr.Install(context.Background(), installOpts) if err == nil || err.Error() != expectedErrorMsg { t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) @@ -517,6 +592,76 @@ func TestManager_Uninstall(t *testing.T) { } } +func TestParsePluginName(t *testing.T) { + if runtime.GOOS == "windows" { + pluginName, err := parsePluginName("notation-my-plugin.exe") + if err != nil { + t.Fatalf("expected nil err, but got %v", err) + } + if pluginName != "my-plugin" { + t.Fatalf("expected plugin name my-plugin, but got %s", pluginName) + } + + expectedErrorMsg := "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}.exe, but got notation-com.plugin" + _, err = parsePluginName("notation-com.plugin") + if err == nil || err.Error() != expectedErrorMsg { + t.Fatalf("expected %s, but got %v", expectedErrorMsg, err) + } + + expectedErrorMsg = "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}.exe, but got my-plugin.exe" + _, err = parsePluginName("my-plugin.exe") + if err == nil || err.Error() != expectedErrorMsg { + t.Fatalf("expected %s, but got %v", expectedErrorMsg, err) + } + + expectedErrorMsg = "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}.exe, but got notation-.exe" + _, err = parsePluginName("notation-.exe") + if err == nil || err.Error() != expectedErrorMsg { + t.Fatalf("expected %s, but got %v", expectedErrorMsg, err) + } + + expectedErrorMsg = "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}.exe, but got my-plugin" + _, err = parsePluginName("my-plugin") + if err == nil || err.Error() != expectedErrorMsg { + t.Fatalf("expected %s, but got %v", expectedErrorMsg, err) + } + } else { + pluginName, err := parsePluginName("notation-my-plugin") + if err != nil { + t.Fatalf("expected nil err, but got %v", err) + } + if pluginName != "my-plugin" { + t.Fatalf("expected plugin name my-plugin, but got %s", pluginName) + } + + pluginName, err = parsePluginName("notation-com.example.plugin") + if err != nil { + t.Fatalf("expected nil err, but got %v", err) + } + if pluginName != "com.example.plugin" { + t.Fatalf("expected plugin name com.example.plugin, but got %s", pluginName) + } + + expectedErrorMsg := "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got myPlugin" + _, err = parsePluginName("myPlugin") + if err == nil || err.Error() != expectedErrorMsg { + t.Fatalf("expected %s, but got %v", expectedErrorMsg, err) + } + + expectedErrorMsg = "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got my-plugin" + _, err = parsePluginName("my-plugin") + if err == nil || err.Error() != expectedErrorMsg { + t.Fatalf("expected %s, but got %v", expectedErrorMsg, err) + } + + expectedErrorMsg = "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got notation-" + _, err = parsePluginName("notation-") + if err == nil || err.Error() != expectedErrorMsg { + t.Fatalf("expected %s, but got %v", expectedErrorMsg, err) + } + } +} + func metadataJSON(m proto.GetMetadataResponse) []byte { d, err := json.Marshal(m) if err != nil { diff --git a/plugin/manager_unix.go b/plugin/manager_unix.go index 6d173383..579926d3 100644 --- a/plugin/manager_unix.go +++ b/plugin/manager_unix.go @@ -17,7 +17,9 @@ package plugin import ( + "fmt" "os" + "strings" "github.com/notaryproject/notation-go/plugin/proto" ) @@ -38,3 +40,22 @@ func isExecutableFile(filePath string) (bool, error) { } return mode.Perm()&0100 != 0, nil } + +// parsePluginName checks if fileName is a valid plugin file name +// and gets plugin name from it based on spec: https://github.com/notaryproject/specifications/blob/main/specs/plugin-extensibility.md#installation +func parsePluginName(fileName string) (string, error) { + pluginName, found := strings.CutPrefix(fileName, proto.Prefix) + if !found || pluginName == "" { + return "", fmt.Errorf("invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got %s", fileName) + } + return pluginName, nil +} + +// setExecutable sets file to be user executable +func setExecutable(filePath string) error { + fileInfo, err := os.Stat(filePath) + if err != nil { + return err + } + return os.Chmod(filePath, fileInfo.Mode()|os.FileMode(0100)) +} diff --git a/plugin/manager_windows.go b/plugin/manager_windows.go index c8504a0e..26d3e11f 100644 --- a/plugin/manager_windows.go +++ b/plugin/manager_windows.go @@ -14,10 +14,12 @@ package plugin import ( + "fmt" "os" "path/filepath" "strings" + "github.com/notaryproject/notation-go/internal/file" "github.com/notaryproject/notation-go/plugin/proto" ) @@ -36,3 +38,23 @@ func isExecutableFile(filePath string) (bool, error) { } return strings.EqualFold(filepath.Ext(filepath.Base(filePath)), ".exe"), nil } + +// parsePluginName checks if fileName is a valid plugin file name +// and gets plugin name from it based on spec: https://github.com/notaryproject/specifications/blob/main/specs/plugin-extensibility.md#installation +func parsePluginName(fileName string) (string, error) { + if !strings.EqualFold(filepath.Ext(fileName), ".exe") { + return "", fmt.Errorf("invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}.exe, but got %s", fileName) + } + fname := file.TrimFileExtension(fileName) + pluginName, found := strings.CutPrefix(fname, proto.Prefix) + if !found || pluginName == "" { + return "", fmt.Errorf("invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}.exe, but got %s", fileName) + } + return pluginName, nil +} + +// setExecutable returns error on Windows. User needs to install the correct +// plugin file. +func setExecutable(filePath string) error { + return fmt.Errorf(`plugin executable file must have file extension ".exe", but got %q`, filepath.Base(filePath)) +} diff --git a/plugin/plugin.go b/plugin/plugin.go index 0afb9ea6..c9a77a7b 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -25,9 +25,7 @@ import ( "os" "os/exec" "path/filepath" - "strings" - "github.com/notaryproject/notation-go/internal/file" "github.com/notaryproject/notation-go/internal/slices" "github.com/notaryproject/notation-go/log" "github.com/notaryproject/notation-go/plugin/proto" @@ -225,17 +223,6 @@ func (c execCommander) Output(ctx context.Context, name string, command proto.Co return stdout.Bytes(), nil, nil } -// ParsePluginName checks if fileName is a valid plugin file name -// and gets plugin name from it based on spec: https://github.com/notaryproject/specifications/blob/main/specs/plugin-extensibility.md#installation -func ParsePluginName(fileName string) (string, error) { - fname := file.TrimFileExtension(fileName) - pluginName, found := strings.CutPrefix(fname, proto.Prefix) - if !found { - return "", fmt.Errorf("invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got %s", fname) - } - return pluginName, nil -} - // validate checks if the metadata is correctly populated. func validate(metadata *proto.GetMetadataResponse) error { if metadata.Name == "" { @@ -264,16 +251,3 @@ func validate(metadata *proto.GetMetadataResponse) error { } return nil } - -// validatePluginFileExtensionAgainstOS validates if plugin executable file -// extension aligns with the runtime OS. -// -// On windows, `.exe` extension is required. -// On other OS, MUST not have the `.exe` extension. -func validatePluginFileExtensionAgainstOS(fileName, pluginName string) error { - expectedPluginFile := binName(pluginName) - if filepath.Ext(fileName) != filepath.Ext(expectedPluginFile) { - return fmt.Errorf("invalid plugin file extension. Expecting file %s, but got %s", expectedPluginFile, fileName) - } - return nil -} diff --git a/plugin/plugin_test.go b/plugin/plugin_test.go index bf592663..45a43fc4 100644 --- a/plugin/plugin_test.go +++ b/plugin/plugin_test.go @@ -20,7 +20,6 @@ import ( "fmt" "os" "reflect" - "runtime" "strconv" "strings" "testing" @@ -271,59 +270,3 @@ func TestNewCLIPlugin_ValidError(t *testing.T) { } }) } - -func TestExtractPluginNameFromExecutableFileName(t *testing.T) { - pluginName, err := ParsePluginName("notation-my-plugin") - if err != nil { - t.Fatalf("expected nil err, got %v", err) - } - if pluginName != "my-plugin" { - t.Fatalf("expected plugin name my-plugin, but got %s", pluginName) - } - - pluginName, err = ParsePluginName("notation-my-plugin.exe") - if err != nil { - t.Fatalf("expected nil err, got %v", err) - } - if pluginName != "my-plugin" { - t.Fatalf("expected plugin name my-plugin, but got %s", pluginName) - } - - _, err = ParsePluginName("myPlugin") - expectedErrorMsg := "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got myPlugin" - if err == nil || err.Error() != expectedErrorMsg { - t.Fatalf("expected %s, got %v", expectedErrorMsg, err) - } - - _, err = ParsePluginName("my-plugin") - expectedErrorMsg = "invalid plugin executable file name. Plugin file name requires format notation-{plugin-name}, but got my-plugin" - if err == nil || err.Error() != expectedErrorMsg { - t.Fatalf("expected %s, got %v", expectedErrorMsg, err) - } -} - -func TestValidatePluginFileExtensionAgainstOS(t *testing.T) { - if runtime.GOOS == "windows" { - err := validatePluginFileExtensionAgainstOS("notation-foo.exe", "foo") - if err != nil { - t.Fatalf("expecting nil error, but got %s", err) - } - - err = validatePluginFileExtensionAgainstOS("notation-foo", "foo") - expectedErrorMsg := "invalid plugin file extension. Expecting file notation-foo.exe, but got notation-foo" - if err == nil || err.Error() != expectedErrorMsg { - t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) - } - return - } - err := validatePluginFileExtensionAgainstOS("notation-foo", "foo") - if err != nil { - t.Fatalf("expecting nil error, but got %s", err) - } - - err = validatePluginFileExtensionAgainstOS("notation-foo.exe", "foo") - expectedErrorMsg := "invalid plugin file extension. Expecting file notation-foo, but got notation-foo.exe" - if err == nil || err.Error() != expectedErrorMsg { - t.Fatalf("expecting error %s, but got %v", expectedErrorMsg, err) - } -} diff --git a/plugin/testdata/plugins/foo/libfoo b/plugin/testdata/plugins/foo/libfoo deleted file mode 100644 index e69de29b..00000000