Skip to content

Commit

Permalink
feat: adding support for os/arch agnostic manifest url for plugin ins…
Browse files Browse the repository at this point in the history
…tall (#193)

* feat: adding support for os/arch agnostic manifest url for plugin installation

* feat: adjusting manifest processing logic, simplifying tests
  • Loading branch information
GabhenDM authored Nov 1, 2022
1 parent ab245ac commit e0a0129
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 3 deletions.
31 changes: 28 additions & 3 deletions tsuru/client/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"net/http"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/tsuru/gnuflag"
Expand All @@ -29,6 +30,17 @@ type Plugin struct {
URL string `json:"url"`
}

type PluginManifest struct {
SchemaVersion string `json:"SchemaVersion"`
Metadata PluginManifestMetadata `json:"Metadata"`
URLPerPlatform map[string]string `json:"UrlPerPlatform"`
}

type PluginManifestMetadata struct {
Name string `json:"Name"`
Version string `json:"Version"`
}

func (p *Plugin) Validate() error {
if p.Name == "" && p.URL == "" {
return fmt.Errorf("Zero value plugin (no Name nor URL)")
Expand Down Expand Up @@ -61,15 +73,18 @@ func (c *PluginInstall) Run(context *cmd.Context, client *cmd.Client) error {
}
pluginName := context.Args[0]
pluginURL := context.Args[1]
if err := installPlugin(pluginName, pluginURL); err != nil {
if err := installPlugin(pluginName, pluginURL, 0); err != nil {
return fmt.Errorf("Error installing plugin %q: %w", pluginName, err)
}

fmt.Fprintf(context.Stdout, `Plugin "%s" successfully installed!`+"\n", pluginName)
return nil
}

func installPlugin(pluginName, pluginURL string) error {
func installPlugin(pluginName, pluginURL string, level int) error {
if level > 1 { // Avoid infinite recursion
return fmt.Errorf("Infinite Recursion detected, check if manifest.json is correct")
}
tmpDir, err := filesystem().MkdirTemp("", "")
if err != nil {
return fmt.Errorf("Could not create a tmpdir: %w", err)
Expand All @@ -91,6 +106,16 @@ func installPlugin(pluginName, pluginURL string) error {
return fmt.Errorf("Invalid status code reading plugin: %d - %q", resp.StatusCode, string(data))
}

// try to unmarshall manifest
manifest := PluginManifest{}
if err = json.Unmarshal(data, &manifest); err == nil {
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) // get platform information
if url, ok := manifest.URLPerPlatform[platform]; ok {
return installPlugin(pluginName, url, level+1)
}
return fmt.Errorf("No plugin URL found for platform: %s", platform)
}

// Try to extract .tar.gz first, then .zip. Fallbacks to copy the content
extractErr := extractTarGz(tmpDir, bytes.NewReader(data))
if extractErr != nil {
Expand Down Expand Up @@ -416,7 +441,7 @@ func (c *PluginBundle) Run(context *cmd.Context, client *cmd.Client) error {
var successfulPlugins []string
failedPlugins := make(map[string]string)
for _, plugin := range bundleManifest.Plugins {
if err := installPlugin(plugin.Name, plugin.URL); err != nil {
if err := installPlugin(plugin.Name, plugin.URL, 0); err != nil {
failedPlugins[plugin.Name] = fmt.Sprintf("%v", err)
} else {
successfulPlugins = append(successfulPlugins, plugin.Name)
Expand Down
47 changes: 47 additions & 0 deletions tsuru/client/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"runtime"

"github.com/tsuru/tsuru/cmd"
"github.com/tsuru/tsuru/exec/exectest"
Expand All @@ -24,6 +25,52 @@ func (s *S) TestPluginInstallInfo(c *check.C) {
c.Assert(PluginInstall{}.Info(), check.NotNil)
}

func (s *S) TestPluginInstallWithManifest(c *check.C) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "fakeplugin")
}))
defer ts.Close()
ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
jsonResp := fmt.Sprintf(`{
"SchemaVersion":"1.0",
"Metadata": {"Name": "myplugin", "Version": "0.33.1"},
"URLPerPlatform": {
"%s/%s": "%s"
}
}`, runtime.GOOS, runtime.GOARCH, ts.URL)
fmt.Fprintln(w, jsonResp)
}))

defer ts2.Close()
rfs := fstest.RecordingFs{}
fsystem = &rfs
defer func() {
fsystem = nil
}()
var stdout bytes.Buffer
context := cmd.Context{
Args: []string{"myplugin", ts2.URL},
Stdout: &stdout,
}
client := cmd.NewClient(&http.Client{}, nil, manager)
command := PluginInstall{}
err := command.Run(&context, client)
c.Assert(err, check.IsNil)
pluginsPath := cmd.JoinWithUserDir(".tsuru", "plugins")
hasAction := rfs.HasAction(fmt.Sprintf("mkdirall %s with mode 0755", pluginsPath))
c.Assert(hasAction, check.Equals, true)
pluginPath := cmd.JoinWithUserDir(".tsuru", "plugins", "myplugin")
hasAction = rfs.HasAction(fmt.Sprintf("openfile %s with mode 0755", pluginPath))
c.Assert(hasAction, check.Equals, true)
f, err := rfs.Open(pluginPath)
c.Assert(err, check.IsNil)
data, err := ioutil.ReadAll(f)
c.Assert(err, check.IsNil)
c.Assert(string(data), check.Equals, "fakeplugin\n")
expected := `Plugin "myplugin" successfully installed!` + "\n"
c.Assert(stdout.String(), check.Equals, expected)
}

func (s *S) TestPluginInstall(c *check.C) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "fakeplugin")
Expand Down

0 comments on commit e0a0129

Please sign in to comment.