diff --git a/Makefile b/Makefile
index ea646f520885aabca2c46aa8fe556971193be41d..8612b0fa7b5c34f9937b6700ff4e3b8b0f33675f 100644
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,7 @@ GOFMT = gofmt
 
 JSONNET_FILES = lib/kubecfg_test.jsonnet examples/guestbook.jsonnet
 # TODO: Simplify this once ./... ignores ./vendor
-GO_PACKAGES = ./cmd/... ./utils/... ./pkg/...
+GO_PACKAGES = ./cmd/... ./utils/... ./pkg/... ./metadata/...
 
 all: kubecfg
 
diff --git a/metadata/clusterspec.go b/metadata/clusterspec.go
new file mode 100644
index 0000000000000000000000000000000000000000..bbe44dd15bdc584da78dcd062f2cc11441ee948e
--- /dev/null
+++ b/metadata/clusterspec.go
@@ -0,0 +1,43 @@
+package metadata
+
+import (
+	"github.com/spf13/afero"
+)
+
+type clusterSpecFile struct {
+	specPath AbsPath
+}
+
+func (cs *clusterSpecFile) data() ([]byte, error) {
+	return afero.ReadFile(appFS, string(cs.specPath))
+}
+
+func (cs *clusterSpecFile) resource() string {
+	return string(cs.specPath)
+}
+
+type clusterSpecLive struct {
+	apiServerURL string
+}
+
+func (cs *clusterSpecLive) data() ([]byte, error) {
+	// TODO: Implement getting spec from path, k8sVersion, and URL.
+	panic("Not implemented")
+}
+
+func (cs *clusterSpecLive) resource() string {
+	return string(cs.apiServerURL)
+}
+
+type clusterSpecVersion struct {
+	k8sVersion string
+}
+
+func (cs *clusterSpecVersion) data() ([]byte, error) {
+	// TODO: Implement getting spec from path, k8sVersion, and URL.
+	panic("Not implemented")
+}
+
+func (cs *clusterSpecVersion) resource() string {
+	return string(cs.k8sVersion)
+}
diff --git a/metadata/clusterspec_test.go b/metadata/clusterspec_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4d13beda69ff703293c4cf29e4546c30a51aeccf
--- /dev/null
+++ b/metadata/clusterspec_test.go
@@ -0,0 +1,76 @@
+package metadata
+
+import (
+	"path/filepath"
+	"testing"
+)
+
+type parseSuccess struct {
+	input  string
+	target ClusterSpec
+}
+
+var successTests = []parseSuccess{
+	{"version:v1.7.1", &clusterSpecVersion{"v1.7.1"}},
+	{"file:swagger.json", &clusterSpecFile{"swagger.json"}},
+	{"url:file:///some_file", &clusterSpecLive{"file:///some_file"}},
+}
+
+func TestClusterSpecParsingSuccess(t *testing.T) {
+	for _, test := range successTests {
+		parsed, err := ParseClusterSpec(test.input)
+		if err != nil {
+			t.Errorf("Failed to parse spec: %v", err)
+		}
+
+		parsedResource := parsed.resource()
+		targetResource := test.target.resource()
+
+		switch pt := parsed.(type) {
+		case *clusterSpecLive:
+		case *clusterSpecVersion:
+			if parsedResource != targetResource {
+				t.Errorf("Expected version '%v', got '%v'", parsedResource, targetResource)
+			}
+		case *clusterSpecFile:
+			// Techncially we're cheating here by passing a *relative path*
+			// into `newPathSpec` instead of an absolute one. This is to
+			// make it work on multiple machines. We convert it here, after
+			// the fact.
+			absPath, err := filepath.Abs(targetResource)
+			if err != nil {
+				t.Errorf("Failed to convert `file:` spec to an absolute path: %v", err)
+			}
+
+			if parsedResource != absPath {
+				t.Errorf("Expected path '%v', got '%v'", absPath, parsedResource)
+			}
+		default:
+			t.Errorf("Unknown cluster spec type '%v'", pt)
+		}
+	}
+}
+
+type parseFailure struct {
+	input    string
+	errorMsg string
+}
+
+var failureTests = []parseFailure{
+	{"fakeprefix:foo", "Could not parse cluster spec 'fakeprefix:foo'"},
+	{"foo:", "Invalid API specification 'foo:'"},
+	{"version:", "Invalid API specification 'version:'"},
+	{"file:", "Invalid API specification 'file:'"},
+	{"url:", "Invalid API specification 'url:'"},
+}
+
+func TestClusterSpecParsingFailure(t *testing.T) {
+	for _, test := range failureTests {
+		_, err := ParseClusterSpec(test.input)
+		if err == nil {
+			t.Errorf("Cluster spec parse for '%s' should have failed, but succeeded", test.input)
+		} else if msg := err.Error(); msg != test.errorMsg {
+			t.Errorf("Expected cluster spec parse error: '%s', got: '%s'", test.errorMsg, msg)
+		}
+	}
+}
diff --git a/metadata/interface.go b/metadata/interface.go
new file mode 100644
index 0000000000000000000000000000000000000000..5d9ed9e39458da38ff544adde7b4e9c890df77ba
--- /dev/null
+++ b/metadata/interface.go
@@ -0,0 +1,80 @@
+package metadata
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/spf13/afero"
+)
+
+// AbsPath is an advisory type that represents an absolute path. It is advisory
+// in that it is not forced to be absolute, but rather, meant to indicate
+// intent, and make code easier to read.
+type AbsPath string
+
+// Manager abstracts over a ksonnet application's metadata, allowing users to do
+// things like: create and delete environments; search for prototypes; vendor
+// libraries; and other non-core-application tasks.
+type Manager interface {
+	Root() AbsPath
+	//
+	// TODO: Fill in methods as we need them.
+	//
+	// GetPrototype(id string) Protoype
+	// SearchPrototypes(query string) []Protoype
+	// VendorLibrary(uri, version string) error
+	// CreateEnv(name string, spec *ClusterSpec) error
+	// DeleteEnv(name string) error
+	//
+}
+
+// Find will recursively search the current directory and its parents for a
+// `.ksonnet` folder, which marks the application root. Returns error if there
+// is no application root.
+func Find(path AbsPath) (Manager, error) {
+	return findManager(path, afero.NewOsFs())
+}
+
+// Init will retrieve a cluster API specification, generate a
+// capabilities-compliant version of ksonnet-lib, and then generate the
+// directory tree for an application.
+func Init(rootPath AbsPath, spec ClusterSpec) (Manager, error) {
+	return initManager(rootPath, spec, afero.NewOsFs())
+}
+
+// ClusterSpec represents the API supported by some cluster. There are several
+// ways to specify a cluster, including: querying the API server, reading an
+// OpenAPI spec in some file, or consulting the OpenAPI spec released in a
+// specific version of Kubernetes.
+type ClusterSpec interface {
+	data() ([]byte, error)
+	resource() string // For testing parsing logic.
+}
+
+// ParseClusterSpec will parse a cluster spec flag and output a well-formed
+// ClusterSpec object. For example, if the flag is `--version:v1.7.1`, then we
+// will output a ClusterSpec representing the cluster specification associated
+// with the `v1.7.1` build of Kubernetes.
+func ParseClusterSpec(specFlag string) (ClusterSpec, error) {
+	split := strings.SplitN(specFlag, ":", 2)
+	if len(split) == 0 || len(split) == 1 || split[1] == "" {
+		return nil, fmt.Errorf("Invalid API specification '%s'", specFlag)
+	}
+
+	switch split[0] {
+	case "version":
+		return &clusterSpecVersion{k8sVersion: split[1]}, nil
+	case "file":
+		abs, err := filepath.Abs(split[1])
+		if err != nil {
+			return nil, err
+		}
+		absPath := AbsPath(abs)
+		return &clusterSpecFile{specPath: absPath}, nil
+	case "url":
+		return &clusterSpecLive{apiServerURL: split[1]}, nil
+	default:
+		return nil, fmt.Errorf("Could not parse cluster spec '%s'", specFlag)
+	}
+}
diff --git a/metadata/manager.go b/metadata/manager.go
new file mode 100644
index 0000000000000000000000000000000000000000..9898bfed3b3ffbcfec433cbc953632b745270570
--- /dev/null
+++ b/metadata/manager.go
@@ -0,0 +1,138 @@
+package metadata
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"path/filepath"
+
+	"github.com/spf13/afero"
+)
+
+func appendToAbsPath(originalPath AbsPath, toAppend ...string) AbsPath {
+	paths := append([]string{string(originalPath)}, toAppend...)
+	return AbsPath(path.Join(paths...))
+}
+
+const (
+	defaultEnvName = "dev"
+
+	ksonnetDir    = ".ksonnet"
+	libDir        = "lib"
+	componentsDir = "components"
+	vendorDir     = "vendor"
+	schemaDir     = "vendor/schema"
+	vendorLibDir  = "vendor/lib"
+
+	schemaFilename = "swagger.json"
+)
+
+type manager struct {
+	appFS afero.Fs
+
+	rootPath       AbsPath
+	ksonnetPath    AbsPath
+	libPath        AbsPath
+	componentsPath AbsPath
+	vendorDir      AbsPath
+	schemaDir      AbsPath
+	vendorLibDir   AbsPath
+}
+
+func findManager(abs AbsPath, appFS afero.Fs) (*manager, error) {
+	var lastBase string
+	currBase := string(abs)
+
+	for {
+		currPath := path.Join(currBase, ksonnetDir)
+		exists, err := afero.Exists(appFS, currPath)
+		if err != nil {
+			return nil, err
+		}
+		if exists {
+			return newManager(AbsPath(currBase), appFS), nil
+		}
+
+		lastBase = currBase
+		currBase = filepath.Dir(currBase)
+		if lastBase == currBase {
+			return nil, fmt.Errorf("No ksonnet application found")
+		}
+	}
+}
+
+func initManager(rootPath AbsPath, spec ClusterSpec, appFS afero.Fs) (*manager, error) {
+	data, err := spec.data()
+	if err != nil {
+		return nil, err
+	}
+
+	m := newManager(rootPath, appFS)
+
+	if err = m.createAppDirTree(); err != nil {
+		return nil, err
+	}
+
+	if err = m.cacheClusterSpecData(defaultEnvName, data); err != nil {
+		return nil, err
+	}
+
+	return m, nil
+}
+
+func newManager(rootPath AbsPath, appFS afero.Fs) *manager {
+	return &manager{
+		appFS: appFS,
+
+		rootPath:       rootPath,
+		ksonnetPath:    appendToAbsPath(rootPath, ksonnetDir),
+		libPath:        appendToAbsPath(rootPath, libDir),
+		componentsPath: appendToAbsPath(rootPath, componentsDir),
+		vendorDir:      appendToAbsPath(rootPath, vendorDir),
+		schemaDir:      appendToAbsPath(rootPath, schemaDir),
+		vendorLibDir:   appendToAbsPath(rootPath, vendorLibDir),
+	}
+}
+
+func (m *manager) Root() AbsPath {
+	return m.rootPath
+}
+
+func (m *manager) cacheClusterSpecData(name string, specData []byte) error {
+	envPath := string(appendToAbsPath(m.schemaDir, name))
+	err := m.appFS.MkdirAll(envPath, os.ModePerm)
+	if err != nil {
+		return err
+	}
+
+	schemaPath := string(filepath.Join(envPath, schemaFilename))
+	err = afero.WriteFile(m.appFS, schemaPath, specData, os.ModePerm)
+	return err
+}
+
+func (m *manager) createAppDirTree() error {
+	exists, err := afero.DirExists(m.appFS, string(m.rootPath))
+	if err != nil {
+		return fmt.Errorf("Could not check existance of directory '%s':\n%v", m.rootPath, err)
+	} else if exists {
+		return fmt.Errorf("Could not create app; directory '%s' already exists", m.rootPath)
+	}
+
+	paths := []AbsPath{
+		m.rootPath,
+		m.ksonnetPath,
+		m.libPath,
+		m.componentsPath,
+		m.vendorDir,
+		m.schemaDir,
+		m.vendorLibDir,
+	}
+
+	for _, p := range paths {
+		if err := m.appFS.MkdirAll(string(p), os.ModePerm); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/metadata/manager_test.go b/metadata/manager_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..1329bc8b9d5385d6f5f9d8227bba8a96c540304f
--- /dev/null
+++ b/metadata/manager_test.go
@@ -0,0 +1,133 @@
+package metadata
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/spf13/afero"
+)
+
+const (
+	blankSwagger     = "/blankSwagger.json"
+	blankSwaggerData = `{}`
+)
+
+var appFS = afero.NewMemMapFs()
+
+func init() {
+	afero.WriteFile(appFS, blankSwagger, []byte(blankSwaggerData), os.ModePerm)
+}
+
+func TestInitSuccess(t *testing.T) {
+	spec, err := ParseClusterSpec(fmt.Sprintf("file:%s", blankSwagger))
+	if err != nil {
+		t.Fatalf("Failed to parse cluster spec: %v", err)
+	}
+
+	appPath := AbsPath("/fromEmptySwagger")
+	_, err = initManager(appPath, spec, appFS)
+	if err != nil {
+		t.Fatalf("Failed to init cluster spec: %v", err)
+	}
+
+	defaultEnvDir := appendToAbsPath(schemaDir, defaultEnvName)
+	paths := []AbsPath{
+		ksonnetDir,
+		libDir,
+		componentsDir,
+		vendorDir,
+		schemaDir,
+		vendorLibDir,
+		defaultEnvDir,
+	}
+
+	for _, p := range paths {
+		path := appendToAbsPath(appPath, string(p))
+		exists, err := afero.DirExists(appFS, string(path))
+		if err != nil {
+			t.Fatalf("Expected to create directory '%s', but failed:\n%v", p, err)
+		} else if !exists {
+			t.Fatalf("Expected to create directory '%s', but failed", path)
+		}
+	}
+
+	envPath := appendToAbsPath(appPath, string(defaultEnvDir))
+	schemaPath := appendToAbsPath(envPath, schemaFilename)
+	bytes, err := afero.ReadFile(appFS, string(schemaPath))
+	if err != nil {
+		t.Fatalf("Failed to read swagger file at '%s':\n%v", schemaPath, err)
+	} else if actualSwagger := string(bytes); actualSwagger != blankSwaggerData {
+		t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", schemaPath, blankSwaggerData, actualSwagger)
+	}
+}
+
+func TestFindSuccess(t *testing.T) {
+	findSuccess := func(t *testing.T, appDir, currDir AbsPath) {
+		m, err := findManager(currDir, appFS)
+		if err != nil {
+			t.Fatalf("Failed to find manager at path '%s':\n%v", currDir, err)
+		} else if m.rootPath != appDir {
+			t.Fatalf("Found manager at incorrect path '%s', expected '%s'", m.rootPath, appDir)
+		}
+	}
+
+	spec, err := ParseClusterSpec(fmt.Sprintf("file:%s", blankSwagger))
+	if err != nil {
+		t.Fatalf("Failed to parse cluster spec: %v", err)
+	}
+
+	appPath := AbsPath("/findSuccess")
+	_, err = initManager(appPath, spec, appFS)
+	if err != nil {
+		t.Fatalf("Failed to init cluster spec: %v", err)
+	}
+
+	findSuccess(t, appPath, appPath)
+
+	components := appendToAbsPath(appPath, componentsDir)
+	findSuccess(t, appPath, components)
+
+	// Create empty app file.
+	appFile := appendToAbsPath(components, "app.jsonnet")
+	f, err := appFS.OpenFile(string(appFile), os.O_RDONLY|os.O_CREATE, 0777)
+	if err != nil {
+		t.Fatalf("Failed to touch app file '%s'\n%v", appFile, err)
+	}
+	f.Close()
+
+	findSuccess(t, appPath, appFile)
+}
+
+func TestFindFailure(t *testing.T) {
+	findFailure := func(t *testing.T, currDir AbsPath) {
+		_, err := findManager(currDir, appFS)
+		if err == nil {
+			t.Fatalf("Expected to fail to find ksonnet app in '%s', but succeeded", currDir)
+		}
+	}
+
+	findFailure(t, "/")
+	findFailure(t, "/fakePath")
+	findFailure(t, "")
+}
+
+func TestDoubleNewFailure(t *testing.T) {
+	spec, err := ParseClusterSpec(fmt.Sprintf("file:%s", blankSwagger))
+	if err != nil {
+		t.Fatalf("Failed to parse cluster spec: %v", err)
+	}
+
+	appPath := AbsPath("/doubleNew")
+
+	_, err = initManager(appPath, spec, appFS)
+	if err != nil {
+		t.Fatalf("Failed to init cluster spec: %v", err)
+	}
+
+	targetErr := fmt.Sprintf("Could not create app; directory '%s' already exists", appPath)
+	_, err = initManager(appPath, spec, appFS)
+	if err == nil || err.Error() != targetErr {
+		t.Fatalf("Expected to fail to create app with message '%s', got '%s'", targetErr, err.Error())
+	}
+}
diff --git a/vendor/github.com/spf13/afero/LICENSE.txt b/vendor/github.com/spf13/afero/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..298f0e2665e512a7d5053faf2ce4793c281efe6a
--- /dev/null
+++ b/vendor/github.com/spf13/afero/LICENSE.txt
@@ -0,0 +1,174 @@
+                                Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
diff --git a/vendor/github.com/spf13/afero/README.md b/vendor/github.com/spf13/afero/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d9e332730ba798380b4d6ee671cf7a2d47c9afec
--- /dev/null
+++ b/vendor/github.com/spf13/afero/README.md
@@ -0,0 +1,449 @@
+![afero logo-sm](https://cloud.githubusercontent.com/assets/173412/11490338/d50e16dc-97a5-11e5-8b12-019a300d0fcb.png)
+
+A FileSystem Abstraction System for Go
+
+[![Build Status](https://travis-ci.org/spf13/afero.svg)](https://travis-ci.org/spf13/afero) [![Build status](https://ci.appveyor.com/api/projects/status/github/spf13/afero?branch=master&svg=true)](https://ci.appveyor.com/project/spf13/afero) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+# Overview
+
+Afero is an filesystem framework providing a simple, uniform and universal API
+interacting with any filesystem, as an abstraction layer providing interfaces,
+types and methods. Afero has an exceptionally clean interface and simple design
+without needless constructors or initialization methods.
+
+Afero is also a library providing a base set of interoperable backend
+filesystems that make it easy to work with afero while retaining all the power
+and benefit of the os and ioutil packages.
+
+Afero provides significant improvements over using the os package alone, most
+notably the ability to create mock and testing filesystems without relying on the disk.
+
+It is suitable for use in a any situation where you would consider using the OS
+package as it provides an additional abstraction that makes it easy to use a
+memory backed file system during testing. It also adds support for the http
+filesystem for full interoperability.
+
+
+## Afero Features
+
+* A single consistent API for accessing a variety of filesystems
+* Interoperation between a variety of file system types
+* A set of interfaces to encourage and enforce interoperability between backends
+* An atomic cross platform memory backed file system
+* Support for compositional (union) file systems by combining multiple file systems acting as one
+* Specialized backends which modify existing filesystems (Read Only, Regexp filtered)
+* A set of utility functions ported from io, ioutil & hugo to be afero aware
+
+
+# Using Afero
+
+Afero is easy to use and easier to adopt.
+
+A few different ways you could use Afero:
+
+* Use the interfaces alone to define you own file system.
+* Wrap for the OS packages.
+* Define different filesystems for different parts of your application.
+* Use Afero for mock filesystems while testing
+
+## Step 1: Install Afero
+
+First use go get to install the latest version of the library.
+
+    $ go get github.com/spf13/afero
+
+Next include Afero in your application.
+```go
+import "github.com/spf13/afero"
+```
+
+## Step 2: Declare a backend
+
+First define a package variable and set it to a pointer to a filesystem.
+```go
+var AppFs afero.Fs = afero.NewMemMapFs()
+
+or
+
+var AppFs afero.Fs = afero.NewOsFs()
+```
+It is important to note that if you repeat the composite literal you
+will be using a completely new and isolated filesystem. In the case of
+OsFs it will still use the same underlying filesystem but will reduce
+the ability to drop in other filesystems as desired.
+
+## Step 3: Use it like you would the OS package
+
+Throughout your application use any function and method like you normally
+would.
+
+So if my application before had:
+```go
+os.Open('/tmp/foo')
+```
+We would replace it with a call to `AppFs.Open('/tmp/foo')`.
+
+`AppFs` being the variable we defined above.
+
+
+## List of all available functions
+
+File System Methods Available:
+```go
+Chmod(name string, mode os.FileMode) : error
+Chtimes(name string, atime time.Time, mtime time.Time) : error
+Create(name string) : File, error
+Mkdir(name string, perm os.FileMode) : error
+MkdirAll(path string, perm os.FileMode) : error
+Name() : string
+Open(name string) : File, error
+OpenFile(name string, flag int, perm os.FileMode) : File, error
+Remove(name string) : error
+RemoveAll(path string) : error
+Rename(oldname, newname string) : error
+Stat(name string) : os.FileInfo, error
+```
+File Interfaces and Methods Available:
+```go
+io.Closer
+io.Reader
+io.ReaderAt
+io.Seeker
+io.Writer
+io.WriterAt
+
+Name() : string
+Readdir(count int) : []os.FileInfo, error
+Readdirnames(n int) : []string, error
+Stat() : os.FileInfo, error
+Sync() : error
+Truncate(size int64) : error
+WriteString(s string) : ret int, err error
+```
+In some applications it may make sense to define a new package that
+simply exports the file system variable for easy access from anywhere.
+
+## Using Afero's utility functions
+
+Afero provides a set of functions to make it easier to use the underlying file systems.
+These functions have been primarily ported from io & ioutil with some developed for Hugo.
+
+The afero utilities support all afero compatible backends.
+
+The list of utilities includes:
+
+```go
+DirExists(path string) (bool, error)
+Exists(path string) (bool, error)
+FileContainsBytes(filename string, subslice []byte) (bool, error)
+GetTempDir(subPath string) string
+IsDir(path string) (bool, error)
+IsEmpty(path string) (bool, error)
+ReadDir(dirname string) ([]os.FileInfo, error)
+ReadFile(filename string) ([]byte, error)
+SafeWriteReader(path string, r io.Reader) (err error)
+TempDir(dir, prefix string) (name string, err error)
+TempFile(dir, prefix string) (f File, err error)
+Walk(root string, walkFn filepath.WalkFunc) error
+WriteFile(filename string, data []byte, perm os.FileMode) error
+WriteReader(path string, r io.Reader) (err error)
+```
+For a complete list see [Afero's GoDoc](https://godoc.org/github.com/spf13/afero)
+
+They are available under two different approaches to use. You can either call
+them directly where the first parameter of each function will be the file
+system, or you can declare a new `Afero`, a custom type used to bind these
+functions as methods to a given filesystem.
+
+### Calling utilities directly
+
+```go
+fs := new(afero.MemMapFs)
+f, err := afero.TempFile(fs,"", "ioutil-test")
+
+```
+
+### Calling via Afero
+
+```go
+fs := afero.NewMemMapFs
+afs := &Afero{Fs: fs}
+f, err := afs.TempFile("", "ioutil-test")
+```
+
+## Using Afero for Testing
+
+There is a large benefit to using a mock filesystem for testing. It has a
+completely blank state every time it is initialized and can be easily
+reproducible regardless of OS. You could create files to your heart’s content
+and the file access would be fast while also saving you from all the annoying
+issues with deleting temporary files, Windows file locking, etc. The MemMapFs
+backend is perfect for testing.
+
+* Much faster than performing I/O operations on disk
+* Avoid security issues and permissions
+* Far more control. 'rm -rf /' with confidence
+* Test setup is far more easier to do
+* No test cleanup needed
+
+One way to accomplish this is to define a variable as mentioned above.
+In your application this will be set to afero.NewOsFs() during testing you
+can set it to afero.NewMemMapFs().
+
+It wouldn't be uncommon to have each test initialize a blank slate memory
+backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere
+appropriate in my application code. This approach ensures that Tests are order
+independent, with no test relying on the state left by an earlier test.
+
+Then in my tests I would initialize a new MemMapFs for each test:
+```go
+func TestExist(t *testing.T) {
+	appFS := afero.NewMemMapFs()
+	// create test files and directories
+	appFS.MkdirAll("src/a", 0755)
+	afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644)
+	afero.WriteFile(appFS, "src/c", []byte("file c"), 0644)
+	name := "src/c"
+	_, err := appFS.Stat(name)
+	if os.IsNotExist(err) {
+		t.Errorf("file \"%s\" does not exist.\n", name)
+	}
+}
+```
+
+# Available Backends
+
+## Operating System Native
+
+### OsFs
+
+The first is simply a wrapper around the native OS calls. This makes it
+very easy to use as all of the calls are the same as the existing OS
+calls. It also makes it trivial to have your code use the OS during
+operation and a mock filesystem during testing or as needed.
+
+```go
+appfs := afero.NewOsFs()
+appfs.MkdirAll("src/a", 0755))
+```
+
+## Memory Backed Storage
+
+### MemMapFs
+
+Afero also provides a fully atomic memory backed filesystem perfect for use in
+mocking and to speed up unnecessary disk io when persistence isn’t
+necessary. It is fully concurrent and will work within go routines
+safely.
+
+```go
+mm := afero.NewMemMapFs()
+mm.MkdirAll("src/a", 0755))
+```
+
+#### InMemoryFile
+
+As part of MemMapFs, Afero also provides an atomic, fully concurrent memory
+backed file implementation. This can be used in other memory backed file
+systems with ease. Plans are to add a radix tree memory stored file
+system using InMemoryFile.
+
+## Network Interfaces
+
+### SftpFs
+
+Afero has experimental support for secure file transfer protocol (sftp). Which can
+be used to perform file operations over a encrypted channel.
+
+## Filtering Backends
+
+### BasePathFs
+
+The BasePathFs restricts all operations to a given path within an Fs.
+The given file name to the operations on this Fs will be prepended with
+the base path before calling the source Fs.
+
+```go
+bp := afero.NewBasePathFs(afero.NewOsFs(), "/base/path")
+```
+
+### ReadOnlyFs
+
+A thin wrapper around the source Fs providing a read only view.
+
+```go
+fs := afero.NewReadOnlyFs(afero.NewOsFs())
+_, err := fs.Create("/file.txt")
+// err = syscall.EPERM
+```
+
+# RegexpFs
+
+A filtered view on file names, any file NOT matching
+the passed regexp will be treated as non-existing.
+Files not matching the regexp provided will not be created.
+Directories are not filtered.
+
+```go
+fs := afero.NewRegexpFs(afero.NewMemMapFs(), regexp.MustCompile(`\.txt$`))
+_, err := fs.Create("/file.html")
+// err = syscall.ENOENT
+```
+
+### HttpFs
+
+Afero provides an http compatible backend which can wrap any of the existing
+backends.
+
+The Http package requires a slightly specific version of Open which
+returns an http.File type.
+
+Afero provides an httpFs file system which satisfies this requirement.
+Any Afero FileSystem can be used as an httpFs.
+
+```go
+httpFs := afero.NewHttpFs(<ExistingFS>)
+fileserver := http.FileServer(httpFs.Dir(<PATH>)))
+http.Handle("/", fileserver)
+```
+
+## Composite Backends
+
+Afero provides the ability have two filesystems (or more) act as a single
+file system.
+
+### CacheOnReadFs
+
+The CacheOnReadFs will lazily make copies of any accessed files from the base
+layer into the overlay. Subsequent reads will be pulled from the overlay
+directly permitting the request is within the cache duration of when it was
+created in the overlay.
+
+If the base filesystem is writeable, any changes to files will be
+done first to the base, then to the overlay layer. Write calls to open file
+handles like `Write()` or `Truncate()` to the overlay first.
+
+To writing files to the overlay only, you can use the overlay Fs directly (not
+via the union Fs).
+
+Cache files in the layer for the given time.Duration, a cache duration of 0
+means "forever" meaning the file will not be re-requested from the base ever.
+
+A read-only base will make the overlay also read-only but still copy files
+from the base to the overlay when they're not present (or outdated) in the
+caching layer.
+
+```go
+base := afero.NewOsFs()
+layer := afero.NewMemMapFs()
+ufs := afero.NewCacheOnReadFs(base, layer, 100 * time.Second)
+```
+
+### CopyOnWriteFs()
+
+The CopyOnWriteFs is a read only base file system with a potentially
+writeable layer on top.
+
+Read operations will first look in the overlay and if not found there, will
+serve the file from the base.
+
+Changes to the file system will only be made in the overlay.
+
+Any attempt to modify a file found only in the base will copy the file to the
+overlay layer before modification (including opening a file with a writable
+handle).
+
+Removing and Renaming files present only in the base layer is not currently
+permitted. If a file is present in the base layer and the overlay, only the
+overlay will be removed/renamed.
+
+```go
+	base := afero.NewOsFs()
+	roBase := afero.NewReadOnlyFs(base)
+	ufs := afero.NewCopyOnWriteFs(roBase, afero.NewMemMapFs())
+
+	fh, _ = ufs.Create("/home/test/file2.txt")
+	fh.WriteString("This is a test")
+	fh.Close()
+```
+
+In this example all write operations will only occur in memory (MemMapFs)
+leaving the base filesystem (OsFs) untouched.
+
+
+## Desired/possible backends
+
+The following is a short list of possible backends we hope someone will
+implement:
+
+* SSH
+* ZIP
+* TAR
+* S3
+
+# About the project
+
+## What's in the name
+
+Afero comes from the latin roots Ad-Facere.
+
+**"Ad"** is a prefix meaning "to".
+
+**"Facere"** is a form of the root "faciō" making "make or do".
+
+The literal meaning of afero is "to make" or "to do" which seems very fitting
+for a library that allows one to make files and directories and do things with them.
+
+The English word that shares the same roots as Afero is "affair". Affair shares
+the same concept but as a noun it means "something that is made or done" or "an
+object of a particular type".
+
+It's also nice that unlike some of my other libraries (hugo, cobra, viper) it
+Googles very well.
+
+## Release Notes
+
+* **0.10.0** 2015.12.10
+  * Full compatibility with Windows
+  * Introduction of afero utilities
+  * Test suite rewritten to work cross platform
+  * Normalize paths for MemMapFs
+  * Adding Sync to the file interface
+  * **Breaking Change** Walk and ReadDir have changed parameter order
+  * Moving types used by MemMapFs to a subpackage
+  * General bugfixes and improvements
+* **0.9.0** 2015.11.05
+  * New Walk function similar to filepath.Walk
+  * MemMapFs.OpenFile handles O_CREATE, O_APPEND, O_TRUNC
+  * MemMapFs.Remove now really deletes the file
+  * InMemoryFile.Readdir and Readdirnames work correctly
+  * InMemoryFile functions lock it for concurrent access
+  * Test suite improvements
+* **0.8.0** 2014.10.28
+  * First public version
+  * Interfaces feel ready for people to build using
+  * Interfaces satisfy all known uses
+  * MemMapFs passes the majority of the OS test suite
+  * OsFs passes the majority of the OS test suite
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Add some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
+
+## Contributors
+
+Names in no particular order:
+
+* [spf13](https://github.com/spf13)
+* [jaqx0r](https://github.com/jaqx0r)
+* [mbertschler](https://github.com/mbertschler)
+* [xor-gate](https://github.com/xor-gate)
+
+## License
+
+Afero is released under the Apache 2.0 license. See
+[LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt)
diff --git a/vendor/github.com/spf13/afero/afero.go b/vendor/github.com/spf13/afero/afero.go
new file mode 100644
index 0000000000000000000000000000000000000000..f5b5e127cd6a793196506d007454eb8b244c247e
--- /dev/null
+++ b/vendor/github.com/spf13/afero/afero.go
@@ -0,0 +1,108 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+// Copyright 2013 tsuru authors. All rights reserved.
+//
+// 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 afero provides types and methods for interacting with the filesystem,
+// as an abstraction layer.
+
+// Afero also provides a few implementations that are mostly interoperable. One that
+// uses the operating system filesystem, one that uses memory to store files
+// (cross platform) and an interface that should be implemented if you want to
+// provide your own filesystem.
+
+package afero
+
+import (
+	"errors"
+	"io"
+	"os"
+	"time"
+)
+
+type Afero struct {
+	Fs
+}
+
+// File represents a file in the filesystem.
+type File interface {
+	io.Closer
+	io.Reader
+	io.ReaderAt
+	io.Seeker
+	io.Writer
+	io.WriterAt
+
+	Name() string
+	Readdir(count int) ([]os.FileInfo, error)
+	Readdirnames(n int) ([]string, error)
+	Stat() (os.FileInfo, error)
+	Sync() error
+	Truncate(size int64) error
+	WriteString(s string) (ret int, err error)
+}
+
+// Fs is the filesystem interface.
+//
+// Any simulated or real filesystem should implement this interface.
+type Fs interface {
+	// Create creates a file in the filesystem, returning the file and an
+	// error, if any happens.
+	Create(name string) (File, error)
+
+	// Mkdir creates a directory in the filesystem, return an error if any
+	// happens.
+	Mkdir(name string, perm os.FileMode) error
+
+	// MkdirAll creates a directory path and all parents that does not exist
+	// yet.
+	MkdirAll(path string, perm os.FileMode) error
+
+	// Open opens a file, returning it or an error, if any happens.
+	Open(name string) (File, error)
+
+	// OpenFile opens a file using the given flags and the given mode.
+	OpenFile(name string, flag int, perm os.FileMode) (File, error)
+
+	// Remove removes a file identified by name, returning an error, if any
+	// happens.
+	Remove(name string) error
+
+	// RemoveAll removes a directory path and any children it contains. It
+	// does not fail if the path does not exist (return nil).
+	RemoveAll(path string) error
+
+	// Rename renames a file.
+	Rename(oldname, newname string) error
+
+	// Stat returns a FileInfo describing the named file, or an error, if any
+	// happens.
+	Stat(name string) (os.FileInfo, error)
+
+	// The name of this FileSystem
+	Name() string
+
+	//Chmod changes the mode of the named file to mode.
+	Chmod(name string, mode os.FileMode) error
+
+	//Chtimes changes the access and modification times of the named file
+	Chtimes(name string, atime time.Time, mtime time.Time) error
+}
+
+var (
+	ErrFileClosed        = errors.New("File is closed")
+	ErrOutOfRange        = errors.New("Out of range")
+	ErrTooLarge          = errors.New("Too large")
+	ErrFileNotFound      = os.ErrNotExist
+	ErrFileExists        = os.ErrExist
+	ErrDestinationExists = os.ErrExist
+)
diff --git a/vendor/github.com/spf13/afero/appveyor.yml b/vendor/github.com/spf13/afero/appveyor.yml
new file mode 100644
index 0000000000000000000000000000000000000000..006f315349210eb528280f8d33a7e089a992bc86
--- /dev/null
+++ b/vendor/github.com/spf13/afero/appveyor.yml
@@ -0,0 +1,15 @@
+version: '{build}'
+clone_folder: C:\gopath\src\github.com\spf13\afero
+environment:
+  GOPATH: C:\gopath
+build_script:
+- cmd: >-
+    go version
+
+    go env
+
+    go get -v github.com/spf13/afero/...
+
+    go build github.com/spf13/afero
+test_script:
+- cmd: go test -v github.com/spf13/afero
diff --git a/vendor/github.com/spf13/afero/basepath.go b/vendor/github.com/spf13/afero/basepath.go
new file mode 100644
index 0000000000000000000000000000000000000000..5e4fc2ec055e5fde456ee5ccb41c04d7861e2a1c
--- /dev/null
+++ b/vendor/github.com/spf13/afero/basepath.go
@@ -0,0 +1,145 @@
+package afero
+
+import (
+	"errors"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"time"
+)
+
+// The BasePathFs restricts all operations to a given path within an Fs.
+// The given file name to the operations on this Fs will be prepended with
+// the base path before calling the base Fs.
+// Any file name (after filepath.Clean()) outside this base path will be
+// treated as non existing file.
+//
+// Note that it does not clean the error messages on return, so you may
+// reveal the real path on errors.
+type BasePathFs struct {
+	source Fs
+	path   string
+}
+
+func NewBasePathFs(source Fs, path string) Fs {
+	return &BasePathFs{source: source, path: path}
+}
+
+// on a file outside the base path it returns the given file name and an error,
+// else the given file with the base path prepended
+func (b *BasePathFs) RealPath(name string) (path string, err error) {
+	if err := validateBasePathName(name); err != nil {
+		return "", err
+	}
+
+	bpath := filepath.Clean(b.path)
+	path = filepath.Clean(filepath.Join(bpath, name))
+	if !strings.HasPrefix(path, bpath) {
+		return name, os.ErrNotExist
+	}
+
+	return path, nil
+}
+
+func validateBasePathName(name string) error {
+	if runtime.GOOS != "windows" {
+		// Not much to do here;
+		// the virtual file paths all look absolute on *nix.
+		return nil
+	}
+
+	// On Windows a common mistake would be to provide an absolute OS path
+	// We could strip out the base part, but that would not be very portable.
+	if filepath.IsAbs(name) {
+		return &os.PathError{Op: "realPath", Path: name, Err: errors.New("got a real OS path instead of a virtual")}
+	}
+
+	return nil
+}
+
+func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return &os.PathError{Op: "chtimes", Path: name, Err: err}
+	}
+	return b.source.Chtimes(name, atime, mtime)
+}
+
+func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return &os.PathError{Op: "chmod", Path: name, Err: err}
+	}
+	return b.source.Chmod(name, mode)
+}
+
+func (b *BasePathFs) Name() string {
+	return "BasePathFs"
+}
+
+func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return nil, &os.PathError{Op: "stat", Path: name, Err: err}
+	}
+	return b.source.Stat(name)
+}
+
+func (b *BasePathFs) Rename(oldname, newname string) (err error) {
+	if oldname, err = b.RealPath(oldname); err != nil {
+		return &os.PathError{Op: "rename", Path: oldname, Err: err}
+	}
+	if newname, err = b.RealPath(newname); err != nil {
+		return &os.PathError{Op: "rename", Path: newname, Err: err}
+	}
+	return b.source.Rename(oldname, newname)
+}
+
+func (b *BasePathFs) RemoveAll(name string) (err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return &os.PathError{Op: "remove_all", Path: name, Err: err}
+	}
+	return b.source.RemoveAll(name)
+}
+
+func (b *BasePathFs) Remove(name string) (err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return &os.PathError{Op: "remove", Path: name, Err: err}
+	}
+	return b.source.Remove(name)
+}
+
+func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
+	}
+	return b.source.OpenFile(name, flag, mode)
+}
+
+func (b *BasePathFs) Open(name string) (f File, err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return nil, &os.PathError{Op: "open", Path: name, Err: err}
+	}
+	return b.source.Open(name)
+}
+
+func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return &os.PathError{Op: "mkdir", Path: name, Err: err}
+	}
+	return b.source.Mkdir(name, mode)
+}
+
+func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return &os.PathError{Op: "mkdir", Path: name, Err: err}
+	}
+	return b.source.MkdirAll(name, mode)
+}
+
+func (b *BasePathFs) Create(name string) (f File, err error) {
+	if name, err = b.RealPath(name); err != nil {
+		return nil, &os.PathError{Op: "create", Path: name, Err: err}
+	}
+	return b.source.Create(name)
+}
+
+// vim: ts=4 sw=4 noexpandtab nolist syn=go
diff --git a/vendor/github.com/spf13/afero/cacheOnReadFs.go b/vendor/github.com/spf13/afero/cacheOnReadFs.go
new file mode 100644
index 0000000000000000000000000000000000000000..e54a4f8b42eb2c3a674be5b9e026c03c7b9108e3
--- /dev/null
+++ b/vendor/github.com/spf13/afero/cacheOnReadFs.go
@@ -0,0 +1,295 @@
+package afero
+
+import (
+	"os"
+	"syscall"
+	"time"
+)
+
+// If the cache duration is 0, cache time will be unlimited, i.e. once
+// a file is in the layer, the base will never be read again for this file.
+//
+// For cache times greater than 0, the modification time of a file is
+// checked. Note that a lot of file system implementations only allow a
+// resolution of a second for timestamps... or as the godoc for os.Chtimes()
+// states: "The underlying filesystem may truncate or round the values to a
+// less precise time unit."
+//
+// This caching union will forward all write calls also to the base file
+// system first. To prevent writing to the base Fs, wrap it in a read-only
+// filter - Note: this will also make the overlay read-only, for writing files
+// in the overlay, use the overlay Fs directly, not via the union Fs.
+type CacheOnReadFs struct {
+	base      Fs
+	layer     Fs
+	cacheTime time.Duration
+}
+
+func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
+	return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
+}
+
+type cacheState int
+
+const (
+	// not present in the overlay, unknown if it exists in the base:
+	cacheMiss cacheState = iota
+	// present in the overlay and in base, base file is newer:
+	cacheStale
+	// present in the overlay - with cache time == 0 it may exist in the base,
+	// with cacheTime > 0 it exists in the base and is same age or newer in the
+	// overlay
+	cacheHit
+	// happens if someone writes directly to the overlay without
+	// going through this union
+	cacheLocal
+)
+
+func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {
+	var lfi, bfi os.FileInfo
+	lfi, err = u.layer.Stat(name)
+	if err == nil {
+		if u.cacheTime == 0 {
+			return cacheHit, lfi, nil
+		}
+		if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {
+			bfi, err = u.base.Stat(name)
+			if err != nil {
+				return cacheLocal, lfi, nil
+			}
+			if bfi.ModTime().After(lfi.ModTime()) {
+				return cacheStale, bfi, nil
+			}
+		}
+		return cacheHit, lfi, nil
+	}
+
+	if err == syscall.ENOENT {
+		return cacheMiss, nil, nil
+	}
+	var ok bool
+	if err, ok = err.(*os.PathError); ok {
+		if err == os.ErrNotExist {
+			return cacheMiss, nil, nil
+		}
+	}
+	return cacheMiss, nil, err
+}
+
+func (u *CacheOnReadFs) copyToLayer(name string) error {
+	return copyToLayer(u.base, u.layer, name)
+}
+
+func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
+	st, _, err := u.cacheStatus(name)
+	if err != nil {
+		return err
+	}
+	switch st {
+	case cacheLocal:
+	case cacheHit:
+		err = u.base.Chtimes(name, atime, mtime)
+	case cacheStale, cacheMiss:
+		if err := u.copyToLayer(name); err != nil {
+			return err
+		}
+		err = u.base.Chtimes(name, atime, mtime)
+	}
+	if err != nil {
+		return err
+	}
+	return u.layer.Chtimes(name, atime, mtime)
+}
+
+func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
+	st, _, err := u.cacheStatus(name)
+	if err != nil {
+		return err
+	}
+	switch st {
+	case cacheLocal:
+	case cacheHit:
+		err = u.base.Chmod(name, mode)
+	case cacheStale, cacheMiss:
+		if err := u.copyToLayer(name); err != nil {
+			return err
+		}
+		err = u.base.Chmod(name, mode)
+	}
+	if err != nil {
+		return err
+	}
+	return u.layer.Chmod(name, mode)
+}
+
+func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
+	st, fi, err := u.cacheStatus(name)
+	if err != nil {
+		return nil, err
+	}
+	switch st {
+	case cacheMiss:
+		return u.base.Stat(name)
+	default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
+		return fi, nil
+	}
+}
+
+func (u *CacheOnReadFs) Rename(oldname, newname string) error {
+	st, _, err := u.cacheStatus(oldname)
+	if err != nil {
+		return err
+	}
+	switch st {
+	case cacheLocal:
+	case cacheHit:
+		err = u.base.Rename(oldname, newname)
+	case cacheStale, cacheMiss:
+		if err := u.copyToLayer(oldname); err != nil {
+			return err
+		}
+		err = u.base.Rename(oldname, newname)
+	}
+	if err != nil {
+		return err
+	}
+	return u.layer.Rename(oldname, newname)
+}
+
+func (u *CacheOnReadFs) Remove(name string) error {
+	st, _, err := u.cacheStatus(name)
+	if err != nil {
+		return err
+	}
+	switch st {
+	case cacheLocal:
+	case cacheHit, cacheStale, cacheMiss:
+		err = u.base.Remove(name)
+	}
+	if err != nil {
+		return err
+	}
+	return u.layer.Remove(name)
+}
+
+func (u *CacheOnReadFs) RemoveAll(name string) error {
+	st, _, err := u.cacheStatus(name)
+	if err != nil {
+		return err
+	}
+	switch st {
+	case cacheLocal:
+	case cacheHit, cacheStale, cacheMiss:
+		err = u.base.RemoveAll(name)
+	}
+	if err != nil {
+		return err
+	}
+	return u.layer.RemoveAll(name)
+}
+
+func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	st, _, err := u.cacheStatus(name)
+	if err != nil {
+		return nil, err
+	}
+	switch st {
+	case cacheLocal, cacheHit:
+	default:
+		if err := u.copyToLayer(name); err != nil {
+			return nil, err
+		}
+	}
+	if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
+		bfi, err := u.base.OpenFile(name, flag, perm)
+		if err != nil {
+			return nil, err
+		}
+		lfi, err := u.layer.OpenFile(name, flag, perm)
+		if err != nil {
+			bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
+			return nil, err
+		}
+		return &UnionFile{base: bfi, layer: lfi}, nil
+	}
+	return u.layer.OpenFile(name, flag, perm)
+}
+
+func (u *CacheOnReadFs) Open(name string) (File, error) {
+	st, fi, err := u.cacheStatus(name)
+	if err != nil {
+		return nil, err
+	}
+
+	switch st {
+	case cacheLocal:
+		return u.layer.Open(name)
+
+	case cacheMiss:
+		bfi, err := u.base.Stat(name)
+		if err != nil {
+			return nil, err
+		}
+		if bfi.IsDir() {
+			return u.base.Open(name)
+		}
+		if err := u.copyToLayer(name); err != nil {
+			return nil, err
+		}
+		return u.layer.Open(name)
+
+	case cacheStale:
+		if !fi.IsDir() {
+			if err := u.copyToLayer(name); err != nil {
+				return nil, err
+			}
+			return u.layer.Open(name)
+		}
+	case cacheHit:
+		if !fi.IsDir() {
+			return u.layer.Open(name)
+		}
+	}
+	// the dirs from cacheHit, cacheStale fall down here:
+	bfile, _ := u.base.Open(name)
+	lfile, err := u.layer.Open(name)
+	if err != nil && bfile == nil {
+		return nil, err
+	}
+	return &UnionFile{base: bfile, layer: lfile}, nil
+}
+
+func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
+	err := u.base.Mkdir(name, perm)
+	if err != nil {
+		return err
+	}
+	return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
+}
+
+func (u *CacheOnReadFs) Name() string {
+	return "CacheOnReadFs"
+}
+
+func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
+	err := u.base.MkdirAll(name, perm)
+	if err != nil {
+		return err
+	}
+	return u.layer.MkdirAll(name, perm)
+}
+
+func (u *CacheOnReadFs) Create(name string) (File, error) {
+	bfh, err := u.base.Create(name)
+	if err != nil {
+		return nil, err
+	}
+	lfh, err := u.layer.Create(name)
+	if err != nil {
+		// oops, see comment about OS_TRUNC above, should we remove? then we have to
+		// remember if the file did not exist before
+		bfh.Close()
+		return nil, err
+	}
+	return &UnionFile{base: bfh, layer: lfh}, nil
+}
diff --git a/vendor/github.com/spf13/afero/const_bsds.go b/vendor/github.com/spf13/afero/const_bsds.go
new file mode 100644
index 0000000000000000000000000000000000000000..5728243d962ddc68f58aaba3c21d016f3444c8a4
--- /dev/null
+++ b/vendor/github.com/spf13/afero/const_bsds.go
@@ -0,0 +1,22 @@
+// Copyright © 2016 Steve Francia <spf@spf13.com>.
+//
+// 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.
+
+// +build darwin openbsd freebsd netbsd dragonfly
+
+package afero
+
+import (
+	"syscall"
+)
+
+const BADFD = syscall.EBADF
diff --git a/vendor/github.com/spf13/afero/const_win_unix.go b/vendor/github.com/spf13/afero/const_win_unix.go
new file mode 100644
index 0000000000000000000000000000000000000000..968fc2783e5e89de05143d7c213b964f2665d97d
--- /dev/null
+++ b/vendor/github.com/spf13/afero/const_win_unix.go
@@ -0,0 +1,25 @@
+// Copyright © 2016 Steve Francia <spf@spf13.com>.
+//
+// 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.
+// +build !darwin
+// +build !openbsd
+// +build !freebsd
+// +build !dragonfly
+// +build !netbsd
+
+package afero
+
+import (
+	"syscall"
+)
+
+const BADFD = syscall.EBADFD
diff --git a/vendor/github.com/spf13/afero/copyOnWriteFs.go b/vendor/github.com/spf13/afero/copyOnWriteFs.go
new file mode 100644
index 0000000000000000000000000000000000000000..ed692ae95c0fe67fa83904b0ee971d501f201601
--- /dev/null
+++ b/vendor/github.com/spf13/afero/copyOnWriteFs.go
@@ -0,0 +1,253 @@
+package afero
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"syscall"
+	"time"
+)
+
+// The CopyOnWriteFs is a union filesystem: a read only base file system with
+// a possibly writeable layer on top. Changes to the file system will only
+// be made in the overlay: Changing an existing file in the base layer which
+// is not present in the overlay will copy the file to the overlay ("changing"
+// includes also calls to e.g. Chtimes() and Chmod()).
+//
+// Reading directories is currently only supported via Open(), not OpenFile().
+type CopyOnWriteFs struct {
+	base  Fs
+	layer Fs
+}
+
+func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
+	return &CopyOnWriteFs{base: base, layer: layer}
+}
+
+// Returns true if the file is not in the overlay
+func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
+	if _, err := u.layer.Stat(name); err == nil {
+		return false, nil
+	}
+	_, err := u.base.Stat(name)
+	if err != nil {
+		if oerr, ok := err.(*os.PathError); ok {
+			if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
+				return false, nil
+			}
+		}
+		if err == syscall.ENOENT {
+			return false, nil
+		}
+	}
+	return true, err
+}
+
+func (u *CopyOnWriteFs) copyToLayer(name string) error {
+	return copyToLayer(u.base, u.layer, name)
+}
+
+func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
+	b, err := u.isBaseFile(name)
+	if err != nil {
+		return err
+	}
+	if b {
+		if err := u.copyToLayer(name); err != nil {
+			return err
+		}
+	}
+	return u.layer.Chtimes(name, atime, mtime)
+}
+
+func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
+	b, err := u.isBaseFile(name)
+	if err != nil {
+		return err
+	}
+	if b {
+		if err := u.copyToLayer(name); err != nil {
+			return err
+		}
+	}
+	return u.layer.Chmod(name, mode)
+}
+
+func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
+	fi, err := u.layer.Stat(name)
+	if err != nil {
+		origErr := err
+		if e, ok := err.(*os.PathError); ok {
+			err = e.Err
+		}
+		if err == syscall.ENOENT || err == syscall.ENOTDIR {
+			return u.base.Stat(name)
+		}
+		return nil, origErr
+	}
+	return fi, nil
+}
+
+// Renaming files present only in the base layer is not permitted
+func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
+	b, err := u.isBaseFile(oldname)
+	if err != nil {
+		return err
+	}
+	if b {
+		return syscall.EPERM
+	}
+	return u.layer.Rename(oldname, newname)
+}
+
+// Removing files present only in the base layer is not permitted. If
+// a file is present in the base layer and the overlay, only the overlay
+// will be removed.
+func (u *CopyOnWriteFs) Remove(name string) error {
+	err := u.layer.Remove(name)
+	switch err {
+	case syscall.ENOENT:
+		_, err = u.base.Stat(name)
+		if err == nil {
+			return syscall.EPERM
+		}
+		return syscall.ENOENT
+	default:
+		return err
+	}
+}
+
+func (u *CopyOnWriteFs) RemoveAll(name string) error {
+	err := u.layer.RemoveAll(name)
+	switch err {
+	case syscall.ENOENT:
+		_, err = u.base.Stat(name)
+		if err == nil {
+			return syscall.EPERM
+		}
+		return syscall.ENOENT
+	default:
+		return err
+	}
+}
+
+func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	b, err := u.isBaseFile(name)
+	if err != nil {
+		return nil, err
+	}
+
+	if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
+		if b {
+			if err = u.copyToLayer(name); err != nil {
+				return nil, err
+			}
+			return u.layer.OpenFile(name, flag, perm)
+		}
+
+		dir := filepath.Dir(name)
+		isaDir, err := IsDir(u.base, dir)
+		if err != nil && !os.IsNotExist(err) {
+			return nil, err
+		}
+		if isaDir {
+			if err = u.layer.MkdirAll(dir, 0777); err != nil {
+				return nil, err
+			}
+			return u.layer.OpenFile(name, flag, perm)
+		}
+
+		isaDir, err = IsDir(u.layer, dir)
+		if err != nil {
+			return nil, err
+		}
+		if isaDir {
+			return u.layer.OpenFile(name, flag, perm)
+		}
+
+		return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
+	}
+	if b {
+		return u.base.OpenFile(name, flag, perm)
+	}
+	return u.layer.OpenFile(name, flag, perm)
+}
+
+// This function handles the 9 different possibilities caused
+// by the union which are the intersection of the following...
+//  layer: doesn't exist, exists as a file, and exists as a directory
+//  base:  doesn't exist, exists as a file, and exists as a directory
+func (u *CopyOnWriteFs) Open(name string) (File, error) {
+	// Since the overlay overrides the base we check that first
+	b, err := u.isBaseFile(name)
+	if err != nil {
+		return nil, err
+	}
+
+	// If overlay doesn't exist, return the base (base state irrelevant)
+	if b {
+		return u.base.Open(name)
+	}
+
+	// If overlay is a file, return it (base state irrelevant)
+	dir, err := IsDir(u.layer, name)
+	if err != nil {
+		return nil, err
+	}
+	if !dir {
+		return u.layer.Open(name)
+	}
+
+	// Overlay is a directory, base state now matters.
+	// Base state has 3 states to check but 2 outcomes:
+	// A. It's a file or non-readable in the base (return just the overlay)
+	// B. It's an accessible directory in the base (return a UnionFile)
+
+	// If base is file or nonreadable, return overlay
+	dir, err = IsDir(u.base, name)
+	if !dir || err != nil {
+		return u.layer.Open(name)
+	}
+
+	// Both base & layer are directories
+	// Return union file (if opens are without error)
+	bfile, bErr := u.base.Open(name)
+	lfile, lErr := u.layer.Open(name)
+
+	// If either have errors at this point something is very wrong. Return nil and the errors
+	if bErr != nil || lErr != nil {
+		return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
+	}
+
+	return &UnionFile{base: bfile, layer: lfile}, nil
+}
+
+func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
+	dir, err := IsDir(u.base, name)
+	if err != nil {
+		return u.layer.MkdirAll(name, perm)
+	}
+	if dir {
+		return syscall.EEXIST
+	}
+	return u.layer.MkdirAll(name, perm)
+}
+
+func (u *CopyOnWriteFs) Name() string {
+	return "CopyOnWriteFs"
+}
+
+func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
+	dir, err := IsDir(u.base, name)
+	if err != nil {
+		return u.layer.MkdirAll(name, perm)
+	}
+	if dir {
+		return syscall.EEXIST
+	}
+	return u.layer.MkdirAll(name, perm)
+}
+
+func (u *CopyOnWriteFs) Create(name string) (File, error) {
+	return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
+}
diff --git a/vendor/github.com/spf13/afero/httpFs.go b/vendor/github.com/spf13/afero/httpFs.go
new file mode 100644
index 0000000000000000000000000000000000000000..c42193688ceb626d75d6a4f97b4cfa6d30ec2e00
--- /dev/null
+++ b/vendor/github.com/spf13/afero/httpFs.go
@@ -0,0 +1,110 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// 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 afero
+
+import (
+	"errors"
+	"net/http"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+type httpDir struct {
+	basePath string
+	fs       HttpFs
+}
+
+func (d httpDir) Open(name string) (http.File, error) {
+	if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
+		strings.Contains(name, "\x00") {
+		return nil, errors.New("http: invalid character in file path")
+	}
+	dir := string(d.basePath)
+	if dir == "" {
+		dir = "."
+	}
+
+	f, err := d.fs.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
+}
+
+type HttpFs struct {
+	source Fs
+}
+
+func NewHttpFs(source Fs) *HttpFs {
+	return &HttpFs{source: source}
+}
+
+func (h HttpFs) Dir(s string) *httpDir {
+	return &httpDir{basePath: s, fs: h}
+}
+
+func (h HttpFs) Name() string { return "h HttpFs" }
+
+func (h HttpFs) Create(name string) (File, error) {
+	return h.source.Create(name)
+}
+
+func (h HttpFs) Chmod(name string, mode os.FileMode) error {
+	return h.source.Chmod(name, mode)
+}
+
+func (h HttpFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+	return h.source.Chtimes(name, atime, mtime)
+}
+
+func (h HttpFs) Mkdir(name string, perm os.FileMode) error {
+	return h.source.Mkdir(name, perm)
+}
+
+func (h HttpFs) MkdirAll(path string, perm os.FileMode) error {
+	return h.source.MkdirAll(path, perm)
+}
+
+func (h HttpFs) Open(name string) (http.File, error) {
+	f, err := h.source.Open(name)
+	if err == nil {
+		if httpfile, ok := f.(http.File); ok {
+			return httpfile, nil
+		}
+	}
+	return nil, err
+}
+
+func (h HttpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	return h.source.OpenFile(name, flag, perm)
+}
+
+func (h HttpFs) Remove(name string) error {
+	return h.source.Remove(name)
+}
+
+func (h HttpFs) RemoveAll(path string) error {
+	return h.source.RemoveAll(path)
+}
+
+func (h HttpFs) Rename(oldname, newname string) error {
+	return h.source.Rename(oldname, newname)
+}
+
+func (h HttpFs) Stat(name string) (os.FileInfo, error) {
+	return h.source.Stat(name)
+}
diff --git a/vendor/github.com/spf13/afero/ioutil.go b/vendor/github.com/spf13/afero/ioutil.go
new file mode 100644
index 0000000000000000000000000000000000000000..5c3a3d8fffc98b973a7a89ee5728de12a4e91ed9
--- /dev/null
+++ b/vendor/github.com/spf13/afero/ioutil.go
@@ -0,0 +1,230 @@
+// Copyright ©2015 The Go Authors
+// Copyright ©2015 Steve Francia <spf@spf13.com>
+//
+// 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 afero
+
+import (
+	"bytes"
+	"io"
+	"os"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"sync"
+	"time"
+)
+
+// byName implements sort.Interface.
+type byName []os.FileInfo
+
+func (f byName) Len() int           { return len(f) }
+func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
+func (f byName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
+
+// ReadDir reads the directory named by dirname and returns
+// a list of sorted directory entries.
+func (a Afero) ReadDir(dirname string) ([]os.FileInfo, error) {
+	return ReadDir(a.Fs, dirname)
+}
+
+func ReadDir(fs Fs, dirname string) ([]os.FileInfo, error) {
+	f, err := fs.Open(dirname)
+	if err != nil {
+		return nil, err
+	}
+	list, err := f.Readdir(-1)
+	f.Close()
+	if err != nil {
+		return nil, err
+	}
+	sort.Sort(byName(list))
+	return list, nil
+}
+
+// ReadFile reads the file named by filename and returns the contents.
+// A successful call returns err == nil, not err == EOF. Because ReadFile
+// reads the whole file, it does not treat an EOF from Read as an error
+// to be reported.
+func (a Afero) ReadFile(filename string) ([]byte, error) {
+	return ReadFile(a.Fs, filename)
+}
+
+func ReadFile(fs Fs, filename string) ([]byte, error) {
+	f, err := fs.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	// It's a good but not certain bet that FileInfo will tell us exactly how much to
+	// read, so let's try it but be prepared for the answer to be wrong.
+	var n int64
+
+	if fi, err := f.Stat(); err == nil {
+		// Don't preallocate a huge buffer, just in case.
+		if size := fi.Size(); size < 1e9 {
+			n = size
+		}
+	}
+	// As initial capacity for readAll, use n + a little extra in case Size is zero,
+	// and to avoid another allocation after Read has filled the buffer.  The readAll
+	// call will read into its allocated internal buffer cheaply.  If the size was
+	// wrong, we'll either waste some space off the end or reallocate as needed, but
+	// in the overwhelmingly common case we'll get it just right.
+	return readAll(f, n+bytes.MinRead)
+}
+
+// readAll reads from r until an error or EOF and returns the data it read
+// from the internal buffer allocated with a specified capacity.
+func readAll(r io.Reader, capacity int64) (b []byte, err error) {
+	buf := bytes.NewBuffer(make([]byte, 0, capacity))
+	// If the buffer overflows, we will get bytes.ErrTooLarge.
+	// Return that as an error. Any other panic remains.
+	defer func() {
+		e := recover()
+		if e == nil {
+			return
+		}
+		if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
+			err = panicErr
+		} else {
+			panic(e)
+		}
+	}()
+	_, err = buf.ReadFrom(r)
+	return buf.Bytes(), err
+}
+
+// ReadAll reads from r until an error or EOF and returns the data it read.
+// A successful call returns err == nil, not err == EOF. Because ReadAll is
+// defined to read from src until EOF, it does not treat an EOF from Read
+// as an error to be reported.
+func ReadAll(r io.Reader) ([]byte, error) {
+	return readAll(r, bytes.MinRead)
+}
+
+// WriteFile writes data to a file named by filename.
+// If the file does not exist, WriteFile creates it with permissions perm;
+// otherwise WriteFile truncates it before writing.
+func (a Afero) WriteFile(filename string, data []byte, perm os.FileMode) error {
+	return WriteFile(a.Fs, filename, data, perm)
+}
+
+func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error {
+	f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
+	if err != nil {
+		return err
+	}
+	n, err := f.Write(data)
+	if err == nil && n < len(data) {
+		err = io.ErrShortWrite
+	}
+	if err1 := f.Close(); err == nil {
+		err = err1
+	}
+	return err
+}
+
+// Random number state.
+// We generate random temporary file names so that there's a good
+// chance the file doesn't exist yet - keeps the number of tries in
+// TempFile to a minimum.
+var rand uint32
+var randmu sync.Mutex
+
+func reseed() uint32 {
+	return uint32(time.Now().UnixNano() + int64(os.Getpid()))
+}
+
+func nextSuffix() string {
+	randmu.Lock()
+	r := rand
+	if r == 0 {
+		r = reseed()
+	}
+	r = r*1664525 + 1013904223 // constants from Numerical Recipes
+	rand = r
+	randmu.Unlock()
+	return strconv.Itoa(int(1e9 + r%1e9))[1:]
+}
+
+// TempFile creates a new temporary file in the directory dir
+// with a name beginning with prefix, opens the file for reading
+// and writing, and returns the resulting *File.
+// If dir is the empty string, TempFile uses the default directory
+// for temporary files (see os.TempDir).
+// Multiple programs calling TempFile simultaneously
+// will not choose the same file.  The caller can use f.Name()
+// to find the pathname of the file.  It is the caller's responsibility
+// to remove the file when no longer needed.
+func (a Afero) TempFile(dir, prefix string) (f File, err error) {
+	return TempFile(a.Fs, dir, prefix)
+}
+
+func TempFile(fs Fs, dir, prefix string) (f File, err error) {
+	if dir == "" {
+		dir = os.TempDir()
+	}
+
+	nconflict := 0
+	for i := 0; i < 10000; i++ {
+		name := filepath.Join(dir, prefix+nextSuffix())
+		f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
+		if os.IsExist(err) {
+			if nconflict++; nconflict > 10 {
+				randmu.Lock()
+				rand = reseed()
+				randmu.Unlock()
+			}
+			continue
+		}
+		break
+	}
+	return
+}
+
+// TempDir creates a new temporary directory in the directory dir
+// with a name beginning with prefix and returns the path of the
+// new directory.  If dir is the empty string, TempDir uses the
+// default directory for temporary files (see os.TempDir).
+// Multiple programs calling TempDir simultaneously
+// will not choose the same directory.  It is the caller's responsibility
+// to remove the directory when no longer needed.
+func (a Afero) TempDir(dir, prefix string) (name string, err error) {
+	return TempDir(a.Fs, dir, prefix)
+}
+func TempDir(fs Fs, dir, prefix string) (name string, err error) {
+	if dir == "" {
+		dir = os.TempDir()
+	}
+
+	nconflict := 0
+	for i := 0; i < 10000; i++ {
+		try := filepath.Join(dir, prefix+nextSuffix())
+		err = fs.Mkdir(try, 0700)
+		if os.IsExist(err) {
+			if nconflict++; nconflict > 10 {
+				randmu.Lock()
+				rand = reseed()
+				randmu.Unlock()
+			}
+			continue
+		}
+		if err == nil {
+			name = try
+		}
+		break
+	}
+	return
+}
diff --git a/vendor/github.com/spf13/afero/mem/dir.go b/vendor/github.com/spf13/afero/mem/dir.go
new file mode 100644
index 0000000000000000000000000000000000000000..e104013f45712024294836f2e8e90a333303cdec
--- /dev/null
+++ b/vendor/github.com/spf13/afero/mem/dir.go
@@ -0,0 +1,37 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// 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 mem
+
+type Dir interface {
+	Len() int
+	Names() []string
+	Files() []*FileData
+	Add(*FileData)
+	Remove(*FileData)
+}
+
+func RemoveFromMemDir(dir *FileData, f *FileData) {
+	dir.memDir.Remove(f)
+}
+
+func AddToMemDir(dir *FileData, f *FileData) {
+	dir.memDir.Add(f)
+}
+
+func InitializeDir(d *FileData) {
+	if d.memDir == nil {
+		d.dir = true
+		d.memDir = &DirMap{}
+	}
+}
diff --git a/vendor/github.com/spf13/afero/mem/dirmap.go b/vendor/github.com/spf13/afero/mem/dirmap.go
new file mode 100644
index 0000000000000000000000000000000000000000..03a57ee5b52e8d28663c2eb4dddc44841b604df6
--- /dev/null
+++ b/vendor/github.com/spf13/afero/mem/dirmap.go
@@ -0,0 +1,43 @@
+// Copyright © 2015 Steve Francia <spf@spf13.com>.
+//
+// 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 mem
+
+import "sort"
+
+type DirMap map[string]*FileData
+
+func (m DirMap) Len() int           { return len(m) }
+func (m DirMap) Add(f *FileData)    { m[f.name] = f }
+func (m DirMap) Remove(f *FileData) { delete(m, f.name) }
+func (m DirMap) Files() (files []*FileData) {
+	for _, f := range m {
+		files = append(files, f)
+	}
+	sort.Sort(filesSorter(files))
+	return files
+}
+
+// implement sort.Interface for []*FileData
+type filesSorter []*FileData
+
+func (s filesSorter) Len() int           { return len(s) }
+func (s filesSorter) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s filesSorter) Less(i, j int) bool { return s[i].name < s[j].name }
+
+func (m DirMap) Names() (names []string) {
+	for x := range m {
+		names = append(names, x)
+	}
+	return names
+}
diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go
new file mode 100644
index 0000000000000000000000000000000000000000..e41e0123d6d8a89a67387ef7f043c99dba7a5874
--- /dev/null
+++ b/vendor/github.com/spf13/afero/mem/file.go
@@ -0,0 +1,285 @@
+// Copyright © 2015 Steve Francia <spf@spf13.com>.
+// Copyright 2013 tsuru authors. All rights reserved.
+//
+// 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 mem
+
+import (
+	"bytes"
+	"errors"
+	"io"
+	"os"
+	"path/filepath"
+	"sync"
+	"sync/atomic"
+)
+
+import "time"
+
+const FilePathSeparator = string(filepath.Separator)
+
+type File struct {
+	// atomic requires 64-bit alignment for struct field access
+	at           int64
+	readDirCount int64
+	closed       bool
+	readOnly     bool
+	fileData     *FileData
+}
+
+func NewFileHandle(data *FileData) *File {
+	return &File{fileData: data}
+}
+
+func NewReadOnlyFileHandle(data *FileData) *File {
+	return &File{fileData: data, readOnly: true}
+}
+
+func (f File) Data() *FileData {
+	return f.fileData
+}
+
+type FileData struct {
+	sync.Mutex
+	name    string
+	data    []byte
+	memDir  Dir
+	dir     bool
+	mode    os.FileMode
+	modtime time.Time
+}
+
+func (d *FileData) Name() string {
+	d.Lock()
+	defer d.Unlock()
+	return d.name
+}
+
+func CreateFile(name string) *FileData {
+	return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()}
+}
+
+func CreateDir(name string) *FileData {
+	return &FileData{name: name, memDir: &DirMap{}, dir: true}
+}
+
+func ChangeFileName(f *FileData, newname string) {
+	f.name = newname
+}
+
+func SetMode(f *FileData, mode os.FileMode) {
+	f.mode = mode
+}
+
+func SetModTime(f *FileData, mtime time.Time) {
+	f.modtime = mtime
+}
+
+func GetFileInfo(f *FileData) *FileInfo {
+	return &FileInfo{f}
+}
+
+func (f *File) Open() error {
+	atomic.StoreInt64(&f.at, 0)
+	atomic.StoreInt64(&f.readDirCount, 0)
+	f.fileData.Lock()
+	f.closed = false
+	f.fileData.Unlock()
+	return nil
+}
+
+func (f *File) Close() error {
+	f.fileData.Lock()
+	f.closed = true
+	if !f.readOnly {
+		SetModTime(f.fileData, time.Now())
+	}
+	f.fileData.Unlock()
+	return nil
+}
+
+func (f *File) Name() string {
+	return f.fileData.Name()
+}
+
+func (f *File) Stat() (os.FileInfo, error) {
+	return &FileInfo{f.fileData}, nil
+}
+
+func (f *File) Sync() error {
+	return nil
+}
+
+func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
+	var outLength int64
+
+	f.fileData.Lock()
+	files := f.fileData.memDir.Files()[f.readDirCount:]
+	if count > 0 {
+		if len(files) < count {
+			outLength = int64(len(files))
+		} else {
+			outLength = int64(count)
+		}
+		if len(files) == 0 {
+			err = io.EOF
+		}
+	} else {
+		outLength = int64(len(files))
+	}
+	f.readDirCount += outLength
+	f.fileData.Unlock()
+
+	res = make([]os.FileInfo, outLength)
+	for i := range res {
+		res[i] = &FileInfo{files[i]}
+	}
+
+	return res, err
+}
+
+func (f *File) Readdirnames(n int) (names []string, err error) {
+	fi, err := f.Readdir(n)
+	names = make([]string, len(fi))
+	for i, f := range fi {
+		_, names[i] = filepath.Split(f.Name())
+	}
+	return names, err
+}
+
+func (f *File) Read(b []byte) (n int, err error) {
+	f.fileData.Lock()
+	defer f.fileData.Unlock()
+	if f.closed == true {
+		return 0, ErrFileClosed
+	}
+	if len(b) > 0 && int(f.at) == len(f.fileData.data) {
+		return 0, io.EOF
+	}
+	if len(f.fileData.data)-int(f.at) >= len(b) {
+		n = len(b)
+	} else {
+		n = len(f.fileData.data) - int(f.at)
+	}
+	copy(b, f.fileData.data[f.at:f.at+int64(n)])
+	atomic.AddInt64(&f.at, int64(n))
+	return
+}
+
+func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
+	atomic.StoreInt64(&f.at, off)
+	return f.Read(b)
+}
+
+func (f *File) Truncate(size int64) error {
+	if f.closed == true {
+		return ErrFileClosed
+	}
+	if f.readOnly {
+		return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
+	}
+	if size < 0 {
+		return ErrOutOfRange
+	}
+	if size > int64(len(f.fileData.data)) {
+		diff := size - int64(len(f.fileData.data))
+		f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...)
+	} else {
+		f.fileData.data = f.fileData.data[0:size]
+	}
+	SetModTime(f.fileData, time.Now())
+	return nil
+}
+
+func (f *File) Seek(offset int64, whence int) (int64, error) {
+	if f.closed == true {
+		return 0, ErrFileClosed
+	}
+	switch whence {
+	case 0:
+		atomic.StoreInt64(&f.at, offset)
+	case 1:
+		atomic.AddInt64(&f.at, int64(offset))
+	case 2:
+		atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset)
+	}
+	return f.at, nil
+}
+
+func (f *File) Write(b []byte) (n int, err error) {
+	if f.readOnly {
+		return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")}
+	}
+	n = len(b)
+	cur := atomic.LoadInt64(&f.at)
+	f.fileData.Lock()
+	defer f.fileData.Unlock()
+	diff := cur - int64(len(f.fileData.data))
+	var tail []byte
+	if n+int(cur) < len(f.fileData.data) {
+		tail = f.fileData.data[n+int(cur):]
+	}
+	if diff > 0 {
+		f.fileData.data = append(bytes.Repeat([]byte{00}, int(diff)), b...)
+		f.fileData.data = append(f.fileData.data, tail...)
+	} else {
+		f.fileData.data = append(f.fileData.data[:cur], b...)
+		f.fileData.data = append(f.fileData.data, tail...)
+	}
+	SetModTime(f.fileData, time.Now())
+
+	atomic.StoreInt64(&f.at, int64(len(f.fileData.data)))
+	return
+}
+
+func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
+	atomic.StoreInt64(&f.at, off)
+	return f.Write(b)
+}
+
+func (f *File) WriteString(s string) (ret int, err error) {
+	return f.Write([]byte(s))
+}
+
+func (f *File) Info() *FileInfo {
+	return &FileInfo{f.fileData}
+}
+
+type FileInfo struct {
+	*FileData
+}
+
+// Implements os.FileInfo
+func (s *FileInfo) Name() string {
+	_, name := filepath.Split(s.name)
+	return name
+}
+func (s *FileInfo) Mode() os.FileMode  { return s.mode }
+func (s *FileInfo) ModTime() time.Time { return s.modtime }
+func (s *FileInfo) IsDir() bool        { return s.dir }
+func (s *FileInfo) Sys() interface{}   { return nil }
+func (s *FileInfo) Size() int64 {
+	if s.IsDir() {
+		return int64(42)
+	}
+	return int64(len(s.data))
+}
+
+var (
+	ErrFileClosed        = errors.New("File is closed")
+	ErrOutOfRange        = errors.New("Out of range")
+	ErrTooLarge          = errors.New("Too large")
+	ErrFileNotFound      = os.ErrNotExist
+	ErrFileExists        = os.ErrExist
+	ErrDestinationExists = os.ErrExist
+)
diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go
new file mode 100644
index 0000000000000000000000000000000000000000..767ac1d5f66ea762eedf6a1810e7aedc7452cf07
--- /dev/null
+++ b/vendor/github.com/spf13/afero/memmap.go
@@ -0,0 +1,361 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// 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 afero
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/spf13/afero/mem"
+)
+
+type MemMapFs struct {
+	mu   sync.RWMutex
+	data map[string]*mem.FileData
+	init sync.Once
+}
+
+func NewMemMapFs() Fs {
+	return &MemMapFs{}
+}
+
+func (m *MemMapFs) getData() map[string]*mem.FileData {
+	m.init.Do(func() {
+		m.data = make(map[string]*mem.FileData)
+		// Root should always exist, right?
+		// TODO: what about windows?
+		m.data[FilePathSeparator] = mem.CreateDir(FilePathSeparator)
+	})
+	return m.data
+}
+
+func (*MemMapFs) Name() string { return "MemMapFS" }
+
+func (m *MemMapFs) Create(name string) (File, error) {
+	name = normalizePath(name)
+	m.mu.Lock()
+	file := mem.CreateFile(name)
+	m.getData()[name] = file
+	m.registerWithParent(file)
+	m.mu.Unlock()
+	return mem.NewFileHandle(file), nil
+}
+
+func (m *MemMapFs) unRegisterWithParent(fileName string) error {
+	f, err := m.lockfreeOpen(fileName)
+	if err != nil {
+		return err
+	}
+	parent := m.findParent(f)
+	if parent == nil {
+		log.Panic("parent of ", f.Name(), " is nil")
+	}
+	mem.RemoveFromMemDir(parent, f)
+	return nil
+}
+
+func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
+	pdir, _ := filepath.Split(f.Name())
+	pdir = filepath.Clean(pdir)
+	pfile, err := m.lockfreeOpen(pdir)
+	if err != nil {
+		return nil
+	}
+	return pfile
+}
+
+func (m *MemMapFs) registerWithParent(f *mem.FileData) {
+	if f == nil {
+		return
+	}
+	parent := m.findParent(f)
+	if parent == nil {
+		pdir := filepath.Dir(filepath.Clean(f.Name()))
+		err := m.lockfreeMkdir(pdir, 0777)
+		if err != nil {
+			//log.Println("Mkdir error:", err)
+			return
+		}
+		parent, err = m.lockfreeOpen(pdir)
+		if err != nil {
+			//log.Println("Open after Mkdir error:", err)
+			return
+		}
+	}
+
+	mem.InitializeDir(parent)
+	mem.AddToMemDir(parent, f)
+}
+
+func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
+	name = normalizePath(name)
+	x, ok := m.getData()[name]
+	if ok {
+		// Only return ErrFileExists if it's a file, not a directory.
+		i := mem.FileInfo{FileData: x}
+		if !i.IsDir() {
+			return ErrFileExists
+		}
+	} else {
+		item := mem.CreateDir(name)
+		m.getData()[name] = item
+		m.registerWithParent(item)
+	}
+	return nil
+}
+
+func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	_, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if ok {
+		return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}
+	}
+
+	m.mu.Lock()
+	item := mem.CreateDir(name)
+	m.getData()[name] = item
+	m.registerWithParent(item)
+	m.mu.Unlock()
+
+	m.Chmod(name, perm)
+
+	return nil
+}
+
+func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error {
+	err := m.Mkdir(path, perm)
+	if err != nil {
+		if err.(*os.PathError).Err == ErrFileExists {
+			return nil
+		} else {
+			return err
+		}
+	}
+	return nil
+}
+
+// Handle some relative paths
+func normalizePath(path string) string {
+	path = filepath.Clean(path)
+
+	switch path {
+	case ".":
+		return FilePathSeparator
+	case "..":
+		return FilePathSeparator
+	default:
+		return path
+	}
+}
+
+func (m *MemMapFs) Open(name string) (File, error) {
+	f, err := m.open(name)
+	if f != nil {
+		return mem.NewReadOnlyFileHandle(f), err
+	}
+	return nil, err
+}
+
+func (m *MemMapFs) openWrite(name string) (File, error) {
+	f, err := m.open(name)
+	if f != nil {
+		return mem.NewFileHandle(f), err
+	}
+	return nil, err
+}
+
+func (m *MemMapFs) open(name string) (*mem.FileData, error) {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	f, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if !ok {
+		return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound}
+	}
+	return f, nil
+}
+
+func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {
+	name = normalizePath(name)
+	f, ok := m.getData()[name]
+	if ok {
+		return f, nil
+	} else {
+		return nil, ErrFileNotFound
+	}
+}
+
+func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	chmod := false
+	file, err := m.openWrite(name)
+	if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {
+		file, err = m.Create(name)
+		chmod = true
+	}
+	if err != nil {
+		return nil, err
+	}
+	if flag == os.O_RDONLY {
+		file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data())
+	}
+	if flag&os.O_APPEND > 0 {
+		_, err = file.Seek(0, os.SEEK_END)
+		if err != nil {
+			file.Close()
+			return nil, err
+		}
+	}
+	if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 {
+		err = file.Truncate(0)
+		if err != nil {
+			file.Close()
+			return nil, err
+		}
+	}
+	if chmod {
+		m.Chmod(name, perm)
+	}
+	return file, nil
+}
+
+func (m *MemMapFs) Remove(name string) error {
+	name = normalizePath(name)
+
+	m.mu.Lock()
+	defer m.mu.Unlock()
+
+	if _, ok := m.getData()[name]; ok {
+		err := m.unRegisterWithParent(name)
+		if err != nil {
+			return &os.PathError{Op: "remove", Path: name, Err: err}
+		}
+		delete(m.getData(), name)
+	} else {
+		return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
+	}
+	return nil
+}
+
+func (m *MemMapFs) RemoveAll(path string) error {
+	path = normalizePath(path)
+	m.mu.Lock()
+	m.unRegisterWithParent(path)
+	m.mu.Unlock()
+
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+
+	for p, _ := range m.getData() {
+		if strings.HasPrefix(p, path) {
+			m.mu.RUnlock()
+			m.mu.Lock()
+			delete(m.getData(), p)
+			m.mu.Unlock()
+			m.mu.RLock()
+		}
+	}
+	return nil
+}
+
+func (m *MemMapFs) Rename(oldname, newname string) error {
+	oldname = normalizePath(oldname)
+	newname = normalizePath(newname)
+
+	if oldname == newname {
+		return nil
+	}
+
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	if _, ok := m.getData()[oldname]; ok {
+		m.mu.RUnlock()
+		m.mu.Lock()
+		m.unRegisterWithParent(oldname)
+		fileData := m.getData()[oldname]
+		delete(m.getData(), oldname)
+		mem.ChangeFileName(fileData, newname)
+		m.getData()[newname] = fileData
+		m.registerWithParent(fileData)
+		m.mu.Unlock()
+		m.mu.RLock()
+	} else {
+		return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
+	}
+	return nil
+}
+
+func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {
+	f, err := m.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	fi := mem.GetFileInfo(f.(*mem.File).Data())
+	return fi, nil
+}
+
+func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	f, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if !ok {
+		return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}
+	}
+
+	m.mu.Lock()
+	mem.SetMode(f, mode)
+	m.mu.Unlock()
+
+	return nil
+}
+
+func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+	name = normalizePath(name)
+
+	m.mu.RLock()
+	f, ok := m.getData()[name]
+	m.mu.RUnlock()
+	if !ok {
+		return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound}
+	}
+
+	m.mu.Lock()
+	mem.SetModTime(f, mtime)
+	m.mu.Unlock()
+
+	return nil
+}
+
+func (m *MemMapFs) List() {
+	for _, x := range m.data {
+		y := mem.FileInfo{FileData: x}
+		fmt.Println(x.Name(), y.Size())
+	}
+}
+
+// func debugMemMapList(fs Fs) {
+// 	if x, ok := fs.(*MemMapFs); ok {
+// 		x.List()
+// 	}
+// }
diff --git a/vendor/github.com/spf13/afero/memradix.go b/vendor/github.com/spf13/afero/memradix.go
new file mode 100644
index 0000000000000000000000000000000000000000..87527f35ad728dcaee028e627fdf9dbdfe19e22a
--- /dev/null
+++ b/vendor/github.com/spf13/afero/memradix.go
@@ -0,0 +1,14 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// 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 afero
diff --git a/vendor/github.com/spf13/afero/os.go b/vendor/github.com/spf13/afero/os.go
new file mode 100644
index 0000000000000000000000000000000000000000..6b8bce1c502c95f4434b727acc5950008f0329da
--- /dev/null
+++ b/vendor/github.com/spf13/afero/os.go
@@ -0,0 +1,94 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+// Copyright 2013 tsuru authors. All rights reserved.
+//
+// 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 afero
+
+import (
+	"os"
+	"time"
+)
+
+// OsFs is a Fs implementation that uses functions provided by the os package.
+//
+// For details in any method, check the documentation of the os package
+// (http://golang.org/pkg/os/).
+type OsFs struct{}
+
+func NewOsFs() Fs {
+	return &OsFs{}
+}
+
+func (OsFs) Name() string { return "OsFs" }
+
+func (OsFs) Create(name string) (File, error) {
+	f, e := os.Create(name)
+	if f == nil {
+		// while this looks strange, we need to return a bare nil (of type nil) not
+		// a nil value of type *os.File or nil won't be nil
+		return nil, e
+	}
+	return f, e
+}
+
+func (OsFs) Mkdir(name string, perm os.FileMode) error {
+	return os.Mkdir(name, perm)
+}
+
+func (OsFs) MkdirAll(path string, perm os.FileMode) error {
+	return os.MkdirAll(path, perm)
+}
+
+func (OsFs) Open(name string) (File, error) {
+	f, e := os.Open(name)
+	if f == nil {
+		// while this looks strange, we need to return a bare nil (of type nil) not
+		// a nil value of type *os.File or nil won't be nil
+		return nil, e
+	}
+	return f, e
+}
+
+func (OsFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	f, e := os.OpenFile(name, flag, perm)
+	if f == nil {
+		// while this looks strange, we need to return a bare nil (of type nil) not
+		// a nil value of type *os.File or nil won't be nil
+		return nil, e
+	}
+	return f, e
+}
+
+func (OsFs) Remove(name string) error {
+	return os.Remove(name)
+}
+
+func (OsFs) RemoveAll(path string) error {
+	return os.RemoveAll(path)
+}
+
+func (OsFs) Rename(oldname, newname string) error {
+	return os.Rename(oldname, newname)
+}
+
+func (OsFs) Stat(name string) (os.FileInfo, error) {
+	return os.Stat(name)
+}
+
+func (OsFs) Chmod(name string, mode os.FileMode) error {
+	return os.Chmod(name, mode)
+}
+
+func (OsFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+	return os.Chtimes(name, atime, mtime)
+}
diff --git a/vendor/github.com/spf13/afero/path.go b/vendor/github.com/spf13/afero/path.go
new file mode 100644
index 0000000000000000000000000000000000000000..1d90e46dd0fc4a34283712b8ae7398380d1bd589
--- /dev/null
+++ b/vendor/github.com/spf13/afero/path.go
@@ -0,0 +1,108 @@
+// Copyright ©2015 The Go Authors
+// Copyright ©2015 Steve Francia <spf@spf13.com>
+//
+// 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 afero
+
+import (
+	"os"
+	"path/filepath"
+	"sort"
+)
+
+// readDirNames reads the directory named by dirname and returns
+// a sorted list of directory entries.
+// adapted from https://golang.org/src/path/filepath/path.go
+func readDirNames(fs Fs, dirname string) ([]string, error) {
+	f, err := fs.Open(dirname)
+	if err != nil {
+		return nil, err
+	}
+	names, err := f.Readdirnames(-1)
+	f.Close()
+	if err != nil {
+		return nil, err
+	}
+	sort.Strings(names)
+	return names, nil
+}
+
+// walk recursively descends path, calling walkFn
+// adapted from https://golang.org/src/path/filepath/path.go
+func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
+	err := walkFn(path, info, nil)
+	if err != nil {
+		if info.IsDir() && err == filepath.SkipDir {
+			return nil
+		}
+		return err
+	}
+
+	if !info.IsDir() {
+		return nil
+	}
+
+	names, err := readDirNames(fs, path)
+	if err != nil {
+		return walkFn(path, info, err)
+	}
+
+	for _, name := range names {
+		filename := filepath.Join(path, name)
+		fileInfo, err := lstatIfOs(fs, filename)
+		if err != nil {
+			if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
+				return err
+			}
+		} else {
+			err = walk(fs, filename, fileInfo, walkFn)
+			if err != nil {
+				if !fileInfo.IsDir() || err != filepath.SkipDir {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+// if the filesystem is OsFs use Lstat, else use fs.Stat
+func lstatIfOs(fs Fs, path string) (info os.FileInfo, err error) {
+	_, ok := fs.(*OsFs)
+	if ok {
+		info, err = os.Lstat(path)
+	} else {
+		info, err = fs.Stat(path)
+	}
+	return
+}
+
+// Walk walks the file tree rooted at root, calling walkFn for each file or
+// directory in the tree, including root. All errors that arise visiting files
+// and directories are filtered by walkFn. The files are walked in lexical
+// order, which makes the output deterministic but means that for very
+// large directories Walk can be inefficient.
+// Walk does not follow symbolic links.
+
+func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error {
+	return Walk(a.Fs, root, walkFn)
+}
+
+func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error {
+	info, err := lstatIfOs(fs, root)
+	if err != nil {
+		return walkFn(root, nil, err)
+	}
+	return walk(fs, root, info, walkFn)
+}
diff --git a/vendor/github.com/spf13/afero/readonlyfs.go b/vendor/github.com/spf13/afero/readonlyfs.go
new file mode 100644
index 0000000000000000000000000000000000000000..f1fa55bcf4e8c77e5093741e35c74a621e4de884
--- /dev/null
+++ b/vendor/github.com/spf13/afero/readonlyfs.go
@@ -0,0 +1,70 @@
+package afero
+
+import (
+	"os"
+	"syscall"
+	"time"
+)
+
+type ReadOnlyFs struct {
+	source Fs
+}
+
+func NewReadOnlyFs(source Fs) Fs {
+	return &ReadOnlyFs{source: source}
+}
+
+func (r *ReadOnlyFs) ReadDir(name string) ([]os.FileInfo, error) {
+	return ReadDir(r.source, name)
+}
+
+func (r *ReadOnlyFs) Chtimes(n string, a, m time.Time) error {
+	return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Chmod(n string, m os.FileMode) error {
+	return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Name() string {
+	return "ReadOnlyFilter"
+}
+
+func (r *ReadOnlyFs) Stat(name string) (os.FileInfo, error) {
+	return r.source.Stat(name)
+}
+
+func (r *ReadOnlyFs) Rename(o, n string) error {
+	return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) RemoveAll(p string) error {
+	return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Remove(n string) error {
+	return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
+		return nil, syscall.EPERM
+	}
+	return r.source.OpenFile(name, flag, perm)
+}
+
+func (r *ReadOnlyFs) Open(n string) (File, error) {
+	return r.source.Open(n)
+}
+
+func (r *ReadOnlyFs) Mkdir(n string, p os.FileMode) error {
+	return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) MkdirAll(n string, p os.FileMode) error {
+	return syscall.EPERM
+}
+
+func (r *ReadOnlyFs) Create(n string) (File, error) {
+	return nil, syscall.EPERM
+}
diff --git a/vendor/github.com/spf13/afero/regexpfs.go b/vendor/github.com/spf13/afero/regexpfs.go
new file mode 100644
index 0000000000000000000000000000000000000000..9d92dbc051ff5591e312ffd09914f914590f8687
--- /dev/null
+++ b/vendor/github.com/spf13/afero/regexpfs.go
@@ -0,0 +1,214 @@
+package afero
+
+import (
+	"os"
+	"regexp"
+	"syscall"
+	"time"
+)
+
+// The RegexpFs filters files (not directories) by regular expression. Only
+// files matching the given regexp will be allowed, all others get a ENOENT error (
+// "No such file or directory").
+//
+type RegexpFs struct {
+	re     *regexp.Regexp
+	source Fs
+}
+
+func NewRegexpFs(source Fs, re *regexp.Regexp) Fs {
+	return &RegexpFs{source: source, re: re}
+}
+
+type RegexpFile struct {
+	f  File
+	re *regexp.Regexp
+}
+
+func (r *RegexpFs) matchesName(name string) error {
+	if r.re == nil {
+		return nil
+	}
+	if r.re.MatchString(name) {
+		return nil
+	}
+	return syscall.ENOENT
+}
+
+func (r *RegexpFs) dirOrMatches(name string) error {
+	dir, err := IsDir(r.source, name)
+	if err != nil {
+		return err
+	}
+	if dir {
+		return nil
+	}
+	return r.matchesName(name)
+}
+
+func (r *RegexpFs) Chtimes(name string, a, m time.Time) error {
+	if err := r.dirOrMatches(name); err != nil {
+		return err
+	}
+	return r.source.Chtimes(name, a, m)
+}
+
+func (r *RegexpFs) Chmod(name string, mode os.FileMode) error {
+	if err := r.dirOrMatches(name); err != nil {
+		return err
+	}
+	return r.source.Chmod(name, mode)
+}
+
+func (r *RegexpFs) Name() string {
+	return "RegexpFs"
+}
+
+func (r *RegexpFs) Stat(name string) (os.FileInfo, error) {
+	if err := r.dirOrMatches(name); err != nil {
+		return nil, err
+	}
+	return r.source.Stat(name)
+}
+
+func (r *RegexpFs) Rename(oldname, newname string) error {
+	dir, err := IsDir(r.source, oldname)
+	if err != nil {
+		return err
+	}
+	if dir {
+		return nil
+	}
+	if err := r.matchesName(oldname); err != nil {
+		return err
+	}
+	if err := r.matchesName(newname); err != nil {
+		return err
+	}
+	return r.source.Rename(oldname, newname)
+}
+
+func (r *RegexpFs) RemoveAll(p string) error {
+	dir, err := IsDir(r.source, p)
+	if err != nil {
+		return err
+	}
+	if !dir {
+		if err := r.matchesName(p); err != nil {
+			return err
+		}
+	}
+	return r.source.RemoveAll(p)
+}
+
+func (r *RegexpFs) Remove(name string) error {
+	if err := r.dirOrMatches(name); err != nil {
+		return err
+	}
+	return r.source.Remove(name)
+}
+
+func (r *RegexpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+	if err := r.dirOrMatches(name); err != nil {
+		return nil, err
+	}
+	return r.source.OpenFile(name, flag, perm)
+}
+
+func (r *RegexpFs) Open(name string) (File, error) {
+	dir, err := IsDir(r.source, name)
+	if err != nil {
+		return nil, err
+	}
+	if !dir {
+		if err := r.matchesName(name); err != nil {
+			return nil, err
+		}
+	}
+	f, err := r.source.Open(name)
+	return &RegexpFile{f: f, re: r.re}, nil
+}
+
+func (r *RegexpFs) Mkdir(n string, p os.FileMode) error {
+	return r.source.Mkdir(n, p)
+}
+
+func (r *RegexpFs) MkdirAll(n string, p os.FileMode) error {
+	return r.source.MkdirAll(n, p)
+}
+
+func (r *RegexpFs) Create(name string) (File, error) {
+	if err := r.matchesName(name); err != nil {
+		return nil, err
+	}
+	return r.source.Create(name)
+}
+
+func (f *RegexpFile) Close() error {
+	return f.f.Close()
+}
+
+func (f *RegexpFile) Read(s []byte) (int, error) {
+	return f.f.Read(s)
+}
+
+func (f *RegexpFile) ReadAt(s []byte, o int64) (int, error) {
+	return f.f.ReadAt(s, o)
+}
+
+func (f *RegexpFile) Seek(o int64, w int) (int64, error) {
+	return f.f.Seek(o, w)
+}
+
+func (f *RegexpFile) Write(s []byte) (int, error) {
+	return f.f.Write(s)
+}
+
+func (f *RegexpFile) WriteAt(s []byte, o int64) (int, error) {
+	return f.f.WriteAt(s, o)
+}
+
+func (f *RegexpFile) Name() string {
+	return f.f.Name()
+}
+
+func (f *RegexpFile) Readdir(c int) (fi []os.FileInfo, err error) {
+	var rfi []os.FileInfo
+	rfi, err = f.f.Readdir(c)
+	if err != nil {
+		return nil, err
+	}
+	for _, i := range rfi {
+		if i.IsDir() || f.re.MatchString(i.Name()) {
+			fi = append(fi, i)
+		}
+	}
+	return fi, nil
+}
+
+func (f *RegexpFile) Readdirnames(c int) (n []string, err error) {
+	fi, err := f.Readdir(c)
+	if err != nil {
+		return nil, err
+	}
+	for _, s := range fi {
+		n = append(n, s.Name())
+	}
+	return n, nil
+}
+
+func (f *RegexpFile) Stat() (os.FileInfo, error) {
+	return f.f.Stat()
+}
+
+func (f *RegexpFile) Sync() error {
+	return f.f.Sync()
+}
+
+func (f *RegexpFile) Truncate(s int64) error {
+	return f.f.Truncate(s)
+}
+
+func (f *RegexpFile) WriteString(s string) (int, error) {
+	return f.f.WriteString(s)
+}
diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go
new file mode 100644
index 0000000000000000000000000000000000000000..99f9e5db27ee6483c72f351d3996271927c82ceb
--- /dev/null
+++ b/vendor/github.com/spf13/afero/unionFile.go
@@ -0,0 +1,274 @@
+package afero
+
+import (
+	"io"
+	"os"
+	"path/filepath"
+	"syscall"
+)
+
+// The UnionFile implements the afero.File interface and will be returned
+// when reading a directory present at least in the overlay or opening a file
+// for writing.
+//
+// The calls to
+// Readdir() and Readdirnames() merge the file os.FileInfo / names from the
+// base and the overlay - for files present in both layers, only those
+// from the overlay will be used.
+//
+// When opening files for writing (Create() / OpenFile() with the right flags)
+// the operations will be done in both layers, starting with the overlay. A
+// successful read in the overlay will move the cursor position in the base layer
+// by the number of bytes read.
+type UnionFile struct {
+	base  File
+	layer File
+	off   int
+	files []os.FileInfo
+}
+
+func (f *UnionFile) Close() error {
+	// first close base, so we have a newer timestamp in the overlay. If we'd close
+	// the overlay first, we'd get a cacheStale the next time we access this file
+	// -> cache would be useless ;-)
+	if f.base != nil {
+		f.base.Close()
+	}
+	if f.layer != nil {
+		return f.layer.Close()
+	}
+	return BADFD
+}
+
+func (f *UnionFile) Read(s []byte) (int, error) {
+	if f.layer != nil {
+		n, err := f.layer.Read(s)
+		if (err == nil || err == io.EOF) && f.base != nil {
+			// advance the file position also in the base file, the next
+			// call may be a write at this position (or a seek with SEEK_CUR)
+			if _, seekErr := f.base.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
+				// only overwrite err in case the seek fails: we need to
+				// report an eventual io.EOF to the caller
+				err = seekErr
+			}
+		}
+		return n, err
+	}
+	if f.base != nil {
+		return f.base.Read(s)
+	}
+	return 0, BADFD
+}
+
+func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
+	if f.layer != nil {
+		n, err := f.layer.ReadAt(s, o)
+		if (err == nil || err == io.EOF) && f.base != nil {
+			_, err = f.base.Seek(o+int64(n), os.SEEK_SET)
+		}
+		return n, err
+	}
+	if f.base != nil {
+		return f.base.ReadAt(s, o)
+	}
+	return 0, BADFD
+}
+
+func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
+	if f.layer != nil {
+		pos, err = f.layer.Seek(o, w)
+		if (err == nil || err == io.EOF) && f.base != nil {
+			_, err = f.base.Seek(o, w)
+		}
+		return pos, err
+	}
+	if f.base != nil {
+		return f.base.Seek(o, w)
+	}
+	return 0, BADFD
+}
+
+func (f *UnionFile) Write(s []byte) (n int, err error) {
+	if f.layer != nil {
+		n, err = f.layer.Write(s)
+		if err == nil && f.base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
+			_, err = f.base.Write(s)
+		}
+		return n, err
+	}
+	if f.base != nil {
+		return f.base.Write(s)
+	}
+	return 0, BADFD
+}
+
+func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
+	if f.layer != nil {
+		n, err = f.layer.WriteAt(s, o)
+		if err == nil && f.base != nil {
+			_, err = f.base.WriteAt(s, o)
+		}
+		return n, err
+	}
+	if f.base != nil {
+		return f.base.WriteAt(s, o)
+	}
+	return 0, BADFD
+}
+
+func (f *UnionFile) Name() string {
+	if f.layer != nil {
+		return f.layer.Name()
+	}
+	return f.base.Name()
+}
+
+// Readdir will weave the two directories together and
+// return a single view of the overlayed directories
+func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
+	if f.off == 0 {
+		var files = make(map[string]os.FileInfo)
+		var rfi []os.FileInfo
+		if f.layer != nil {
+			rfi, err = f.layer.Readdir(-1)
+			if err != nil {
+				return nil, err
+			}
+			for _, fi := range rfi {
+				files[fi.Name()] = fi
+			}
+		}
+
+		if f.base != nil {
+			rfi, err = f.base.Readdir(-1)
+			if err != nil {
+				return nil, err
+			}
+			for _, fi := range rfi {
+				if _, exists := files[fi.Name()]; !exists {
+					files[fi.Name()] = fi
+				}
+			}
+		}
+		for _, fi := range files {
+			f.files = append(f.files, fi)
+		}
+	}
+	if c == -1 {
+		return f.files[f.off:], nil
+	}
+	defer func() { f.off += c }()
+	return f.files[f.off:c], nil
+}
+
+func (f *UnionFile) Readdirnames(c int) ([]string, error) {
+	rfi, err := f.Readdir(c)
+	if err != nil {
+		return nil, err
+	}
+	var names []string
+	for _, fi := range rfi {
+		names = append(names, fi.Name())
+	}
+	return names, nil
+}
+
+func (f *UnionFile) Stat() (os.FileInfo, error) {
+	if f.layer != nil {
+		return f.layer.Stat()
+	}
+	if f.base != nil {
+		return f.base.Stat()
+	}
+	return nil, BADFD
+}
+
+func (f *UnionFile) Sync() (err error) {
+	if f.layer != nil {
+		err = f.layer.Sync()
+		if err == nil && f.base != nil {
+			err = f.base.Sync()
+		}
+		return err
+	}
+	if f.base != nil {
+		return f.base.Sync()
+	}
+	return BADFD
+}
+
+func (f *UnionFile) Truncate(s int64) (err error) {
+	if f.layer != nil {
+		err = f.layer.Truncate(s)
+		if err == nil && f.base != nil {
+			err = f.base.Truncate(s)
+		}
+		return err
+	}
+	if f.base != nil {
+		return f.base.Truncate(s)
+	}
+	return BADFD
+}
+
+func (f *UnionFile) WriteString(s string) (n int, err error) {
+	if f.layer != nil {
+		n, err = f.layer.WriteString(s)
+		if err == nil && f.base != nil {
+			_, err = f.base.WriteString(s)
+		}
+		return n, err
+	}
+	if f.base != nil {
+		return f.base.WriteString(s)
+	}
+	return 0, BADFD
+}
+
+func copyToLayer(base Fs, layer Fs, name string) error {
+	bfh, err := base.Open(name)
+	if err != nil {
+		return err
+	}
+	defer bfh.Close()
+
+	// First make sure the directory exists
+	exists, err := Exists(layer, filepath.Dir(name))
+	if err != nil {
+		return err
+	}
+	if !exists {
+		err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
+		if err != nil {
+			return err
+		}
+	}
+
+	// Create the file on the overlay
+	lfh, err := layer.Create(name)
+	if err != nil {
+		return err
+	}
+	n, err := io.Copy(lfh, bfh)
+	if err != nil {
+		// If anything fails, clean up the file
+		layer.Remove(name)
+		lfh.Close()
+		return err
+	}
+
+	bfi, err := bfh.Stat()
+	if err != nil || bfi.Size() != n {
+		layer.Remove(name)
+		lfh.Close()
+		return syscall.EIO
+	}
+
+	err = lfh.Close()
+	if err != nil {
+		layer.Remove(name)
+		lfh.Close()
+		return err
+	}
+	return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
+}
diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go
new file mode 100644
index 0000000000000000000000000000000000000000..2f44e6a1004256db2e7aed636c7bb2937e63f5d7
--- /dev/null
+++ b/vendor/github.com/spf13/afero/util.go
@@ -0,0 +1,331 @@
+// Copyright ©2015 Steve Francia <spf@spf13.com>
+// Portions Copyright ©2015 The Hugo Authors
+// Portions Copyright 2016-present Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
+//
+// 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 afero
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"unicode"
+
+	"golang.org/x/text/transform"
+	"golang.org/x/text/unicode/norm"
+)
+
+// Filepath separator defined by os.Separator.
+const FilePathSeparator = string(filepath.Separator)
+
+// Takes a reader and a path and writes the content
+func (a Afero) WriteReader(path string, r io.Reader) (err error) {
+	return WriteReader(a.Fs, path, r)
+}
+
+func WriteReader(fs Fs, path string, r io.Reader) (err error) {
+	dir, _ := filepath.Split(path)
+	ospath := filepath.FromSlash(dir)
+
+	if ospath != "" {
+		err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
+		if err != nil {
+			if err != os.ErrExist {
+				log.Panicln(err)
+			}
+		}
+	}
+
+	file, err := fs.Create(path)
+	if err != nil {
+		return
+	}
+	defer file.Close()
+
+	_, err = io.Copy(file, r)
+	return
+}
+
+// Same as WriteReader but checks to see if file/directory already exists.
+func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {
+	return SafeWriteReader(a.Fs, path, r)
+}
+
+func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
+	dir, _ := filepath.Split(path)
+	ospath := filepath.FromSlash(dir)
+
+	if ospath != "" {
+		err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
+		if err != nil {
+			return
+		}
+	}
+
+	exists, err := Exists(fs, path)
+	if err != nil {
+		return
+	}
+	if exists {
+		return fmt.Errorf("%v already exists", path)
+	}
+
+	file, err := fs.Create(path)
+	if err != nil {
+		return
+	}
+	defer file.Close()
+
+	_, err = io.Copy(file, r)
+	return
+}
+
+func (a Afero) GetTempDir(subPath string) string {
+	return GetTempDir(a.Fs, subPath)
+}
+
+// GetTempDir returns the default temp directory with trailing slash
+// if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx
+func GetTempDir(fs Fs, subPath string) string {
+	addSlash := func(p string) string {
+		if FilePathSeparator != p[len(p)-1:] {
+			p = p + FilePathSeparator
+		}
+		return p
+	}
+	dir := addSlash(os.TempDir())
+
+	if subPath != "" {
+		// preserve windows backslash :-(
+		if FilePathSeparator == "\\" {
+			subPath = strings.Replace(subPath, "\\", "____", -1)
+		}
+		dir = dir + UnicodeSanitize((subPath))
+		if FilePathSeparator == "\\" {
+			dir = strings.Replace(dir, "____", "\\", -1)
+		}
+
+		if exists, _ := Exists(fs, dir); exists {
+			return addSlash(dir)
+		}
+
+		err := fs.MkdirAll(dir, 0777)
+		if err != nil {
+			panic(err)
+		}
+		dir = addSlash(dir)
+	}
+	return dir
+}
+
+// Rewrite string to remove non-standard path characters
+func UnicodeSanitize(s string) string {
+	source := []rune(s)
+	target := make([]rune, 0, len(source))
+
+	for _, r := range source {
+		if unicode.IsLetter(r) ||
+			unicode.IsDigit(r) ||
+			unicode.IsMark(r) ||
+			r == '.' ||
+			r == '/' ||
+			r == '\\' ||
+			r == '_' ||
+			r == '-' ||
+			r == '%' ||
+			r == ' ' ||
+			r == '#' {
+			target = append(target, r)
+		}
+	}
+
+	return string(target)
+}
+
+// Transform characters with accents into plan forms
+func NeuterAccents(s string) string {
+	t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
+	result, _, _ := transform.String(t, string(s))
+
+	return result
+}
+
+func isMn(r rune) bool {
+	return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
+}
+
+func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
+	return FileContainsBytes(a.Fs, filename, subslice)
+}
+
+// Check if a file contains a specified byte slice.
+func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
+	f, err := fs.Open(filename)
+	if err != nil {
+		return false, err
+	}
+	defer f.Close()
+
+	return readerContainsAny(f, subslice), nil
+}
+
+func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
+	return FileContainsAnyBytes(a.Fs, filename, subslices)
+}
+
+// Check if a file contains any of the specified byte slices.
+func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
+	f, err := fs.Open(filename)
+	if err != nil {
+		return false, err
+	}
+	defer f.Close()
+
+	return readerContainsAny(f, subslices...), nil
+}
+
+// readerContains reports whether any of the subslices is within r.
+func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
+
+	if r == nil || len(subslices) == 0 {
+		return false
+	}
+
+	largestSlice := 0
+
+	for _, sl := range subslices {
+		if len(sl) > largestSlice {
+			largestSlice = len(sl)
+		}
+	}
+
+	if largestSlice == 0 {
+		return false
+	}
+
+	bufflen := largestSlice * 4
+	halflen := bufflen / 2
+	buff := make([]byte, bufflen)
+	var err error
+	var n, i int
+
+	for {
+		i++
+		if i == 1 {
+			n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
+		} else {
+			if i != 2 {
+				// shift left to catch overlapping matches
+				copy(buff[:], buff[halflen:])
+			}
+			n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
+		}
+
+		if n > 0 {
+			for _, sl := range subslices {
+				if bytes.Contains(buff, sl) {
+					return true
+				}
+			}
+		}
+
+		if err != nil {
+			break
+		}
+	}
+	return false
+}
+
+func (a Afero) DirExists(path string) (bool, error) {
+	return DirExists(a.Fs, path)
+}
+
+// DirExists checks if a path exists and is a directory.
+func DirExists(fs Fs, path string) (bool, error) {
+	fi, err := fs.Stat(path)
+	if err == nil && fi.IsDir() {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}
+
+func (a Afero) IsDir(path string) (bool, error) {
+	return IsDir(a.Fs, path)
+}
+
+// IsDir checks if a given path is a directory.
+func IsDir(fs Fs, path string) (bool, error) {
+	fi, err := fs.Stat(path)
+	if err != nil {
+		return false, err
+	}
+	return fi.IsDir(), nil
+}
+
+func (a Afero) IsEmpty(path string) (bool, error) {
+	return IsEmpty(a.Fs, path)
+}
+
+// IsEmpty checks if a given file or directory is empty.
+func IsEmpty(fs Fs, path string) (bool, error) {
+	if b, _ := Exists(fs, path); !b {
+		return false, fmt.Errorf("%q path does not exist", path)
+	}
+	fi, err := fs.Stat(path)
+	if err != nil {
+		return false, err
+	}
+	if fi.IsDir() {
+		f, err := fs.Open(path)
+		if err != nil {
+			return false, err
+		}
+		defer f.Close()
+		list, err := f.Readdir(-1)
+		return len(list) == 0, nil
+	}
+	return fi.Size() == 0, nil
+}
+
+func (a Afero) Exists(path string) (bool, error) {
+	return Exists(a.Fs, path)
+}
+
+// Check if a file or directory exists.
+func Exists(fs Fs, path string) (bool, error) {
+	_, err := fs.Stat(path)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}
+
+func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
+	combinedPath := filepath.Join(basePathFs.path, relativePath)
+	if parent, ok := basePathFs.source.(*BasePathFs); ok {
+		return FullBaseFsPath(parent, combinedPath)
+	}
+
+	return combinedPath
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 7c9aecb94cf7cf777dfc1ae03f1f483b079ad86a..7e9f4892e7a5721a9ad2e57ad6c381a3cf5c20a5 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -238,6 +238,18 @@
 			"revision": "3d4380f53a34dcdc95f0c1db702615992b38d9a4",
 			"revisionTime": "2017-06-20T14:45:10Z"
 		},
+		{
+			"checksumSHA1": "lBehULzb2/kIK3wZ0gz2yNmHq9s=",
+			"path": "github.com/spf13/afero",
+			"revision": "9be650865eab0c12963d8753212f4f9c66cdcf12",
+			"revisionTime": "2017-02-17T16:41:46Z"
+		},
+		{
+			"checksumSHA1": "5KRbEQ28dDaQmKwAYTD0if/aEvg=",
+			"path": "github.com/spf13/afero/mem",
+			"revision": "9be650865eab0c12963d8753212f4f9c66cdcf12",
+			"revisionTime": "2017-02-17T16:41:46Z"
+		},
 		{
 			"checksumSHA1": "Wgm8Y0np1Usy3+ZTTPFdHoqzed8=",
 			"path": "github.com/spf13/cobra",