Skip to content

Commit

Permalink
Fixes #8
Browse files Browse the repository at this point in the history
Added support or mappers
Fixed distributions
  • Loading branch information
leucos committed Aug 19, 2020
1 parent 6ea981c commit e3b34e0
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 38 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,18 @@ Currently supported distributions are:
- binenv
- cli53
- consul
- docker-compose (soon [#8](https://github.com/devops-works/binenv/issues/8))
- hadolint (soon [#8](https://github.com/devops-works/binenv/issues/8))
- docker-compose
- hadolint
- helm
- helmfile
- hugo (soon [#8](https://github.com/devops-works/binenv/issues/8))
- hugo
- k9s
- krew
- kops
- kube-bench (soon [#8](https://github.com/devops-works/binenv/issues/8))
- kube-bench
- kubectl
- kubectx (soon [#8](https://github.com/devops-works/binenv/issues/8))
- kubens (soon [#8](https://github.com/devops-works/binenv/issues/8))
- kubectx
- kubens
- rancher[^1] (soon [#11](https://github.com/devops-works/binenv/issues/11))
- terraform
- terragrunt
Expand Down
8 changes: 4 additions & 4 deletions distributions/distributions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ sources:

hugo:
map:
amd64: 64bits
"386": 32bits
amd64: 64bit
"386": 32bit
darwin: Darwin
linux: Linux
arm: ARM
Expand All @@ -71,7 +71,7 @@ sources:
type: github-releases
url: https://api.github.com/repos/gohugoio/hugo/releases
fetch:
url: https://github.com/gohugoio/hugo/releases/download/v{{ .Version }}/hugo-{{ .Version }}_{{ .OS }}-{{ .Arch }}
url: https://github.com/gohugoio/hugo/releases/download/v{{ .Version }}/hugo_{{ .Version }}_{{ .OS }}-{{ .Arch }}.tar.gz
install:
type: direct

Expand All @@ -85,7 +85,7 @@ sources:
type: github-releases
url: https://api.github.com/repos/hadolint/hadolint/releases
fetch:
url: https://github.com/hadolint/hadolint/releases/download/{{ .Version }}/hadolint-{{ .OS }}-{{ .Arch }}
url: https://github.com/hadolint/hadolint/releases/download/v{{ .Version }}/hadolint-{{ .OS }}-{{ .Arch }}
install:
type: direct

Expand Down
37 changes: 27 additions & 10 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -41,6 +42,11 @@ type App struct {
logger *log.Logger
}

var (
// ErrAlreadyInstalled is returned when the requested version is already installed
ErrAlreadyInstalled = errors.New("version already installed")
)

// New create a new app instance
func New(o ...func(*App) error) (*App, error) {
d, err := homedir.Dir()
Expand Down Expand Up @@ -202,11 +208,14 @@ func (a *App) Install(specs ...string) error {
version = specs[i+1]
}
v, err := a.install(dist, version)
if err != nil {
log.Errorf("unable to install %q version %q: %v", dist, version, err)
switch {
case errors.Is(err, ErrAlreadyInstalled):
case err != nil:
log.Errorf("unable to install %q version %q: %v", dist, v, err)
continue
default:
fmt.Printf("%q version %q installed\n", dist, v)
}
fmt.Printf("%q version %q installed\n", dist, v)

}
return nil
Expand Down Expand Up @@ -242,25 +251,32 @@ func (a *App) install(dist, version string) (string, error) {
version = cleanVersion.String()
if stringInSlice(version, versions) {
a.logger.Warnf("version %q already installed for %q", version, dist)
return "", nil
return version, ErrAlreadyInstalled
}

var m mapping.Mapper
{
if v, ok := a.mappers[dist]; ok {
m = v
}
}

// Call fetcher for distribution
file, err := a.fetchers[dist].Fetch(context.Background(), version)
file, err := a.fetchers[dist].Fetch(context.Background(), version, m)
if err != nil {
return "", err
return version, err
}

// Create destination directory
if _, err := os.Stat(a.getBinDirFor(dist)); os.IsNotExist(err) {
err := os.MkdirAll(a.getBinDirFor(dist), 0750)
if err != nil {
return "", err
return version, err
}
}

if a.installers[dist] == nil {
return "", fmt.Errorf("no installer found for %s", dist)
return version, fmt.Errorf("no installer found for %s", dist)
}
err = a.installers[dist].Install(
file,
Expand All @@ -269,14 +285,15 @@ func (a *App) install(dist, version string) (string, error) {
gov.Must(gov.NewVersion(version)).String(),
),
version,
m,
)
if err != nil {
return "", err
return version, err
}

err = a.CreateShimFor(dist)
if err != nil {
return "", err
return version, err
}

return version, nil
Expand Down
5 changes: 3 additions & 2 deletions internal/fetch/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

log "github.com/sirupsen/logrus"

"github.com/devops-works/binenv/internal/mapping"
"github.com/devops-works/binenv/internal/tpl"
)

Expand All @@ -19,8 +20,8 @@ type Download struct {
}

// Fetch gets the package and returns location of downloaded file
func (d Download) Fetch(ctx context.Context, v string) (string, error) {
args := tpl.New(v)
func (d Download) Fetch(ctx context.Context, v string, mapper mapping.Mapper) (string, error) {
args := tpl.New(v, mapper)

url, err := args.Render(d.url)
if err != nil {
Expand Down
8 changes: 6 additions & 2 deletions internal/fetch/fetch.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package fetch

import "context"
import (
"context"

"github.com/devops-works/binenv/internal/mapping"
)

// Fetcher should implement fetching a release from a version
// and return a path where the release has been downloaded
type Fetcher interface {
Fetch(context.Context, string) (string, error)
Fetch(ctx context.Context, version string, mapper mapping.Mapper) (string, error)
}

// Fetch contains fetch configuration
Expand Down
4 changes: 3 additions & 1 deletion internal/install/direct.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package install

import "github.com/devops-works/binenv/internal/mapping"

// Direct installs directly downloaded binaries
type Direct struct {
filter string
}

// Install will move the binary from src to dst
func (d Direct) Install(src, dst, version string) error {
func (d Direct) Install(src, dst, version string, mapper mapping.Mapper) error {
return installFile(src, dst)
}
4 changes: 3 additions & 1 deletion internal/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package install
import (
"io"
"os"

"github.com/devops-works/binenv/internal/mapping"
)

// Install defines the install config struct
Expand All @@ -13,7 +15,7 @@ type Install struct {

// Installer should implement installation
type Installer interface {
Install(src, dst, version string) error
Install(src, dst, version string, mapper mapping.Mapper) error
}

// Factory returns instances that comply to Installer interface
Expand Down
5 changes: 3 additions & 2 deletions internal/install/tgz.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"os"

"github.com/devops-works/binenv/internal/mapping"
"github.com/devops-works/binenv/internal/tpl"
)

Expand All @@ -16,7 +17,7 @@ type Tgz struct {
}

// Install files from tgz file
func (t Tgz) Install(src, dst, version string) error {
func (t Tgz) Install(src, dst, version string, mapper mapping.Mapper) error {
// var filenames []string

f, err := os.Open(src)
Expand All @@ -31,7 +32,7 @@ func (t Tgz) Install(src, dst, version string) error {
}

tarReader := tar.NewReader(gzf)
args := tpl.New(version)
args := tpl.New(version, mapper)

for true {
header, err := tarReader.Next()
Expand Down
5 changes: 3 additions & 2 deletions internal/install/zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"os"

"github.com/devops-works/binenv/internal/mapping"
"github.com/devops-works/binenv/internal/tpl"
)

Expand All @@ -14,7 +15,7 @@ type Zip struct {
}

// Install files from zip file
func (z Zip) Install(src, dst, version string) error {
func (z Zip) Install(src, dst, version string, mapper mapping.Mapper) error {
// var filenames []string

r, err := zip.OpenReader(src)
Expand All @@ -23,7 +24,7 @@ func (z Zip) Install(src, dst, version string) error {
}
defer r.Close()

args := tpl.New(version)
args := tpl.New(version, mapper)
for _, f := range r.File {
ok, err := args.MatchFilters(f.Name, z.filters)
if err != nil {
Expand Down
11 changes: 6 additions & 5 deletions internal/mapping/mapping.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package mapping

// Remapper contains list definition
type Remapper struct {
Entries map[string]string `yaml:"map"`
}
type Remapper map[string]string

// Mapper should return a list of available release versions
type Mapper interface {
Expand All @@ -13,15 +11,18 @@ type Mapper interface {

// MustInterpolate interpolates a key
func (r Remapper) MustInterpolate(k string) string {
if v, ok := r.Entries[k]; ok {
if v, ok := r[k]; ok {
return v
}
return k
}

// IsZero returns true if the map is empty
func (r Remapper) IsZero() bool {
if r.Entries == nil {
if r == nil {
return true
}
if len(r) == 0 {
return true
}
return false
Expand Down
53 changes: 53 additions & 0 deletions internal/mapping/mapping_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package mapping

import "testing"

func TestRemapper_MustInterpolate(t *testing.T) {

tests := []struct {
name string
entries map[string]string
k string
want string
}{
{name: "arch", entries: map[string]string{"amd64": "x86_64"}, k: "amd64", want: "x86_64"},
{name: "same when empty", entries: map[string]string{}, k: "amd64", want: "amd64"},
{name: "os+arch", entries: map[string]string{"linux": "Linux", "amd64": "x86_64"}, k: "amd64", want: "x86_64"},
{name: "os+arch", entries: map[string]string{"linux": "Linux", "amd64": "x86_64"}, k: "linux", want: "Linux"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := Remapper(tt.entries)

if got := r.MustInterpolate(tt.k); got != tt.want {
t.Errorf("Remapper.MustInterpolate() = %v, want %v", got, tt.want)
}
})
}
}

func TestRemapper_IsZero(t *testing.T) {
type fields struct {
Entries map[string]string
}
tests := []struct {
name string
entries map[string]string
want bool
}{
{name: "empty", entries: map[string]string{}, want: true},
{name: "nil", want: true},
{name: "not empty", entries: map[string]string{"amd64": "AMD64"}, want: false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := Remapper(tt.entries)

if got := r.IsZero(); got != tt.want {
t.Errorf("Remapper.MustInterpolate() = %v, want %v", got, tt.want)
}
})
}
}
16 changes: 13 additions & 3 deletions internal/tpl/tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"sync"
"text/template"

"github.com/devops-works/binenv/internal/mapping"
gov "github.com/hashicorp/go-version"
)

Expand All @@ -21,10 +22,19 @@ type Args struct {
}

// New returns populated template Args
func New(v string) Args {
func New(v string, mapper mapping.Mapper) Args {
rarch := runtime.GOARCH
ros := runtime.GOOS

if mapper != nil {
rarch = mapper.MustInterpolate(runtime.GOARCH)
ros = mapper.MustInterpolate(runtime.GOOS)
fmt.Printf("remapping arch %s to %s\n", runtime.GOARCH, rarch)
fmt.Printf("remapping os %s to %s\n", runtime.GOOS, ros)
}
a := Args{
Arch: runtime.GOARCH,
OS: runtime.GOOS,
Arch: rarch,
OS: ros,
Version: v,
NakedVersion: gov.Must(gov.NewVersion(v)).String(),
}
Expand Down

0 comments on commit e3b34e0

Please sign in to comment.