Skip to content

Commit

Permalink
feat(tools): Add tools installer to automatically install tools.
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveRuble committed Apr 3, 2019
1 parent b7a46b2 commit c397ed2
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 3 deletions.
2 changes: 1 addition & 1 deletion bosun.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ environments: []
appRefs: {}
apps:
- name: bosun
version: 0.5.2
version: 0.6.0
images: []
scripts:
- name: publish
Expand Down
7 changes: 6 additions & 1 deletion cmd/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,9 @@ var err error

return nil
},
})
})

func init(){
rootCmd.AddCommand(metaUpgradeCmd)
}

114 changes: 114 additions & 0 deletions cmd/tools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright © 2018 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"fmt"
"github.com/cheynewallace/tabby"
"github.com/kyokomi/emoji"
"github.com/naveego/bosun/pkg/bosun"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

var toolsCmd = addCommand(rootCmd, &cobra.Command{
Use: "tools",
Short: "Commands for listing and installing tools.",

})

var toolsListCmd = addCommand(toolsCmd, &cobra.Command{
Use: "list",
Short: "Lists known tools",
Run: func(cmd *cobra.Command, args []string) {
b := mustGetBosun()
tools := b.GetTools()

byFromPath := map[string][]bosun.ToolDef{}
for _, tool := range tools {
byFromPath[tool.FromPath] = append(byFromPath[tool.FromPath], tool)
}

for fromPath, tools := range byFromPath {
fmt.Printf("Defined in %s:\n", fromPath)
t := tabby.New()
t.AddHeader("Name", "Installed", "Location", "Description")

for _, tool := range tools {

var installInfo string
var location string
executable, installErr := tool.GetExecutable()
if installErr != nil {
if tool.Installer != nil {
if _, ok := tool.GetInstaller(); ok {
installInfo = emoji.Sprint(":cloud:")
location = "(installable)"
} else {
installInfo = emoji.Sprintf(":x:")
}
}
} else {
installInfo = emoji.Sprintf(":heavy_check_mark:")
location = executable
}

t.AddLine(tool.Name, installInfo, location, tool.Description)
}
t.Print()
fmt.Println()
}
},
})

var toolsInstallCmd = addCommand(toolsCmd, &cobra.Command{
Use: "install {tool}",
Short: "Installs a tool.",
SilenceUsage:true,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
b := mustGetBosun()
tools := b.GetTools()
var tool bosun.ToolDef
var ok bool
name := args[0]
for _, tool = range tools {
if tool.Name == name {
ok = true
break
}
}
if !ok {
return errors.Errorf("no tool found with name %q", name)
}

ctx := b.NewContext()

installer, ok := tool.GetInstaller()
if !ok {
return errors.Errorf("could not get installer for %q", name)
}

err := installer.Execute(ctx)

return err
},
})


func init(){
rootCmd.AddCommand(metaUpgradeCmd)
}

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ require (
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/keybase/go-crypto v0.0.0-20181127160227-255a5089e85a // indirect
github.com/kr/binarydist v0.1.0 // indirect
github.com/kyokomi/emoji v2.1.0+incompatible
github.com/lib/pq v1.0.0 // indirect
github.com/magefile/mage v1.8.0
github.com/manifoldco/promptui v0.3.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyokomi/emoji v2.1.0+incompatible h1:+DYU2RgpI6OHG4oQkM5KlqD3Wd3UPEsX8jamTo1Mp6o=
github.com/kyokomi/emoji v2.1.0+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
Expand Down
4 changes: 4 additions & 0 deletions pkg/bosun/bosun.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,4 +537,8 @@ func (b *Bosun) ConfirmEnvironment() error {
}

return errors.Errorf("The %q environment is protected, so you must confirm that you want to perform this action.\n(you can do this by setting the --confirm-env to the name of the environment)", b.env.Name)
}

func (b *Bosun) GetTools() []ToolDef {
return b.ws.MergedBosunFile.Tools
}
4 changes: 3 additions & 1 deletion pkg/bosun/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ type File struct {
FromPath string `yaml:"fromPath"`
Config *Workspace `yaml:"-"`
Releases []*ReleaseConfig `yaml:"releases,omitempty"`
Tools []ToolDef `yaml:"tools,omitempty"`
// merged indicates that this File has had File instances merged into it and cannot be saved.
merged bool `yaml:"-"`
}


func (c *File) Merge(other *File) error {

c.merged = true
Expand All @@ -47,6 +47,8 @@ func (c *File) Merge(other *File) error {
c.mergeRelease(other)
}

c.Tools = append(c.Tools, other.Tools...)

return nil
}

Expand Down
132 changes: 132 additions & 0 deletions pkg/bosun/tools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package bosun

import (
"github.com/hashicorp/go-getter"
"github.com/pkg/errors"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)

type ToolDef struct {
FromPath string `yaml:"-"`
Name string `yaml:"name"`
Description string `yaml:"description"`
URL string `yaml:"url,omitempty"`
Cmd map[string]string `yaml:"cmd,omitempty"`
Installer map[string]Installer `yaml:"installer,omitempty"`
}

type Installer struct {
Script string `yaml:"script,omitempty"`
Getter *GetterConfig `yaml:"getter,omitempty"`
}

type GetterConfig struct {
URL string `yaml:"url"`
Mappings map[string]string `yaml:"mappings"`
}

func (t ToolDef) GetExecutable() (string, error) {

var ok bool
var cmd string
for key, val := range t.Cmd {
if strings.Contains(key, runtime.GOOS) {
cmd = val
ok = true
}
}
if !ok {
return "", errors.Errorf("no cmd registered for os %q", runtime.GOOS)
}

ex, err := exec.LookPath(cmd)
return ex, err
}

func (t ToolDef) GetInstaller() (*Installer, bool) {

if t.Installer == nil {
return nil, false
}

var ok bool
var installer Installer
for key, val := range t.Installer {
if strings.Contains(key, runtime.GOOS) {
installer = val
ok = true
}
}

if !ok {
return nil, false
}

return &installer, true
}

func (t ToolDef) RunInstall(ctx BosunContext) error {
installer, ok := t.GetInstaller()
if !ok {
return errors.New("no installer script available")
}

err := installer.Execute(ctx)
if err != nil {
return err
}

_, err = t.GetExecutable()
if err != nil {
return errors.Wrap(err, "install completed, but executable still not found")
}

return nil
}

func (i Installer) Execute(ctx BosunContext) error {
if i.Script != "" {
cmd := &Command{Script:i.Script}
_, err := cmd.Execute(ctx, CommandOpts{StreamOutput:true})
return err
}

if i.Getter != nil {
tmp, err := ioutil.TempDir(os.TempDir(), "bosun-install")
if err != nil {
return err
}

ctx.Log.Debugf("Downloading from %s to %s", i.Getter.URL, tmp)

defer func(){
ctx.Log.Debugf("Deleting %s", tmp)
os.RemoveAll(tmp)
} ()

err = getter.Get(tmp, i.Getter.URL)
if err != nil {
return errors.Errorf("error getting content from %q: %s", i.Getter.URL, err)
}
ctx.Log.Debugf("Download complete.")

for from, to := range i.Getter.Mappings {

from = filepath.Join(tmp, from)
to = os.ExpandEnv(to)

ctx.Log.Debugf("Moving %s to %s.")
err = os.Rename(from, to)
if err != nil {
return err
}
}
}

return errors.New("no install strategy defined")
}
4 changes: 4 additions & 0 deletions pkg/bosun/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ func (r *Workspace) importFileFromPath(path string) error {
m.SetParent(c)
}

for i := range c.Tools {
c.Tools[i].FromPath = c.FromPath
}

err = r.MergedBosunFile.Merge(c)

if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions pkg/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ func (c *Command) prepare() {

c.cmd = exec.Command(exe, c.Args...)

c.cmd.Stdin = os.Stdin

if c.Dir != nil {
c.cmd.Dir = *c.Dir
}
Expand Down

0 comments on commit c397ed2

Please sign in to comment.