diff --git a/model/manifest.go b/model/manifest.go index e82b3b5ae5..3fba089075 100644 --- a/model/manifest.go +++ b/model/manifest.go @@ -196,9 +196,21 @@ type Manifest struct { } type ManifestServer struct { - // 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"` + // AllExecutables are the paths to your executable binaries, specifying multiple entry + // points for different platforms when bundled together in a single plugin. + AllExecutables map[string]string `json:"executables,omitempty" yaml:"executables,omitempty"` + + // Executables is a legacy field populated with a subset of supported platform executables. + // When unmarshalling, Executables is authoritative for the platform executable paths it + // contains, overriding any values in AllExecutables. When marshalling, AllExecutables + // is authoritative. + // + // Code duplication is avoided when (un)marshalling by leveraging type aliases in the + // various (Un)Marshal(JSON|YAML) methods, since aliases don't inherit the aliased type's + // methods. + // + // In v6.0, we should remove this field and rename AllExecutables back to Executables. + Executables *ManifestExecutables `json:"-" yaml:"-"` // 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. @@ -210,6 +222,79 @@ type ManifestServer struct { Executable string `json:"executable" yaml:"executable"` } +func (ms *ManifestServer) MarshalJSON() ([]byte, error) { + type auxManifestServer ManifestServer + + // Populate AllExecutables from Executables, if it exists. + if ms.Executables != nil { + if ms.AllExecutables == nil { + ms.AllExecutables = make(map[string]string) + } + + ms.AllExecutables["linux-amd64"] = ms.Executables.LinuxAmd64 + ms.AllExecutables["darwin-amd64"] = ms.Executables.DarwinAmd64 + ms.AllExecutables["windows-amd64"] = ms.Executables.WindowsAmd64 + } + + return json.Marshal((*auxManifestServer)(ms)) +} + +func (ms *ManifestServer) UnmarshalJSON(data []byte) error { + type auxManifestServer ManifestServer + + aux := (*auxManifestServer)(ms) + if err := json.Unmarshal(data, aux); err != nil { + return err + } + + if len(aux.AllExecutables) > 0 { + ms.Executables = &ManifestExecutables{ + LinuxAmd64: aux.AllExecutables["linux-amd64"], + DarwinAmd64: aux.AllExecutables["darwin-amd64"], + WindowsAmd64: aux.AllExecutables["windows-amd64"], + } + } + + return nil +} + +func (ms *ManifestServer) MarshalYAML() ([]byte, error) { + type auxManifestServer ManifestServer + + // Populate AllExecutables from Executables, if it exists. + if ms.Executables != nil { + if ms.AllExecutables == nil { + ms.AllExecutables = make(map[string]string) + } + + ms.AllExecutables["linux-amd64"] = ms.Executables.LinuxAmd64 + ms.AllExecutables["darwin-amd64"] = ms.Executables.DarwinAmd64 + ms.AllExecutables["windows-amd64"] = ms.Executables.WindowsAmd64 + } + + return yaml.Marshal((*auxManifestServer)(ms)) +} + +func (ms *ManifestServer) UnmarshalYAML(unmarshal func(interface{}) error) error { + type auxManifestServer ManifestServer + + aux := (*auxManifestServer)(ms) + if err := unmarshal(&aux); err != nil { + return err + } + + if len(aux.AllExecutables) > 0 { + ms.Executables = &ManifestExecutables{ + LinuxAmd64: aux.AllExecutables["linux-amd64"], + DarwinAmd64: aux.AllExecutables["darwin-amd64"], + WindowsAmd64: aux.AllExecutables["windows-amd64"], + } + } + + return nil +} + +// ManifestExecutables is a legacy structure capturing a subet of the known platform executables. 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"` @@ -287,14 +372,9 @@ func (m *Manifest) GetExecutableForRuntime(goOs, goArch string) string { } var executable string - if server.Executables != nil { - if goOs == "linux" && goArch == "amd64" { - executable = server.Executables.LinuxAmd64 - } else if goOs == "darwin" && goArch == "amd64" { - executable = server.Executables.DarwinAmd64 - } else if goOs == "windows" && goArch == "amd64" { - executable = server.Executables.WindowsAmd64 - } + if len(server.AllExecutables) > 0 { + osArch := fmt.Sprintf("%s-%s", goOs, goArch) + executable = server.AllExecutables[osArch] } if executable == "" { diff --git a/model/manifest_test.go b/model/manifest_test.go index 90f1569bfc..3efffdcea5 100644 --- a/model/manifest_test.go +++ b/model/manifest_test.go @@ -285,6 +285,12 @@ func TestManifestUnmarshal(t *testing.T) { DarwinAmd64: "theexecutable-darwin-amd64", WindowsAmd64: "theexecutable-windows-amd64", }, + AllExecutables: map[string]string{ + "linux-amd64": "theexecutable-linux-amd64", + "darwin-amd64": "theexecutable-darwin-amd64", + "windows-amd64": "theexecutable-windows-amd64", + "linux-arm64": "theexecutable-linux-arm64", + }, }, Webapp: &ManifestWebapp{ BundlePath: "thebundlepath", @@ -312,8 +318,9 @@ func TestManifestUnmarshal(t *testing.T) { }, } - var yamlResult Manifest - require.NoError(t, yaml.Unmarshal([]byte(` + t.Run("yaml", func(t *testing.T) { + var yamlResult Manifest + require.NoError(t, yaml.Unmarshal([]byte(` id: theid homepage_url: https://example.com support_url: https://example.com/support @@ -325,6 +332,7 @@ server: linux-amd64: theexecutable-linux-amd64 darwin-amd64: theexecutable-darwin-amd64 windows-amd64: theexecutable-windows-amd64 + linux-arm64: theexecutable-linux-arm64 webapp: bundle_path: thebundlepath settings_schema: @@ -342,10 +350,12 @@ settings_schema: value: thevalue default: thedefault `), &yamlResult)) - assert.Equal(t, expected, yamlResult) + assert.Equal(t, expected, yamlResult) + }) - var jsonResult Manifest - require.NoError(t, json.Unmarshal([]byte(`{ + t.Run("json", func(t *testing.T) { + var jsonResult Manifest + require.NoError(t, json.Unmarshal([]byte(`{ "id": "theid", "homepage_url": "https://example.com", "support_url": "https://example.com/support", @@ -356,7 +366,8 @@ settings_schema: "executables": { "linux-amd64": "theexecutable-linux-amd64", "darwin-amd64": "theexecutable-darwin-amd64", - "windows-amd64": "theexecutable-windows-amd64" + "windows-amd64": "theexecutable-windows-amd64", + "linux-arm64": "theexecutable-linux-arm64" } }, "webapp": { @@ -384,7 +395,8 @@ settings_schema: ] } }`), &jsonResult)) - assert.Equal(t, expected, jsonResult) + assert.Equal(t, expected, jsonResult) + }) } func TestFindManifest_FileErrors(t *testing.T) { @@ -434,6 +446,17 @@ func TestManifestJson(t *testing.T) { Id: "theid", Server: &ManifestServer{ Executable: "theexecutable", + Executables: &ManifestExecutables{ + LinuxAmd64: "linux-amd64/path/to/executable", + DarwinAmd64: "darwin-amd64/path/to/executable", + WindowsAmd64: "windows-amd64/path/to/executable", + }, + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", + }, }, Webapp: &ManifestWebapp{ BundlePath: "thebundlepath", @@ -597,22 +620,7 @@ func TestManifestGetExecutableForRuntime(t *testing.T) { "path/to/executable", }, { - "multiple executables, no match", - &Manifest{ - Server: &ManifestServer{ - 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", + "multiple legacy executables ignored", &Manifest{ Server: &ManifestServer{ Executables: &ManifestExecutables{ @@ -624,16 +632,49 @@ func TestManifestGetExecutableForRuntime(t *testing.T) { }, "linux", "amd64", + "", + }, + { + "multiple executables, no match", + &Manifest{ + Server: &ManifestServer{ + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", + }, + }, + }, + "other", + "amd64", + "", + }, + { + "multiple executables, linux-amd64 match", + &Manifest{ + Server: &ManifestServer{ + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", + }, + }, + }, + "linux", + "amd64", "linux-amd64/path/to/executable", }, { "multiple executables, linux-amd64 match, single executable ignored", &Manifest{ Server: &ManifestServer{ - Executables: &ManifestExecutables{ - LinuxAmd64: "linux-amd64/path/to/executable", - DarwinAmd64: "darwin-amd64/path/to/executable", - WindowsAmd64: "windows-amd64/path/to/executable", + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", }, Executable: "path/to/executable", }, @@ -646,10 +687,11 @@ func TestManifestGetExecutableForRuntime(t *testing.T) { "multiple executables, darwin-amd64 match", &Manifest{ Server: &ManifestServer{ - Executables: &ManifestExecutables{ - LinuxAmd64: "linux-amd64/path/to/executable", - DarwinAmd64: "darwin-amd64/path/to/executable", - WindowsAmd64: "windows-amd64/path/to/executable", + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", }, }, }, @@ -661,10 +703,11 @@ func TestManifestGetExecutableForRuntime(t *testing.T) { "multiple executables, windows-amd64 match", &Manifest{ Server: &ManifestServer{ - Executables: &ManifestExecutables{ - LinuxAmd64: "linux-amd64/path/to/executable", - DarwinAmd64: "darwin-amd64/path/to/executable", - WindowsAmd64: "windows-amd64/path/to/executable", + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", }, }, }, @@ -676,10 +719,11 @@ func TestManifestGetExecutableForRuntime(t *testing.T) { "multiple executables, no match, single executable fallback", &Manifest{ Server: &ManifestServer{ - Executables: &ManifestExecutables{ - LinuxAmd64: "linux-amd64/path/to/executable", - DarwinAmd64: "darwin-amd64/path/to/executable", - WindowsAmd64: "windows-amd64/path/to/executable", + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", }, Executable: "path/to/executable", }, @@ -692,17 +736,18 @@ func TestManifestGetExecutableForRuntime(t *testing.T) { "deprecated backend field, ignored since server present", &Manifest{ Server: &ManifestServer{ - Executables: &ManifestExecutables{ - LinuxAmd64: "linux-amd64/path/to/executable", - DarwinAmd64: "darwin-amd64/path/to/executable", - WindowsAmd64: "windows-amd64/path/to/executable", + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", }, }, Backend: &ManifestServer{ - Executables: &ManifestExecutables{ - LinuxAmd64: "linux-amd64/path/to/executable/backend", - DarwinAmd64: "darwin-amd64/path/to/executable/backend", - WindowsAmd64: "windows-amd64/path/to/executable/backend", + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", }, }, }, @@ -714,16 +759,58 @@ func TestManifestGetExecutableForRuntime(t *testing.T) { "deprecated backend field used, since no server present", &Manifest{ Backend: &ManifestServer{ - Executables: &ManifestExecutables{ - LinuxAmd64: "linux-amd64/path/to/executable/backend", - DarwinAmd64: "darwin-amd64/path/to/executable/backend", - WindowsAmd64: "windows-amd64/path/to/executable/backend", + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", }, }, }, "linux", "amd64", - "linux-amd64/path/to/executable/backend", + "linux-amd64/path/to/executable", + }, + { + "all executables, linux-arm64", + &Manifest{ + Backend: &ManifestServer{ + Executables: &ManifestExecutables{ + LinuxAmd64: "linux-amd64/path/to/executable", + DarwinAmd64: "darwin-amd64/path/to/executable", + WindowsAmd64: "windows-amd64/path/to/executable", + }, + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", + }, + }, + }, + "linux", + "arm64", + "linux-arm64/path/to/executable", + }, + { + "all executables, linux-amd64, legacy executables ignored", + &Manifest{ + Backend: &ManifestServer{ + Executables: &ManifestExecutables{ + LinuxAmd64: "linux-amd64/ignored/path/to/executable", + DarwinAmd64: "darwin-amd64/ignored/path/to/executable", + WindowsAmd64: "windows-amd64/ignored/path/to/executable", + }, + AllExecutables: map[string]string{ + "linux-amd64": "linux-amd64/path/to/executable", + "darwin-amd64": "darwin-amd64/path/to/executable", + "windows-amd64": "windows-amd64/path/to/executable", + "linux-arm64": "linux-arm64/path/to/executable", + }, + }, + }, + "linux", + "amd64", + "linux-amd64/path/to/executable", }, }