Skip to content

Commit

Permalink
Merge pull request #98 from vdice/feat/add-loader-from-duffle
Browse files Browse the repository at this point in the history
feat(bundle): add loader pkg from duffle
  • Loading branch information
carolynvs-msft authored Aug 12, 2019
2 parents 4a8869e + 405b451 commit b47ce64
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
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) {

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)
}

0 comments on commit b47ce64

Please sign in to comment.