Skip to content

Commit

Permalink
feat: introduce plugins tmvp (#17)
Browse files Browse the repository at this point in the history
* refactor: move source interface to types package

* refactor: convert ListSources to GetDefaultSource since we have only one

* feat: introduce plugins
  • Loading branch information
aweris authored Dec 13, 2022
1 parent df44d6c commit 436ded9
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 70 deletions.
2 changes: 1 addition & 1 deletion cmd/initialize/intialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewCommand(out output.Output) *cobra.Command {
return fmt.Errorf("failed to initialize avm: %w", err)
}

fmt.Printf("Initialized avm with sources: %s\n", avm.ListSources())
fmt.Printf("Initialized avm with sources: %s\n", avm.GetDefaultSource())

return nil
},
Expand Down
70 changes: 44 additions & 26 deletions cmd/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"github.com/spf13/cobra"

"github.com/mesosphere/dkp-cli-runtime/core/output"

avmpkg "github.com/d2iq-labs/avm/pkg/avm"
"github.com/d2iq-labs/avm/pkg/types"
)

func NewCommand(out output.Output) *cobra.Command {
Expand All @@ -19,8 +22,6 @@ func NewCommand(out output.Output) *cobra.Command {

// Subcommands
cmd.AddCommand(InstallCommand(out))
cmd.AddCommand(AddCommand(out))
cmd.AddCommand(RemoveCommand(out))
cmd.AddCommand(ListCommand(out))

return cmd
Expand All @@ -32,31 +33,25 @@ func InstallCommand(out output.Output) *cobra.Command {
Use: "install",
Short: "Installs a tool for a specific plugin",
RunE: func(cmd *cobra.Command, args []string) error {
out.V(6).Info(fmt.Sprintf("args: %v", args))
return nil
},
}
}
avm, err := avmpkg.New(out)
if err != nil {
return fmt.Errorf("failed to initialize avm: %w", err)
}

// AddCommand creates a new command to add a plugin
func AddCommand(out output.Output) *cobra.Command {
return &cobra.Command{
Use: "add",
Short: "Adds a plugin",
RunE: func(cmd *cobra.Command, args []string) error {
out.V(6).Info(fmt.Sprintf("args: %v", args))
return nil
},
}
}
defaultSource := avm.GetDefaultSource()

err = defaultSource.InstallPluginVersion(
&types.InstallPluginVersionRequest{
Name: "golang",
Version: "1.19.3",
},
)
if err != nil {
return fmt.Errorf("failed to install plugin: %w", err)
}

fmt.Printf("installed plugin %s with version %s\n", "golang", "1.19.3")

// RemoveCommand creates a new command to remove a plugin
func RemoveCommand(out output.Output) *cobra.Command {
return &cobra.Command{
Use: "remove",
Short: "Removes a plugin",
RunE: func(cmd *cobra.Command, args []string) error {
out.V(6).Info(fmt.Sprintf("args: %v", args))
return nil
},
}
Expand All @@ -68,7 +63,30 @@ func ListCommand(out output.Output) *cobra.Command {
Use: "list",
Short: "List all plugins",
RunE: func(cmd *cobra.Command, args []string) error {
out.V(6).Info(fmt.Sprintf("args: %v", args))
avm, err := avmpkg.New(out)
if err != nil {
return fmt.Errorf("failed to initialize avm: %w", err)
}

defaultSource := avm.GetDefaultSource()

plugins, err := defaultSource.ListPlugins()
if err != nil {
return fmt.Errorf("failed to list plugins: %w", err)
}

for _, plugin := range plugins {
fmt.Printf("%s\n", plugin.Name)

versions, err := plugin.Versions()
if err != nil {
return fmt.Errorf("failed to list versions: %w", err)
}

for _, version := range versions {
fmt.Printf("\t%s\n", version.Version)
}
}
return nil
},
}
Expand Down
5 changes: 2 additions & 3 deletions cmd/source/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ func ListCommand(out output.Output) *cobra.Command {
if err != nil {
return fmt.Errorf("failed to initialize avm: %w", err)
}
// we only have one source for now, so we can just print it.
fmt.Printf("%s\n", avm.GetDefaultSource().Name())

for _, source := range avm.ListSources() {
fmt.Printf("%s\n", source.Name())
}
return nil
},
}
Expand Down
21 changes: 8 additions & 13 deletions pkg/avm/avm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import (
"github.com/mesosphere/dkp-cli-runtime/core/output"

"github.com/d2iq-labs/avm/pkg/config"
"github.com/d2iq-labs/avm/pkg/sources"
asdfpkg "github.com/d2iq-labs/avm/pkg/sources/asdf"
"github.com/d2iq-labs/avm/pkg/types"
)

var _ AVM = new(avm)

// AVM provides an interface for managing the avm.
type AVM interface {
// ListSources returns a list of installed sources.
ListSources() []sources.Source
// GetDefaultSource returns the default the source.
GetDefaultSource() types.Source
}

type avm struct {
out output.Output
cfg config.Config
sources map[string]sources.Source
sources map[string]types.Source
}

func New(out output.Output) (AVM, error) {
Expand All @@ -46,7 +46,7 @@ func New(out output.Output) (AVM, error) {
return nil, err
}

avm := &avm{out: out, cfg: cfg, sources: make(map[string]sources.Source)}
avm := &avm{out: out, cfg: cfg, sources: make(map[string]types.Source)}

// install sources, if not already installed. this is a no-op if the source is already installed.
asdf, err := asdfpkg.New(cfg, out)
Expand All @@ -59,14 +59,9 @@ func New(out output.Output) (AVM, error) {
return avm, nil
}

func (a *avm) ListSources() []sources.Source {
var sourceList []sources.Source

for _, source := range a.sources {
sourceList = append(sourceList, source)
}

return sourceList
func (a *avm) GetDefaultSource() types.Source {
// we only have one source right now, so just return it
return a.sources["asdf"]
}

// ensureDirectory ensures that the given directory path exists.
Expand Down
14 changes: 0 additions & 14 deletions pkg/sources/asdf/cmd.go

This file was deleted.

126 changes: 123 additions & 3 deletions pkg/sources/asdf/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@
package asdf

import (
"bufio"
"bytes"
"fmt"
"github.com/magefile/mage/sh"
"os"
"path/filepath"
"strings"

_ "embed"

"github.com/magefile/mage/sh"

"github.com/mesosphere/dkp-cli-runtime/core/output"

"github.com/d2iq-labs/avm/pkg/config"
"github.com/d2iq-labs/avm/pkg/sources"
"github.com/d2iq-labs/avm/pkg/types"
)

var (
//go:embed bin/entrypoint.sh
entrypoint string

// ensure that asdf implements the Source interface
_ sources.Source = new(Asdf)
_ types.Source = new(Asdf)
)

// Asdf is a source plugin for asdf
Expand Down Expand Up @@ -74,3 +78,119 @@ func New(cfg config.Config, out output.Output) (*Asdf, error) {
func (a *Asdf) Name() string {
return "asdf"
}

func (a *Asdf) GetPlugin(name string) (*types.Plugin, error) {
plugins, err := a.ListPlugins()
if err != nil {
return nil, err
}

plugin, ok := plugins[name]
if !ok {
return nil, fmt.Errorf("plugin %s not found", name)
}

return plugin, nil
}

func (a *Asdf) ListPlugins() (map[string]*types.Plugin, error) {
plugins := make(map[string]*types.Plugin)

out, err := a.execute("plugin", "list", "--urls")

// no need to check for err here, asdf returns an error if no plugins are installed
if out == "No plugins installed" {
return plugins, nil
}

if err != nil {
return nil, err
}

plugin := bufio.NewScanner(strings.NewReader(out))

for plugin.Scan() {
line := strings.TrimSpace(plugin.Text())

tokens := strings.Fields(line)

if len(tokens) != 2 {
return nil, fmt.Errorf("unexpected output from asdf plugin list: %+v", tokens)
}

name := tokens[0]
url := tokens[1]

plugins[name] = &types.Plugin{
Name: name,
URL: url,
Source: a,
}
}

return plugins, nil
}

func (a *Asdf) ListPluginVersions(req *types.ListPluginVersionsRequest) ([]*types.PluginVersion, error) {
var versions []*types.PluginVersion

args := []string{"list"}

if req.AllVersions {
args = append(args, "all")
}

args = append(args, req.Name)

out, err := a.execute(args...)
if err != nil {
return nil, err
}

scanner := bufio.NewScanner(strings.NewReader(out))

for scanner.Scan() {
version := strings.TrimSpace(scanner.Text())

if version == "" {
continue
}

versions = append(versions, &types.PluginVersion{Version: version})
}

return versions, nil
}

func (a *Asdf) InstallPluginVersion(req *types.InstallPluginVersionRequest) error {
plugins, err := a.ListPlugins()
if err != nil {
return err
}

if _, ok := plugins[req.Name]; !ok {
_, err := a.execute("plugin", "add", req.Name, req.URL)
if err != nil {
return fmt.Errorf("failed to install plugin %s: %w", req.Name, err)
}
}

_, err = a.execute("install", req.Name, req.Version)
if err != nil {
return fmt.Errorf("failed to install plugin %s version %s: %w", req.Name, req.Version, err)
}

return nil
}

func (a *Asdf) execute(args ...string) (string, error) {
bufOut := &bytes.Buffer{}
bufErr := &bytes.Buffer{}

_, err := sh.Exec(a.env, bufOut, bufErr, filepath.Join(a.path, "bin", "entrypoint.sh"), args...)
if err != nil {
return strings.TrimSuffix(bufErr.String(), "\n"), err
}

return strings.TrimSuffix(bufOut.String(), "\n"), err
}
10 changes: 0 additions & 10 deletions pkg/sources/sources.go

This file was deleted.

Loading

0 comments on commit 436ded9

Please sign in to comment.