summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--model/manifest.go49
-rw-r--r--model/manifest_test.go167
-rw-r--r--plugin/supervisor.go6
3 files changed, 218 insertions, 4 deletions
diff --git a/model/manifest.go b/model/manifest.go
index d494df466..6a3b0bfad 100644
--- a/model/manifest.go
+++ b/model/manifest.go
@@ -120,13 +120,30 @@ type Manifest struct {
}
type ManifestBackend struct {
- // The path to your executable binary. This should be relative to the root of your bundle and the
- // location of the manifest file.
+ // Executables are the paths to your executable binaries, specifying multiple entry points
+ // for different platforms when bundled together in a single plugin.
+ Executables *ManifestExecutables `json:"executables,omitempty" yaml:"executables,omitempty"`
+
+ // Executable is the path to your executable binary. This should be relative to the root
+ // of your bundle and the location of the manifest file.
//
// On Windows, this file must have a ".exe" extension.
+ //
+ // If your plugin is compiled for multiple platforms, consider bundling them together
+ // and using the Executables field instead.
Executable string `json:"executable" yaml:"executable"`
}
+type ManifestExecutables struct {
+ // LinuxAmd64 is the path to your executable binary for the corresponding platform
+ LinuxAmd64 string `json:"linux-amd64,omitempty" yaml:"linux-amd64,omitempty"`
+ // DarwinAmd64 is the path to your executable binary for the corresponding platform
+ DarwinAmd64 string `json:"darwin-amd64,omitempty" yaml:"darwin-amd64,omitempty"`
+ // WindowsAmd64 is the path to your executable binary for the corresponding platform
+ // This file must have a ".exe" extension
+ WindowsAmd64 string `json:"windows-amd64,omitempty" yaml:"windows-amd64,omitempty"`
+}
+
type ManifestWebapp struct {
// The path to your webapp bundle. This should be relative to the root of your bundle and the
// location of the manifest file.
@@ -173,6 +190,34 @@ func (m *Manifest) ClientManifest() *Manifest {
return cm
}
+// GetExecutableForRuntime returns the path to the executable for the given runtime architecture.
+//
+// If the manifest defines multiple executables, but none match, or if only a single executable
+// is defined, the Executable field will be returned. This method does not guarantee that the
+// resulting binary can actually execute on the given platform.
+func (m *Manifest) GetExecutableForRuntime(goOs, goArch string) string {
+ if m.Backend == nil {
+ return ""
+ }
+
+ var executable string
+ if m.Backend.Executables != nil {
+ if goOs == "linux" && goArch == "amd64" {
+ executable = m.Backend.Executables.LinuxAmd64
+ } else if goOs == "darwin" && goArch == "amd64" {
+ executable = m.Backend.Executables.DarwinAmd64
+ } else if goOs == "windows" && goArch == "amd64" {
+ executable = m.Backend.Executables.WindowsAmd64
+ }
+ }
+
+ if executable == "" {
+ executable = m.Backend.Executable
+ }
+
+ return executable
+}
+
// FindManifest will find and parse the manifest in a given directory.
//
// In all cases other than a does-not-exist error, path is set to the path of the manifest file that was
diff --git a/model/manifest_test.go b/model/manifest_test.go
index 0c55b5b66..e264a73c8 100644
--- a/model/manifest_test.go
+++ b/model/manifest_test.go
@@ -66,6 +66,11 @@ func TestManifestUnmarshal(t *testing.T) {
Id: "theid",
Backend: &ManifestBackend{
Executable: "theexecutable",
+ Executables: &ManifestExecutables{
+ LinuxAmd64: "theexecutable-linux-amd64",
+ DarwinAmd64: "theexecutable-darwin-amd64",
+ WindowsAmd64: "theexecutable-windows-amd64",
+ },
},
Webapp: &ManifestWebapp{
BundlePath: "thebundlepath",
@@ -98,6 +103,10 @@ func TestManifestUnmarshal(t *testing.T) {
id: theid
backend:
executable: theexecutable
+ executables:
+ linux-amd64: theexecutable-linux-amd64
+ darwin-amd64: theexecutable-darwin-amd64
+ windows-amd64: theexecutable-windows-amd64
webapp:
bundle_path: thebundlepath
settings_schema:
@@ -121,7 +130,12 @@ settings_schema:
require.NoError(t, json.Unmarshal([]byte(`{
"id": "theid",
"backend": {
- "executable": "theexecutable"
+ "executable": "theexecutable",
+ "executables": {
+ "linux-amd64": "theexecutable-linux-amd64",
+ "darwin-amd64": "theexecutable-darwin-amd64",
+ "windows-amd64": "theexecutable-windows-amd64"
+ }
},
"webapp": {
"bundle_path": "thebundlepath"
@@ -283,3 +297,154 @@ func TestManifestClientManifest(t *testing.T) {
assert.NotEmpty(t, manifest.Backend)
assert.NotEmpty(t, manifest.SettingsSchema)
}
+
+func TestManifestGetExecutableForRuntime(t *testing.T) {
+ testCases := []struct {
+ Description string
+ Manifest *Manifest
+ GoOs string
+ GoArch string
+ ExpectedExecutable string
+ }{
+ {
+ "no backend",
+ &Manifest{},
+ "linux",
+ "amd64",
+ "",
+ },
+ {
+ "no executable",
+ &Manifest{
+ Backend: &ManifestBackend{},
+ },
+ "linux",
+ "amd64",
+ "",
+ },
+ {
+ "single executable",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executable: "path/to/executable",
+ },
+ },
+ "linux",
+ "amd64",
+ "path/to/executable",
+ },
+ {
+ "single executable, different runtime",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executable: "path/to/executable",
+ },
+ },
+ "darwin",
+ "amd64",
+ "path/to/executable",
+ },
+ {
+ "multiple executables, no match",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executables: &ManifestExecutables{
+ LinuxAmd64: "linux-amd64/path/to/executable",
+ DarwinAmd64: "darwin-amd64/path/to/executable",
+ WindowsAmd64: "windows-amd64/path/to/executable",
+ },
+ },
+ },
+ "other",
+ "amd64",
+ "",
+ },
+ {
+ "multiple executables, linux-amd64 match",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executables: &ManifestExecutables{
+ LinuxAmd64: "linux-amd64/path/to/executable",
+ DarwinAmd64: "darwin-amd64/path/to/executable",
+ WindowsAmd64: "windows-amd64/path/to/executable",
+ },
+ },
+ },
+ "linux",
+ "amd64",
+ "linux-amd64/path/to/executable",
+ },
+ {
+ "multiple executables, linux-amd64 match, single executable ignored",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executables: &ManifestExecutables{
+ LinuxAmd64: "linux-amd64/path/to/executable",
+ DarwinAmd64: "darwin-amd64/path/to/executable",
+ WindowsAmd64: "windows-amd64/path/to/executable",
+ },
+ Executable: "path/to/executable",
+ },
+ },
+ "linux",
+ "amd64",
+ "linux-amd64/path/to/executable",
+ },
+ {
+ "multiple executables, darwin-amd64 match",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executables: &ManifestExecutables{
+ LinuxAmd64: "linux-amd64/path/to/executable",
+ DarwinAmd64: "darwin-amd64/path/to/executable",
+ WindowsAmd64: "windows-amd64/path/to/executable",
+ },
+ },
+ },
+ "darwin",
+ "amd64",
+ "darwin-amd64/path/to/executable",
+ },
+ {
+ "multiple executables, windows-amd64 match",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executables: &ManifestExecutables{
+ LinuxAmd64: "linux-amd64/path/to/executable",
+ DarwinAmd64: "darwin-amd64/path/to/executable",
+ WindowsAmd64: "windows-amd64/path/to/executable",
+ },
+ },
+ },
+ "windows",
+ "amd64",
+ "windows-amd64/path/to/executable",
+ },
+ {
+ "multiple executables, no match, single executable fallback",
+ &Manifest{
+ Backend: &ManifestBackend{
+ Executables: &ManifestExecutables{
+ LinuxAmd64: "linux-amd64/path/to/executable",
+ DarwinAmd64: "darwin-amd64/path/to/executable",
+ WindowsAmd64: "windows-amd64/path/to/executable",
+ },
+ Executable: "path/to/executable",
+ },
+ },
+ "other",
+ "amd64",
+ "path/to/executable",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.Description, func(t *testing.T) {
+ assert.Equal(
+ t,
+ testCase.ExpectedExecutable,
+ testCase.Manifest.GetExecutableForRuntime(testCase.GoOs, testCase.GoArch),
+ )
+ })
+ }
+}
diff --git a/plugin/supervisor.go b/plugin/supervisor.go
index 1e1005f53..f6264f47c 100644
--- a/plugin/supervisor.go
+++ b/plugin/supervisor.go
@@ -7,6 +7,7 @@ import (
"fmt"
"os/exec"
"path/filepath"
+ "runtime"
"strings"
"time"
@@ -39,7 +40,10 @@ func newSupervisor(pluginInfo *model.BundleInfo, parentLogger *mlog.Logger, apiI
},
}
- executable := filepath.Clean(filepath.Join(".", pluginInfo.Manifest.Backend.Executable))
+ executable := filepath.Clean(filepath.Join(
+ ".",
+ pluginInfo.Manifest.GetExecutableForRuntime(runtime.GOOS, runtime.GOARCH),
+ ))
if strings.HasPrefix(executable, "..") {
return nil, fmt.Errorf("invalid backend executable")
}