Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bundle): add loader pkg from duffle #98

Merged
merged 1 commit into from
Aug 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions bundle/loader/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package loader

import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"

"github.com/deislabs/cnab-go/bundle"
)

// BundleLoader provides an interface for loading a bundle
type BundleLoader interface {
// Load a bundle from a local file
Load(source string) (*bundle.Bundle, error)
// Load a bundle from raw data
LoadData(data []byte) (*bundle.Bundle, error)
}

// Loader loads a bundle manifest (bundle.json)
type Loader struct{}

// New creates a loader for bundle files.
//TODO: remove if unnecessary
func New() BundleLoader {
return &Loader{}
}

func NewLoader() *Loader {
return &Loader{}
}

// Load loads the given bundle.
func (l *Loader) Load(filename string) (*bundle.Bundle, error) {
b := &bundle.Bundle{}
data, err := loadData(filename)
if err != nil {
return b, err
}
return l.LoadData(data)
}

// LoadData loads a Bundle from the given data.
//
// This loads a JSON bundle file into a *bundle.Bundle.
func (l *Loader) LoadData(data []byte) (*bundle.Bundle, error) {
return bundle.Unmarshal(data)
}

// loadData is a utility method that loads a file either off of the FS (if it exists) or via a remote HTTP GET.
//
// If bundleFile exists on disk, this will return that file. Otherwise, it will attempt to parse the
// file name as a URL and request it as an HTTP GET request.
func loadData(bundleFile string) ([]byte, error) {
if isLocalReference(bundleFile) {
return ioutil.ReadFile(bundleFile)
}

if u, err := url.ParseRequestURI(bundleFile); err != nil {
// The error emitted by ParseRequestURI is icky.
return []byte{}, fmt.Errorf("bundle %q not found", bundleFile)
} else if u.Scheme == "file" {
// What do we do if someone passes a `file:///` URL in? Is `file` inferred
// if no protocol is specified?
return []byte{}, fmt.Errorf("bundle %q not found", bundleFile)
}

response, err := http.Get(bundleFile)
if err != nil {
return []byte{}, fmt.Errorf("cannot download bundle file: %v", err)
}
defer response.Body.Close()

return ioutil.ReadAll(response.Body)
}

func isLocalReference(file string) bool {
_, err := os.Stat(file)
return err == nil
}
50 changes: 50 additions & 0 deletions bundle/loader/loader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package loader

import (
"bytes"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

var testFooJSON = filepath.Join("..", "testdata", "minimal.json")

func TestLoader(t *testing.T) {
is := assert.New(t)

l := NewLoader()
bundle, err := l.Load(testFooJSON)
if err != nil {
t.Fatalf("cannot load bundle: %v", err)
}

is.Equal("mybun", bundle.Name)
is.Equal("v1.0.0", bundle.Version)
}
func TestLoader_Remote(t *testing.T) {

silvin-lubecki marked this conversation as resolved.
Show resolved Hide resolved
data, err := ioutil.ReadFile(testFooJSON)
if err != nil {
t.Fatalf("cannot read bundle file: %v", err)
}

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
io.Copy(w, bytes.NewBuffer(data))
}))
defer ts.Close()

l := NewLoader()
bundle, err := l.Load(ts.URL)
if err != nil {
t.Fatal(err)
}
is := assert.New(t)

is.Equal("mybun", bundle.Name)
is.Equal("v1.0.0", bundle.Version)
}