Merge pull request #6999 from hashicorp/gogetter

go getter
This commit is contained in:
Megan Marsh 2019-03-13 16:23:21 -07:00 committed by GitHub
commit e4a189ce5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1362 changed files with 184477 additions and 41261 deletions

View File

@ -26,10 +26,9 @@ build_script:
- git rev-parse HEAD
# go test $(go list ./... | grep -v vendor)
- ps: |
go.exe test -timeout=2m (go.exe list ./... `
go.exe test -v -timeout=2m (go.exe list ./... `
|? { -not $_.Contains('/vendor/') } `
|? { $_ -ne 'github.com/hashicorp/packer/builder/parallels/common' } `
|? { $_ -ne 'github.com/hashicorp/packer/common' }`
|? { $_ -ne 'github.com/hashicorp/packer/provisioner/ansible' })
test: off

View File

@ -76,7 +76,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
client, err := b.config.Client()
if err != nil {

View File

@ -190,7 +190,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warns, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
if runtime.GOOS != "linux" {
return nil, errors.New("The amazon-chroot builder only works on Linux environments.")
}

View File

@ -85,7 +85,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
session, err := b.config.Session()
if err != nil {
return nil, err

View File

@ -100,7 +100,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
session, err := b.config.Session()
if err != nil {
return nil, err

View File

@ -89,7 +89,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
session, err := b.config.Session()
if err != nil {
return nil, err

View File

@ -170,7 +170,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
session, err := b.config.Session()
if err != nil {
return nil, err

View File

@ -51,7 +51,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, errs
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
ui.Say("Running builder ...")

View File

@ -31,7 +31,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
// Run implements the packer.Builder interface.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
b.ui = ui
// Create a CloudStack API client.

View File

@ -35,7 +35,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
client := godo.NewClient(oauth2.NewClient(oauth2.NoContext, &apiTokenSource{
AccessToken: b.config.APIToken,
}))

View File

@ -29,7 +29,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
driver := &DockerDriver{Ctx: &b.config.ctx, Ui: ui}
if err := driver.Verify(); err != nil {
return nil, err

View File

@ -21,7 +21,6 @@ func TestCommunicator_impl(t *testing.T) {
// TestUploadDownload verifies that basic upload / download functionality works
func TestUploadDownload(t *testing.T) {
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
tpl, err := template.Parse(strings.NewReader(dockerBuilderConfig))
if err != nil {
@ -76,7 +75,7 @@ func TestUploadDownload(t *testing.T) {
hook := &packer.DispatchHook{Mapping: hooks}
// Run things
artifact, err := builder.Run(ui, hook, cache)
artifact, err := builder.Run(ui, hook)
if err != nil {
t.Fatalf("Error running build %s", err)
}
@ -105,7 +104,6 @@ func TestUploadDownload(t *testing.T) {
// only intermittently.
func TestLargeDownload(t *testing.T) {
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
tpl, err := template.Parse(strings.NewReader(dockerLargeBuilderConfig))
if err != nil {
@ -166,7 +164,7 @@ func TestLargeDownload(t *testing.T) {
hook := &packer.DispatchHook{Mapping: hooks}
// Run things
artifact, err := builder.Run(ui, hook, cache)
artifact, err := builder.Run(ui, hook)
if err != nil {
t.Fatalf("Error running build %s", err)
}
@ -210,7 +208,6 @@ func TestLargeDownload(t *testing.T) {
// TestFixUploadOwner verifies that owner of uploaded files is the user the container is running as.
func TestFixUploadOwner(t *testing.T) {
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
tpl, err := template.Parse(strings.NewReader(testFixUploadOwnerTemplate))
if err != nil {
@ -275,7 +272,7 @@ func TestFixUploadOwner(t *testing.T) {
}
hook := &packer.DispatchHook{Mapping: hooks}
artifact, err := builder.Run(ui, hook, cache)
artifact, err := builder.Run(ui, hook)
if err != nil {
t.Fatalf("Error running build %s", err)
}

View File

@ -33,7 +33,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
// Run is where the actual build should take place. It takes a Build and a Ui.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
artifact := new(FileArtifact)
if b.config.Source != "" {

View File

@ -33,7 +33,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Run executes a googlecompute Packer build and returns a packer.Artifact
// representing a GCE machine image.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
driver, err := NewDriverGCE(
ui, b.config.ProjectId, &b.config.Account)
if err != nil {

View File

@ -31,7 +31,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
opts := []hcloud.ClientOption{
hcloud.WithToken(b.config.HCloudToken),
hcloud.WithEndpoint(b.config.Endpoint),

View File

@ -9,7 +9,7 @@ import (
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/hyperonecom/h1-client-go"
openapi "github.com/hyperonecom/h1-client-go"
)
const BuilderID = "hyperone.builder"
@ -50,7 +50,7 @@ type wrappedCommandTemplate struct {
Command string
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
wrappedCommand := func(command string) (string, error) {
ctx := b.config.ctx
ctx.Data = &wrappedCommandTemplate{Command: command}

View File

@ -356,7 +356,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Run executes a Packer build and returns a packer.Artifact representing
// a Hyperv appliance.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with Hyperv
driver, err := hypervcommon.NewHypervPS4Driver()
if err != nil {
@ -365,7 +365,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)

View File

@ -5,7 +5,6 @@ package iso
import (
"context"
"fmt"
"os"
"reflect"
"strconv"
"testing"
@ -287,9 +286,6 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
t.Fatalf("should not have error: %s", err)
}
if b.config.ISOChecksum != "foo" {
t.Fatalf("should've lowercased: %s", b.config.ISOChecksum)
}
}
func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
@ -302,8 +298,8 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test good
@ -329,7 +325,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
t.Log("should error in prepare but go-getter doesn't let us validate yet. This will fail before dl.")
}
// Test none
@ -606,13 +602,11 @@ func TestUserVariablesInBootCommand(t *testing.T) {
}
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
hook := &packer.MockHook{}
driver := &hypervcommon.DriverMock{}
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("driver", driver)
state.Put("hook", hook)

View File

@ -372,7 +372,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Run executes a Packer build and returns a packer.Artifact representing
// a Hyperv appliance.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with Hyperv
driver, err := hypervcommon.NewHypervPS4Driver()
if err != nil {
@ -381,7 +381,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)

View File

@ -203,9 +203,6 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
t.Fatalf("should not have error: %s", err)
}
if b.config.ISOChecksum != "foo" {
t.Fatalf("should've lowercased: %s", b.config.ISOChecksum)
}
}
func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
@ -226,8 +223,8 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test good
@ -245,17 +242,6 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
t.Fatalf("should've lowercased: %s", b.config.ISOChecksumType)
}
// Test unknown
config["iso_checksum_type"] = "fake"
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test none
config["iso_checksum_type"] = "none"
b = Builder{}
@ -517,13 +503,11 @@ func TestUserVariablesInBootCommand(t *testing.T) {
}
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
hook := &packer.MockHook{}
driver := &hypervcommon.DriverMock{}
// Set up the state.
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("driver", driver)
state.Put("hook", hook)

View File

@ -33,7 +33,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
wrappedCommand := func(command string) (string, error) {
b.config.ctx.Data = &wrappedCommandTemplate{Command: command}
return interpolate.Render(b.config.CommandWrapper, &b.config.ctx)
@ -52,7 +52,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("cache", cache)
state.Put("hook", hook)
state.Put("ui", ui)
state.Put("wrappedCommand", CommandWrapper(wrappedCommand))

View File

@ -31,7 +31,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
wrappedCommand := func(command string) (string, error) {
b.config.ctx.Data = &wrappedCommandTemplate{Command: command}
return interpolate.Render(b.config.CommandWrapper, &b.config.ctx)
@ -46,7 +46,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("cache", cache)
state.Put("hook", hook)
state.Put("ui", ui)
state.Put("wrappedCommand", CommandWrapper(wrappedCommand))

View File

@ -27,7 +27,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
ui.Message("Creating Naver Cloud Platform Connection ...")
conn := ncloud.NewConnection(b.config.AccessKey, b.config.SecretKey)

View File

@ -26,7 +26,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
steps := []multistep.Step{}
if b.config.CommConfig.Type != "none" {

View File

@ -28,7 +28,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
state := new(multistep.BasicStateBag)

View File

@ -65,7 +65,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
computeClient, err := b.config.computeV2Client()
if err != nil {
return nil, fmt.Errorf("Error initializing compute client: %s", err)

View File

@ -41,7 +41,7 @@ func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
loggingEnabled := os.Getenv("PACKER_OCI_CLASSIC_LOGGING") != ""
httpClient := cleanhttp.DefaultClient()
config := &opc.Config{

View File

@ -36,7 +36,7 @@ func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
driver, err := NewDriverOCI(b.config)
if err != nil {
return nil, err

View File

@ -141,7 +141,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with Parallels
driver, err := parallelscommon.NewDriver()
if err != nil {
@ -229,7 +229,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)

View File

@ -32,7 +32,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Run executes a Packer build and returns a packer.Artifact representing
// a Parallels appliance.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with Parallels
driver, err := parallelscommon.NewDriver()
if err != nil {

View File

@ -27,7 +27,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
state := new(multistep.BasicStateBag)
state.Put("config", b.config)

View File

@ -354,7 +354,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with Qemu
driver, err := b.newDriver(b.config.QemuBinary)
if err != nil {
@ -448,7 +448,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)

View File

@ -33,7 +33,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
client, err := api.NewScalewayAPI(b.config.Organization, b.config.Token, b.config.UserAgent, b.config.Region)
if err != nil {

View File

@ -58,7 +58,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
cvmClient, vpcClient, err := b.config.Client()
if err != nil {
return nil, err

View File

@ -45,7 +45,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return nil, errs.ErrorOrNil()
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
config := b.config
driver, err := NewDriverTriton(ui, config)

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
@ -123,14 +124,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("You may either set global_id or source_path but not both"))
}
if strings.HasSuffix(b.config.SourceBox, ".box") {
b.config.SourceBox, err = common.ValidatedURL(b.config.SourceBox)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err))
}
fileOK := common.FileExistsLocally(b.config.SourceBox)
if !fileOK {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Source file '%s' needs to exist at time of config validation!", b.config.SourceBox))
if _, err := os.Stat(b.config.SourceBox); err != nil {
packer.MultiErrorAppend(errs,
fmt.Errorf("Source box '%s' needs to exist at time of config validation! %v", b.config.SourceBox, err))
}
}
}
@ -166,7 +162,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Run executes a Packer build and returns a packer.Artifact representing
// a VirtualBox appliance.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with VirtualBox
VagrantCWD, err := filepath.Abs(b.config.OutputDir)
if err != nil {
@ -182,7 +178,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("config", b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)
state.Put("cache", cache)
state.Put("hook", hook)
state.Put("ui", ui)

View File

@ -121,15 +121,6 @@ func (s *StepDownloadGuestAdditions) Run(ctx context.Context, state multistep.St
}
}
// Convert the file/url to an actual URL for step_download to process.
url, err = common.ValidatedURL(url)
if err != nil {
err := fmt.Errorf("Error preparing guest additions url: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Printf("Guest additions URL: %s", url)
// We're good, so let's go ahead and download this thing..
@ -139,6 +130,7 @@ func (s *StepDownloadGuestAdditions) Run(ctx context.Context, state multistep.St
Description: "Guest additions",
ResultKey: "guest_additions_path",
Url: []string{url},
Extension: "iso",
}
return downStep.Run(ctx, state)

View File

@ -191,7 +191,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with VirtualBox
driver, err := vboxcommon.NewDriver()
if err != nil {
@ -311,7 +311,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)

View File

@ -32,7 +32,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Run executes a Packer build and returns a packer.Artifact representing
// a VirtualBox appliance.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
// Create the driver that we'll use to communicate with VirtualBox
driver, err := vboxcommon.NewDriver()
if err != nil {
@ -44,7 +44,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("config", b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("driver", driver)
state.Put("cache", cache)
state.Put("hook", hook)
state.Put("ui", ui)

View File

@ -2,6 +2,7 @@ package ovf
import (
"fmt"
"os"
"strings"
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
@ -102,17 +103,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
if c.SourcePath == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required"))
} else {
c.SourcePath, err = common.ValidatedURL(c.SourcePath)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is invalid: %s", err))
}
fileOK := common.FileExistsLocally(c.SourcePath)
if !fileOK {
packer.MultiErrorAppend(errs,
fmt.Errorf("Source file '%s' needs to exist at time of config validation!", c.SourcePath))
}
}
if _, err := os.Stat(c.SourcePath); err != nil {
packer.MultiErrorAppend(errs,
fmt.Errorf("Source file '%s' needs to exist at time of config validation! %v", c.SourcePath, err))
}
validMode := false

View File

@ -30,7 +30,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
return warnings, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, b.config.VMName)
if err != nil {
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
@ -56,7 +56,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("debug", b.config.PackerDebug)
state.Put("dir", dir)

View File

@ -174,8 +174,7 @@ func setupVMwareBuild(t *testing.T, builderConfig map[string]string, provisioner
}
// and then finally build it
cache := &packer.FileCache{CacheDir: os.TempDir()}
artifacts, err := b.Run(ui, cache)
artifacts, err := b.Run(ui)
if err != nil {
t.Fatalf("Failed to build artifact: %s", err)
}

View File

@ -33,7 +33,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
// Run executes a Packer build and returns a packer.Artifact representing
// a VMware image.
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, b.config.VMName)
if err != nil {
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)

View File

@ -177,7 +177,7 @@ func (c *BuildCommand) Run(args []string) int {
name := b.Name()
log.Printf("Starting build run: %s", name)
ui := buildUis[name]
runArtifacts, err := b.Run(ui, c.Cache)
runArtifacts, err := b.Run(ui)
if err != nil {
ui.Error(fmt.Sprintf("Build '%s' errored: %s", name, err))

View File

@ -26,7 +26,6 @@ const (
// Packer command inherits.
type Meta struct {
CoreConfig *packer.CoreConfig
Cache packer.Cache
Ui packer.Ui
Version string

View File

@ -1,16 +1,7 @@
package common
import (
"fmt"
"net/url"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"time"
"github.com/hashicorp/packer/packer"
)
// PackerKeyEnv is used to specify the key interval (delay) between keystrokes
@ -21,188 +12,3 @@ const PackerKeyEnv = "PACKER_KEY_INTERVAL"
// PackerKeyDefault 100ms is appropriate for shared build infrastructure while a
// shorter delay (e.g. 10ms) can be used on a workstation. See PackerKeyEnv.
const PackerKeyDefault = 100 * time.Millisecond
// ChooseString returns the first non-empty value.
func ChooseString(vals ...string) string {
for _, el := range vals {
if el != "" {
return el
}
}
return ""
}
// SupportedProtocol verifies that the url passed is actually supported or not
// This will also validate that the protocol is one that's actually implemented.
func SupportedProtocol(u *url.URL) bool {
// url.Parse shouldn't return nil except on error....but it can.
if u == nil {
return false
}
// build a dummy NewDownloadClient since this is the only place that valid
// protocols are actually exposed.
cli := NewDownloadClient(&DownloadConfig{}, new(packer.NoopUi))
// Iterate through each downloader to see if a protocol was found.
ok := false
for scheme := range cli.config.DownloaderMap {
if strings.ToLower(u.Scheme) == strings.ToLower(scheme) {
ok = true
}
}
return ok
}
// DownloadableURL processes a URL that may also be a file path and returns
// a completely valid URL representing the requested file. For example,
// the original URL might be "local/file.iso" which isn't a valid URL,
// and so DownloadableURL will return "file://local/file.iso"
// No other transformations are done to the path.
func DownloadableURL(original string) (string, error) {
var absPrefix, result string
absPrefix = ""
if runtime.GOOS == "windows" {
absPrefix = "/"
}
// Check that the user specified a UNC path, and promote it to an smb:// uri.
if strings.HasPrefix(original, "\\\\") && len(original) > 2 && original[2] != '?' {
result = filepath.ToSlash(original[2:])
return fmt.Sprintf("smb://%s", result), nil
}
// Fix the url if it's using bad characters commonly mistaken with a path.
original = filepath.ToSlash(original)
// Check to see that this is a parseable URL with a scheme and a host.
// If so, then just pass it through.
if u, err := url.Parse(original); err == nil && u.Scheme != "" && u.Host != "" {
return original, nil
}
// If it's a file scheme, then convert it back to a regular path so the next
// case which forces it to an absolute path, will correct it.
if u, err := url.Parse(original); err == nil && strings.ToLower(u.Scheme) == "file" {
original = u.Path
}
// If we're on Windows and we start with a slash, then this absolute path
// is wrong. Fix it up, so the next case can figure out the absolute path.
if rpath := strings.SplitN(original, "/", 2); rpath[0] == "" && runtime.GOOS == "windows" {
result = rpath[1]
} else {
result = original
}
// Since we should be some kind of path (relative or absolute), check
// that the file exists, then make it an absolute path so we can return an
// absolute uri.
if _, err := os.Stat(result); err == nil {
result, err = filepath.Abs(filepath.FromSlash(result))
if err != nil {
return "", err
}
result, err = filepath.EvalSymlinks(result)
if err != nil {
return "", err
}
result = filepath.Clean(result)
return fmt.Sprintf("file://%s%s", absPrefix, filepath.ToSlash(result)), nil
}
// Otherwise, check if it was originally an absolute path, and fix it if so.
if strings.HasPrefix(original, "/") {
return fmt.Sprintf("file://%s%s", absPrefix, result), nil
}
// Anything left should be a non-existent relative path. So fix it up here.
result = filepath.ToSlash(filepath.Clean(result))
return fmt.Sprintf("file://./%s", result), nil
}
// Force the parameter into a url. This will transform the parameter into
// a proper url, removing slashes, adding the proper prefix, etc.
func ValidatedURL(original string) (string, error) {
// See if the user failed to give a url
if ok, _ := regexp.MatchString("(?m)^[^[:punct:]]+://", original); !ok {
// So since no magic was found, this must be a path.
result, err := DownloadableURL(original)
if err == nil {
return ValidatedURL(result)
}
return "", err
}
// Verify that the url is parseable...just in case.
u, err := url.Parse(original)
if err != nil {
return "", err
}
// We should now have a url, so verify that it's a protocol we support.
if !SupportedProtocol(u) {
return "", fmt.Errorf("Unsupported protocol scheme! (%#v)", u)
}
// We should now have a properly formatted and supported url
return u.String(), nil
}
// FileExistsLocally takes the URL output from DownloadableURL, and determines
// whether it is present on the file system.
// example usage:
//
// myFile, err = common.DownloadableURL(c.SourcePath)
// ...
// fileExists := common.StatURL(myFile)
// possible output:
// true -- should occur if the file is present, or if the file is not present,
// but is not supposed to be (e.g. the schema is http://, not file://)
// false -- should occur if there was an error stating the file, so the
// file is not present when it should be.
func FileExistsLocally(original string) bool {
// original should be something like file://C:/my/path.iso
u, _ := url.Parse(original)
// First create a dummy downloader so we can figure out which
// protocol to use.
cli := NewDownloadClient(&DownloadConfig{}, new(packer.NoopUi))
d, ok := cli.config.DownloaderMap[u.Scheme]
if !ok {
return false
}
// Check to see that it's got a Local way of doing things.
local, ok := d.(LocalDownloader)
if !ok {
return true // XXX: Remote URLs short-circuit this logic.
}
// Figure out where we're at.
wd, err := os.Getwd()
if err != nil {
return false
}
// Now figure out the real path to the file.
realpath, err := local.toPath(wd, *u)
if err != nil {
return false
}
// Finally we can seek the truth via os.Stat.
_, err = os.Stat(realpath)
if err != nil {
return false
}
return true
}

View File

@ -1,312 +0,0 @@
package common
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)
func TestChooseString(t *testing.T) {
cases := []struct {
Input []string
Output string
}{
{
[]string{"", "foo", ""},
"foo",
},
{
[]string{"", "foo", "bar"},
"foo",
},
{
[]string{"", "", ""},
"",
},
}
for _, tc := range cases {
result := ChooseString(tc.Input...)
if result != tc.Output {
t.Fatalf("bad: %#v", tc.Input)
}
}
}
func TestValidatedURL(t *testing.T) {
// Invalid URL: has hex code in host
_, err := ValidatedURL("http://what%20.com")
if err == nil {
t.Fatalf("expected err : %s", err)
}
// Invalid: unsupported scheme
_, err = ValidatedURL("ftp://host.com/path")
if err == nil {
t.Fatalf("expected err : %s", err)
}
// Valid: http
u, err := ValidatedURL("HTTP://packer.io/path")
if err != nil {
t.Fatalf("err: %s", err)
}
if u != "http://packer.io/path" {
t.Fatalf("bad: %s", u)
}
cases := []struct {
InputString string
OutputURL string
ErrExpected bool
}{
// Invalid URL: has hex code in host
{"http://what%20.com", "", true},
// Valid: http
{"HTTP://packer.io/path", "http://packer.io/path", false},
// No path
{"HTTP://packer.io", "http://packer.io", false},
// Invalid: unsupported scheme
{"ftp://host.com/path", "", true},
}
for _, tc := range cases {
u, err := ValidatedURL(tc.InputString)
if u != tc.OutputURL {
t.Fatal(fmt.Sprintf("Error with URL %s: got %s but expected %s",
tc.InputString, tc.OutputURL, u))
}
if (err != nil) != tc.ErrExpected {
if tc.ErrExpected == true {
t.Fatal(fmt.Sprintf("Error with URL %s: we expected "+
"ValidatedURL to return an error but didn't get one.",
tc.InputString))
} else {
t.Fatal(fmt.Sprintf("Error with URL %s: we did not expect an "+
" error from ValidatedURL but we got: %s",
tc.InputString, err))
}
}
}
}
func GetNativePathToTestFixtures(t *testing.T) string {
const path = "./test-fixtures"
res, err := filepath.Abs(path)
if err != nil {
t.Fatalf("err converting test-fixtures path into an absolute path : %s", err)
}
return res
}
func GetPortablePathToTestFixtures(t *testing.T) string {
res := GetNativePathToTestFixtures(t)
return filepath.ToSlash(res)
}
func TestDownloadableURL_WindowsFiles(t *testing.T) {
if runtime.GOOS == "windows" {
portablepath := GetPortablePathToTestFixtures(t)
nativepath := GetNativePathToTestFixtures(t)
dirCases := []struct {
InputString string
OutputURL string
ErrExpected bool
}{ // TODO: add different directories
{
fmt.Sprintf("%s\\SomeDir\\myfile.txt", nativepath),
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // without the drive makes this native path a relative file:// uri
"test-fixtures\\SomeDir\\myfile.txt",
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // without the drive makes this native path a relative file:// uri
"test-fixtures/SomeDir/myfile.txt",
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // UNC paths being promoted to smb:// uri scheme.
fmt.Sprintf("\\\\localhost\\C$\\%s\\SomeDir\\myfile.txt", nativepath),
fmt.Sprintf("smb://localhost/C$/%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // Absolute uri (incorrect slash type)
fmt.Sprintf("file:///%s\\SomeDir\\myfile.txt", nativepath),
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // Absolute uri (existing and mis-spelled)
fmt.Sprintf("file:///%s/Somedir/myfile.txt", nativepath),
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // Absolute path (non-existing)
"\\absolute\\path\\to\\non-existing\\file.txt",
"file:///absolute/path/to/non-existing/file.txt",
false,
},
{ // Absolute paths (existing)
fmt.Sprintf("%s/SomeDir/myfile.txt", nativepath),
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // Relative path (non-existing)
"./nonexisting/relative/path/to/file.txt",
"file://./nonexisting/relative/path/to/file.txt",
false,
},
{ // Relative path (existing)
"./test-fixtures/SomeDir/myfile.txt",
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // Absolute uri (existing and with `/` prefix)
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
fmt.Sprintf("file:///%s/SomeDir/myfile.txt", portablepath),
false,
},
{ // Absolute uri (non-existing and with `/` prefix)
"file:///path/to/non-existing/file.txt",
"file:///path/to/non-existing/file.txt",
false,
},
{ // Absolute uri (non-existing and missing `/` prefix)
"file://path/to/non-existing/file.txt",
"file://path/to/non-existing/file.txt",
false,
},
{ // Absolute uri and volume (non-existing and with `/` prefix)
"file:///T:/path/to/non-existing/file.txt",
"file:///T:/path/to/non-existing/file.txt",
false,
},
{ // Absolute uri and volume (non-existing and missing `/` prefix)
"file://T:/path/to/non-existing/file.txt",
"file://T:/path/to/non-existing/file.txt",
false,
},
}
// Run through test cases to make sure they all parse correctly
for idx, tc := range dirCases {
u, err := DownloadableURL(tc.InputString)
if (err != nil) != tc.ErrExpected {
t.Fatalf("Test Case %d failed: Expected err = %#v, err = %#v, input = %s",
idx, tc.ErrExpected, err, tc.InputString)
}
if u != tc.OutputURL {
t.Fatalf("Test Case %d failed: Expected %s but received %s from input %s",
idx, tc.OutputURL, u, tc.InputString)
}
}
}
}
func TestDownloadableURL_FilePaths(t *testing.T) {
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("tempfile err: %s", err)
}
defer os.Remove(tf.Name())
tf.Close()
tfPath, err := filepath.EvalSymlinks(tf.Name())
if err != nil {
t.Fatalf("tempfile err: %s", err)
}
tfPath = filepath.Clean(tfPath)
filePrefix := "file://"
// If we're running windows, then absolute URIs are `/`-prefixed.
platformPrefix := ""
if runtime.GOOS == "windows" {
platformPrefix = "/"
}
// Relative filepath. We run this test in a func so that
// the defers run right away.
func() {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd err: %s", err)
}
err = os.Chdir(filepath.Dir(tfPath))
if err != nil {
t.Fatalf("chdir err: %s", err)
}
defer os.Chdir(wd)
filename := filepath.Base(tfPath)
u, err := DownloadableURL(filename)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := fmt.Sprintf("%s%s%s",
filePrefix,
platformPrefix,
strings.Replace(tfPath, `\`, `/`, -1))
if u != expected {
t.Fatalf("unexpected: %#v != %#v", u, expected)
}
}()
// Test some cases with and without a schema prefix
for _, prefix := range []string{"", filePrefix + platformPrefix} {
// Nonexistent file
_, err = DownloadableURL(prefix + "i/dont/exist")
if err != nil {
t.Fatalf("err: %s", err)
}
// Good file (absolute)
u, err := DownloadableURL(prefix + tfPath)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := fmt.Sprintf("%s%s%s",
filePrefix,
platformPrefix,
strings.Replace(tfPath, `\`, `/`, -1))
if u != expected {
t.Fatalf("unexpected: %s != %s", u, expected)
}
}
}
func TestFileExistsLocally(t *testing.T) {
portablepath := GetPortablePathToTestFixtures(t)
dirCases := []struct {
Input string
Output bool
}{
// file exists locally
{fmt.Sprintf("file://%s/SomeDir/myfile.txt", portablepath), true},
// remote protocols short-circuit and are considered to exist locally
{"https://myfile.iso", true},
// non-existent protocols do not exist and hence fail
{"nonexistent-protocol://myfile.iso", false},
// file does not exist locally
{"file:///C/i/dont/exist", false},
}
// Run through test cases to make sure they all parse correctly
for _, tc := range dirCases {
fileOK := FileExistsLocally(tc.Input)
if fileOK != tc.Output {
t.Fatalf("Test Case failed: Expected %#v, received = %#v, input = %s",
tc.Output, fileOK, tc.Input)
}
}
}

View File

@ -1,564 +0,0 @@
package common
import (
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"errors"
"fmt"
"hash"
"io"
"log"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"runtime"
"strings"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
)
// DownloadConfig is the configuration given to instantiate a new
// download instance. Once a configuration is used to instantiate
// a download client, it must not be modified.
type DownloadConfig struct {
// The source URL in the form of a string.
Url string
// This is the path to download the file to.
TargetPath string
// DownloaderMap maps a schema to a Download.
DownloaderMap map[string]Downloader
// If true, this will copy even a local file to the target
// location. If false, then it will "download" the file by just
// returning the local path to the file.
CopyFile bool
// The hashing implementation to use to checksum the downloaded file.
Hash hash.Hash
// The checksum for the downloaded file. The hash implementation configuration
// for the downloader will be used to verify with this checksum after
// it is downloaded.
Checksum []byte
// What to use for the user agent for HTTP requests. If set to "", use the
// default user agent provided by Go.
UserAgent string
}
// A DownloadClient helps download, verify checksums, etc.
type DownloadClient struct {
config *DownloadConfig
}
// HashForType returns the Hash implementation for the given string
// type, or nil if the type is not supported.
func HashForType(t string) hash.Hash {
switch t {
case "md5":
return md5.New()
case "sha1":
return sha1.New()
case "sha256":
return sha256.New()
case "sha512":
return sha512.New()
default:
return nil
}
}
// NewDownloadClient returns a new DownloadClient for the given
// configuration.
func NewDownloadClient(c *DownloadConfig, ui packer.Ui) *DownloadClient {
// Create downloader map if it hasn't been specified already.
if c.DownloaderMap == nil {
c.DownloaderMap = map[string]Downloader{
"file": &FileDownloader{Ui: ui, bufferSize: nil},
"http": &HTTPDownloader{Ui: ui, userAgent: c.UserAgent},
"https": &HTTPDownloader{Ui: ui, userAgent: c.UserAgent},
"smb": &SMBDownloader{Ui: ui, bufferSize: nil},
}
}
return &DownloadClient{config: c}
}
// Downloader defines what capabilities a downloader should have.
type Downloader interface {
Resume()
Cancel()
ProgressBar() packer.ProgressBar
}
// A LocalDownloader is responsible for converting a uri to a local path
// that the platform can open directly.
type LocalDownloader interface {
toPath(string, url.URL) (string, error)
}
// A RemoteDownloader is responsible for actually taking a remote URL and
// downloading it.
type RemoteDownloader interface {
Download(*os.File, *url.URL) error
}
func (d *DownloadClient) Cancel() {
// TODO(mitchellh): Implement
}
func (d *DownloadClient) Get(ui packer.Ui) (string, error) {
// If we already have the file and it matches, then just return the target path.
if verify, _ := d.VerifyChecksum(ui, d.config.TargetPath); verify {
log.Println("[DEBUG] Initial checksum matched, no download needed.")
return d.config.TargetPath, nil
}
/* parse the configuration url into a net/url object */
u, err := url.Parse(d.config.Url)
if err != nil {
return "", err
}
log.Printf("Parsed URL: %#v", u)
/* use the current working directory as the base for relative uri's */
cwd, err := os.Getwd()
if err != nil {
return "", err
}
// Determine which is the correct downloader to use
var finalPath string
var ok bool
downloader, ok := d.config.DownloaderMap[u.Scheme]
if !ok {
return "", fmt.Errorf("No downloader for scheme: %s", u.Scheme)
}
remote, ok := downloader.(RemoteDownloader)
if !ok {
return "", fmt.Errorf("Unable to treat uri scheme %s as a Downloader. : %T", u.Scheme, downloader)
}
local, ok := downloader.(LocalDownloader)
if !ok && !d.config.CopyFile {
d.config.CopyFile = true
}
// If we're copying the file, then just use the actual downloader
if d.config.CopyFile {
var f *os.File
finalPath = d.config.TargetPath
f, err = os.OpenFile(finalPath, os.O_RDWR|os.O_CREATE, os.FileMode(0666))
if err != nil {
return "", err
}
log.Printf("[DEBUG] Downloading: %s", u.String())
err = remote.Download(f, u)
f.Close()
if err != nil {
return "", err
}
// Otherwise if our Downloader is a LocalDownloader we can just use the
// path after transforming it.
} else {
finalPath, err = local.toPath(cwd, *u)
if err != nil {
return "", err
}
log.Printf("[DEBUG] Using local file: %s", finalPath)
}
if d.config.Hash != nil {
var verify bool
verify, err = d.VerifyChecksum(ui, finalPath)
if err == nil && !verify {
// Only delete the file if we made a copy or downloaded it
if d.config.CopyFile {
os.Remove(finalPath)
}
err = fmt.Errorf(
"checksums didn't match. expected %s and got %s",
hex.EncodeToString(d.config.Checksum),
hex.EncodeToString(d.config.Hash.Sum(nil)))
}
}
return finalPath, err
}
// VerifyChecksum tests that the path matches the checksum for the
// download.
func (d *DownloadClient) VerifyChecksum(ui packer.Ui, path string) (bool, error) {
if d.config.Checksum == nil || d.config.Hash == nil {
return false, errors.New("Checksum or Hash isn't set on download.")
}
f, err := os.Open(path)
if err != nil {
return false, err
}
defer f.Close()
ui.Message(fmt.Sprintf("Verifying checksum of %s", path))
d.config.Hash.Reset()
io.Copy(d.config.Hash, f)
return bytes.Equal(d.config.Hash.Sum(nil), d.config.Checksum), nil
}
// HTTPDownloader is an implementation of Downloader that downloads
// files over HTTP.
type HTTPDownloader struct {
userAgent string
Ui packer.Ui
}
func (d *HTTPDownloader) Cancel() {
// TODO(mitchellh): Implement
}
func (d *HTTPDownloader) Resume() {
// TODO(mitchellh): Implement
}
func (d *HTTPDownloader) Download(dst *os.File, src *url.URL) error {
log.Printf("Starting download over HTTP: %s", src.String())
// Seek to the beginning by default
if _, err := dst.Seek(0, 0); err != nil {
return err
}
var current int64
// Make the request. We first make a HEAD request so we can check
// if the server supports range queries. If the server/URL doesn't
// support HEAD requests, we just fall back to GET.
req, err := http.NewRequest("HEAD", src.String(), nil)
if err != nil {
return err
}
if d.userAgent != "" {
req.Header.Set("User-Agent", d.userAgent)
}
httpClient := commonhelper.HttpClientWithEnvironmentProxy()
resp, err := httpClient.Do(req)
if err != nil || resp == nil {
if resp == nil {
log.Printf("[DEBUG] (download) HTTP connection error: %s", err.Error())
} else if resp.StatusCode >= 400 && resp.StatusCode < 600 {
log.Printf("[DEBUG] (download) Non-successful HTTP status code (%s) while making HEAD request: %s", resp.Status, err.Error())
} else {
log.Printf("[DEBUG] (download) Error making HTTP HEAD request (%s): %s", resp.Status, err.Error())
}
} else {
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
// If the HEAD request succeeded, then attempt to set the range
// query if we can.
if resp.Header.Get("Accept-Ranges") == "bytes" {
if fi, err := dst.Stat(); err == nil {
if _, err = dst.Seek(0, os.SEEK_END); err == nil {
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", fi.Size()))
current = fi.Size()
}
}
}
} else {
log.Printf("[DEBUG] (download) Unexpected HTTP response during HEAD request: %s", resp.Status)
}
}
// Set the request to GET now, and redo the query to download
req.Method = "GET"
resp, err = httpClient.Do(req)
if err == nil && (resp.StatusCode >= 400 && resp.StatusCode < 600) {
return fmt.Errorf("Error making HTTP GET request: %s", resp.Status)
} else if err != nil {
if resp == nil {
return fmt.Errorf("HTTP connection error: %s", err.Error())
} else if resp.StatusCode >= 400 && resp.StatusCode < 600 {
return fmt.Errorf("HTTP %s error: %s", resp.Status, err.Error())
}
return fmt.Errorf("HTTP error: %s", err.Error())
}
total := current + resp.ContentLength
bar := d.ProgressBar()
bar.Start(total)
defer bar.Finish()
bar.Add(current)
body := bar.NewProxyReader(resp.Body)
var buffer [4096]byte
for {
n, err := body.Read(buffer[:])
if err != nil && err != io.EOF {
return err
}
if _, werr := dst.Write(buffer[:n]); werr != nil {
return werr
}
if err == io.EOF {
break
}
}
return nil
}
// FileDownloader is an implementation of Downloader that downloads
// files using the regular filesystem.
type FileDownloader struct {
bufferSize *uint
active bool
Ui packer.Ui
}
func (d *FileDownloader) Cancel() {
d.active = false
}
func (d *FileDownloader) Resume() {
// TODO: Implement
}
func (d *FileDownloader) toPath(base string, uri url.URL) (string, error) {
var result string
// absolute path -- file://c:/absolute/path -> c:/absolute/path
if strings.HasSuffix(uri.Host, ":") {
result = path.Join(uri.Host, uri.Path)
// semi-absolute path (current drive letter)
// -- file:///absolute/path -> drive:/absolute/path
} else if uri.Host == "" && strings.HasPrefix(uri.Path, "/") {
apath := uri.Path
components := strings.Split(apath, "/")
volume := filepath.VolumeName(base)
// semi-absolute absolute path (includes volume letter)
// -- file://drive:/path -> drive:/absolute/path
if len(components) > 1 && strings.HasSuffix(components[1], ":") {
volume = components[1]
apath = path.Join(components[2:]...)
}
result = path.Join(volume, apath)
// relative path -- file://./relative/path -> ./relative/path
} else if uri.Host == "." {
result = path.Join(base, uri.Path)
// relative path -- file://relative/path -> ./relative/path
} else {
result = path.Join(base, uri.Host, uri.Path)
}
return filepath.ToSlash(result), nil
}
func (d *FileDownloader) Download(dst *os.File, src *url.URL) error {
d.active = false
/* check the uri's scheme to make sure it matches */
if src == nil || src.Scheme != "file" {
return fmt.Errorf("Unexpected uri scheme: %s", src.Scheme)
}
uri := src
/* use the current working directory as the base for relative uri's */
cwd, err := os.Getwd()
if err != nil {
return err
}
/* determine which uri format is being used and convert to a real path */
realpath, err := d.toPath(cwd, *uri)
if err != nil {
return err
}
/* download the file using the operating system's facilities */
d.active = true
f, err := os.Open(realpath)
if err != nil {
return err
}
defer f.Close()
// get the file size
fi, err := f.Stat()
if err != nil {
return err
}
bar := d.ProgressBar()
bar.Start(fi.Size())
defer bar.Finish()
fProxy := bar.NewProxyReader(f)
// no bufferSize specified, so copy synchronously.
if d.bufferSize == nil {
_, err = io.Copy(dst, fProxy)
d.active = false
// use a goro in case someone else wants to enable cancel/resume
} else {
errch := make(chan error)
go func(d *FileDownloader, r io.Reader, w io.Writer, e chan error) {
for d.active {
_, err := io.CopyN(w, r, int64(*d.bufferSize))
if err != nil {
break
}
}
d.active = false
e <- err
}(d, fProxy, dst, errch)
// ...and we spin until it's done
err = <-errch
}
return err
}
// SMBDownloader is an implementation of Downloader that downloads
// files using the "\\" path format on Windows
type SMBDownloader struct {
bufferSize *uint
active bool
Ui packer.Ui
}
func (d *SMBDownloader) Cancel() {
d.active = false
}
func (d *SMBDownloader) Resume() {
// TODO: Implement
}
func (d *SMBDownloader) toPath(base string, uri url.URL) (string, error) {
const UNCPrefix = string(os.PathSeparator) + string(os.PathSeparator)
if runtime.GOOS != "windows" {
return "", fmt.Errorf("Support for SMB based uri's are not supported on %s", runtime.GOOS)
}
return UNCPrefix + filepath.ToSlash(path.Join(uri.Host, uri.Path)), nil
}
func (d *SMBDownloader) Download(dst *os.File, src *url.URL) error {
/* first we warn the world if we're not running windows */
if runtime.GOOS != "windows" {
return fmt.Errorf("Support for SMB based uri's are not supported on %s", runtime.GOOS)
}
d.active = false
/* convert the uri using the net/url module to a UNC path */
if src == nil || src.Scheme != "smb" {
return fmt.Errorf("Unexpected uri scheme: %s", src.Scheme)
}
uri := src
/* use the current working directory as the base for relative uri's */
cwd, err := os.Getwd()
if err != nil {
return err
}
/* convert uri to an smb-path */
realpath, err := d.toPath(cwd, *uri)
if err != nil {
return err
}
/* Open up the "\\"-prefixed path using the Windows filesystem */
d.active = true
f, err := os.Open(realpath)
if err != nil {
return err
}
defer f.Close()
// get the file size (at the risk of performance)
fi, err := f.Stat()
if err != nil {
return err
}
bar := d.ProgressBar()
bar.Start(fi.Size())
defer bar.Finish()
fProxy := bar.NewProxyReader(f)
// no bufferSize specified, so copy synchronously.
if d.bufferSize == nil {
_, err = io.Copy(dst, fProxy)
d.active = false
// use a goro in case someone else wants to enable cancel/resume
} else {
errch := make(chan error)
go func(d *SMBDownloader, r io.Reader, w io.Writer, e chan error) {
for d.active {
_, err := io.CopyN(w, r, int64(*d.bufferSize))
if err != nil {
break
}
}
d.active = false
e <- err
}(d, fProxy, dst, errch)
// ...and as usual we spin until it's done
err = <-errch
}
return err
}
func (d *HTTPDownloader) ProgressBar() packer.ProgressBar { return d.Ui.ProgressBar() }
func (d *FileDownloader) ProgressBar() packer.ProgressBar { return d.Ui.ProgressBar() }
func (d *SMBDownloader) ProgressBar() packer.ProgressBar { return d.Ui.ProgressBar() }

View File

@ -1,535 +0,0 @@
package common
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
)
func TestDownloadClientVerifyChecksum(t *testing.T) {
ui := packer.TestUi(t)
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("tempfile error: %s", err)
}
defer os.Remove(tf.Name())
// "foo"
checksum, err := hex.DecodeString("acbd18db4cc2f85cedef654fccc4a4d8")
if err != nil {
t.Fatalf("decode err: %s", err)
}
// Write the file
tf.Write([]byte("foo"))
tf.Close()
config := &DownloadConfig{
Hash: md5.New(),
Checksum: checksum,
}
d := NewDownloadClient(config, new(packer.NoopUi))
result, err := d.VerifyChecksum(ui, tf.Name())
if err != nil {
t.Fatalf("Verify err: %s", err)
}
if !result {
t.Fatal("didn't verify")
}
}
func TestDownloadClient_basic(t *testing.T) {
ui := packer.TestUi(t)
tf, _ := ioutil.TempFile("", "packer")
tf.Close()
defer os.Remove(tf.Name())
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
defer ts.Close()
client := NewDownloadClient(&DownloadConfig{
Url: ts.URL + "/basic.txt",
TargetPath: tf.Name(),
CopyFile: true,
}, new(packer.NoopUi))
path, err := client.Get(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
raw, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("err: %s", err)
}
if string(raw) != "hello\n" {
t.Fatalf("bad: %s", string(raw))
}
}
func TestDownloadClient_checksumBad(t *testing.T) {
ui := packer.TestUi(t)
checksum, err := hex.DecodeString("b2946ac92492d2347c6235b4d2611184")
if err != nil {
t.Fatalf("err: %s", err)
}
tf, _ := ioutil.TempFile("", "packer")
tf.Close()
defer os.Remove(tf.Name())
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
defer ts.Close()
client := NewDownloadClient(&DownloadConfig{
Url: ts.URL + "/basic.txt",
TargetPath: tf.Name(),
Hash: HashForType("md5"),
Checksum: checksum,
CopyFile: true,
}, new(packer.NoopUi))
if _, err := client.Get(ui); err == nil {
t.Fatal("should error")
}
}
func TestDownloadClient_checksumGood(t *testing.T) {
ui := packer.TestUi(t)
checksum, err := hex.DecodeString("b1946ac92492d2347c6235b4d2611184")
if err != nil {
t.Fatalf("err: %s", err)
}
tf, _ := ioutil.TempFile("", "packer")
tf.Close()
defer os.Remove(tf.Name())
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
defer ts.Close()
client := NewDownloadClient(&DownloadConfig{
Url: ts.URL + "/basic.txt",
TargetPath: tf.Name(),
Hash: HashForType("md5"),
Checksum: checksum,
CopyFile: true,
}, new(packer.NoopUi))
path, err := client.Get(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
raw, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("err: %s", err)
}
if string(raw) != "hello\n" {
t.Fatalf("bad: %s", string(raw))
}
}
func TestDownloadClient_checksumNoDownload(t *testing.T) {
ui := packer.TestUi(t)
checksum, err := hex.DecodeString("3740570a423feec44c2a759225a9fcf9")
if err != nil {
t.Fatalf("err: %s", err)
}
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
defer ts.Close()
client := NewDownloadClient(&DownloadConfig{
Url: ts.URL + "/basic.txt",
TargetPath: "./test-fixtures/root/another.txt",
Hash: HashForType("md5"),
Checksum: checksum,
CopyFile: true,
}, new(packer.NoopUi))
path, err := client.Get(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
raw, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("err: %s", err)
}
// If this says "hello" it means we downloaded it. We faked out
// the downloader above by giving it the checksum for "another", but
// requested the download of "hello"
if string(raw) != "another\n" {
t.Fatalf("bad: %s", string(raw))
}
}
func TestDownloadClient_notFound(t *testing.T) {
ui := packer.TestUi(t)
tf, _ := ioutil.TempFile("", "packer")
tf.Close()
defer os.Remove(tf.Name())
ts := httptest.NewServer(http.FileServer(http.Dir("./test-fixtures/root")))
defer ts.Close()
client := NewDownloadClient(&DownloadConfig{
Url: ts.URL + "/not-found.txt",
TargetPath: tf.Name(),
}, new(packer.NoopUi))
if _, err := client.Get(ui); err == nil {
t.Fatal("should error")
}
}
func TestDownloadClient_resume(t *testing.T) {
ui := packer.TestUi(t)
tf, _ := ioutil.TempFile("", "packer")
tf.Write([]byte("w"))
tf.Close()
defer os.Remove(tf.Name())
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
rw.Header().Set("Accept-Ranges", "bytes")
rw.WriteHeader(204)
return
}
http.ServeFile(rw, r, "./test-fixtures/root/basic.txt")
}))
defer ts.Close()
client := NewDownloadClient(&DownloadConfig{
Url: ts.URL,
TargetPath: tf.Name(),
CopyFile: true,
}, new(packer.NoopUi))
path, err := client.Get(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
raw, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("err: %s", err)
}
if string(raw) != "wello\n" {
t.Fatalf("bad: %s", string(raw))
}
}
func TestDownloadClient_usesDefaultUserAgent(t *testing.T) {
ui := packer.TestUi(t)
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("tempfile error: %s", err)
}
tf.Close()
defer os.Remove(tf.Name())
defaultUserAgent := ""
asserted := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if defaultUserAgent == "" {
defaultUserAgent = r.UserAgent()
} else {
incomingUserAgent := r.UserAgent()
if incomingUserAgent != defaultUserAgent {
t.Fatalf("Expected user agent %s, got: %s", defaultUserAgent, incomingUserAgent)
}
asserted = true
}
}))
req, err := http.NewRequest("GET", server.URL, nil)
if err != nil {
t.Fatal(err)
}
httpClient := commonhelper.HttpClientWithEnvironmentProxy()
_, err = httpClient.Do(req)
if err != nil {
t.Fatal(err)
}
config := &DownloadConfig{
Url: server.URL,
TargetPath: tf.Name(),
CopyFile: true,
}
client := NewDownloadClient(config, new(packer.NoopUi))
_, err = client.Get(ui)
if err != nil {
t.Fatal(err)
}
if !asserted {
t.Fatal("User-Agent never observed")
}
}
func TestDownloadClient_setsUserAgent(t *testing.T) {
ui := packer.TestUi(t)
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("tempfile error: %s", err)
}
tf.Close()
defer os.Remove(tf.Name())
asserted := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
asserted = true
if r.UserAgent() != "fancy user agent" {
t.Fatalf("Expected useragent fancy user agent, got: %s", r.UserAgent())
}
}))
config := &DownloadConfig{
Url: server.URL,
TargetPath: tf.Name(),
UserAgent: "fancy user agent",
CopyFile: true,
}
client := NewDownloadClient(config, new(packer.NoopUi))
_, err = client.Get(ui)
if err != nil {
t.Fatal(err)
}
if !asserted {
t.Fatal("HTTP request never made")
}
}
func TestHashForType(t *testing.T) {
if h := HashForType("md5"); h == nil {
t.Fatalf("md5 hash is nil")
} else {
h.Write([]byte("foo"))
result := h.Sum(nil)
expected := "acbd18db4cc2f85cedef654fccc4a4d8"
actual := hex.EncodeToString(result)
if actual != expected {
t.Fatalf("bad hash: %s", actual)
}
}
if h := HashForType("sha1"); h == nil {
t.Fatalf("sha1 hash is nil")
} else {
h.Write([]byte("foo"))
result := h.Sum(nil)
expected := "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
actual := hex.EncodeToString(result)
if actual != expected {
t.Fatalf("bad hash: %s", actual)
}
}
if h := HashForType("sha256"); h == nil {
t.Fatalf("sha256 hash is nil")
} else {
h.Write([]byte("foo"))
result := h.Sum(nil)
expected := "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
actual := hex.EncodeToString(result)
if actual != expected {
t.Fatalf("bad hash: %s", actual)
}
}
if h := HashForType("sha512"); h == nil {
t.Fatalf("sha512 hash is nil")
} else {
h.Write([]byte("foo"))
result := h.Sum(nil)
expected := "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7"
actual := hex.EncodeToString(result)
if actual != expected {
t.Fatalf("bad hash: %s", actual)
}
}
if HashForType("fake") != nil {
t.Fatalf("fake hash is not nil")
}
}
// TestDownloadFileUrl tests a special case where we use a local file for
// iso_url. In this case we can still verify the checksum but we should not
// delete the file if the checksum fails. Instead we'll just error and let the
// user fix the checksum.
func TestDownloadFileUrl(t *testing.T) {
ui := packer.TestUi(t)
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("Unable to detect working directory: %s", err)
}
cwd = filepath.ToSlash(cwd)
// source_path is a file path and source is a network path
sourcePath := fmt.Sprintf("%s/test-fixtures/fileurl/%s", cwd, "cake")
filePrefix := "file://"
if runtime.GOOS == "windows" {
filePrefix += "/"
}
source := fmt.Sprintf(filePrefix + sourcePath)
t.Logf("Trying to download %s", source)
config := &DownloadConfig{
Url: source,
// This should be wrong. We want to make sure we don't delete
Checksum: []byte("nope"),
Hash: HashForType("sha256"),
CopyFile: false,
}
client := NewDownloadClient(config, new(packer.NoopUi))
// Verify that we fail to match the checksum
_, err = client.Get(ui)
if err.Error() != "checksums didn't match. expected 6e6f7065 and got 606f1945f81a022d0ed0bd99edfd4f99081c1cb1f97fae087291ee14e945e608" {
t.Fatalf("Unexpected failure; expected checksum not to match. Error was \"%v\"", err)
}
if _, err = os.Stat(sourcePath); err != nil {
t.Errorf("Could not stat source file: %s", sourcePath)
}
}
// SimulateFileUriDownload is a simple utility function that converts a uri
// into a testable file path whilst ignoring a correct checksum match, stripping
// UNC path info, and then calling stat to ensure the correct file exists.
// (used by TestFileUriTransforms)
func SimulateFileUriDownload(t *testing.T, uri string) (string, error) {
ui := packer.TestUi(t)
// source_path is a file path and source is a network path
source := fmt.Sprintf(uri)
t.Logf("Trying to download %s", source)
config := &DownloadConfig{
Url: source,
// This should be wrong. We want to make sure we don't delete
Checksum: []byte("nope"),
Hash: HashForType("sha256"),
CopyFile: false,
}
// go go go
client := NewDownloadClient(config, new(packer.NoopUi))
path, err := client.Get(ui)
// ignore any non-important checksum errors if it's not a unc path
if !strings.HasPrefix(path, "\\\\") && err.Error() != "checksums didn't match. expected 6e6f7065 and got 606f1945f81a022d0ed0bd99edfd4f99081c1cb1f97fae087291ee14e945e608" {
t.Fatalf("Unexpected failure; expected checksum not to match")
}
// if it's a unc path, then remove the host and share name so we don't have
// to force the user to enable ADMIN$ and Windows File Sharing
if strings.HasPrefix(path, "\\\\") {
res := strings.SplitN(path, "/", 3)
path = "/" + res[2]
}
if _, err = os.Stat(path); err != nil {
t.Errorf("Could not stat source file: %s", path)
}
return path, err
}
// TestFileUriTransforms tests the case where we use a local file uri
// for iso_url. There's a few different formats that a file uri can exist as
// and so we try to test the most useful and common ones.
func TestFileUriTransforms(t *testing.T) {
const testpath = /* have your */ "test-fixtures/fileurl/cake" /* and eat it too */
const host = "localhost"
var cwd string
var volume string
var share string
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("Unable to detect working directory: %s", err)
return
}
cwd = filepath.ToSlash(cwd)
volume = filepath.VolumeName(cwd)
share = volume
// if a volume was found (on windows), replace the ':' from
// C: to C$ to convert it into a hidden windows share.
if len(share) > 1 && share[len(share)-1] == ':' {
share = share[:len(share)-1] + "$"
}
cwd = cwd[len(volume):]
t.Logf("TestFileUriTransforms : Running with cwd : '%s'", cwd)
t.Logf("TestFileUriTransforms : Running with volume : '%s'", volume)
// ./relative/path -> ./relative/path
// /absolute/path -> /absolute/path
// c:/windows/absolute -> c:/windows/absolute
testcases := []string{
"./%s",
cwd + "/%s",
volume + cwd + "/%s",
}
// all regular slashed testcases
for _, testcase := range testcases {
uri := "file://" + fmt.Sprintf(testcase, testpath)
t.Logf("TestFileUriTransforms : Trying Uri '%s'", uri)
res, err := SimulateFileUriDownload(t, uri)
if err != nil {
t.Errorf("Unable to transform uri '%s' into a path : %v", uri, err)
}
t.Logf("TestFileUriTransforms : Result Path '%s'", res)
}
// smb protocol depends on platform support which currently
// only exists on windows.
if runtime.GOOS == "windows" {
// ...and finally the oddball windows native path
// smb://host/sharename/file -> \\host\sharename\file
testcase := host + "/" + share + "/" + cwd[1:] + "/%s"
uri := "smb://" + fmt.Sprintf(testcase, testpath)
t.Logf("TestFileUriTransforms : Trying Uri '%s'", uri)
res, err := SimulateFileUriDownload(t, uri)
if err != nil {
t.Errorf("Unable to transform uri '%s' into a path", uri)
return
}
t.Logf("TestFileUriTransforms : Result Path '%s'", res)
}
}

View File

@ -1,15 +1,8 @@
package common
import (
"bufio"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/hashicorp/packer/template/interpolate"
@ -27,103 +20,24 @@ type ISOConfig struct {
}
func (c *ISOConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs []error) {
if c.RawSingleISOUrl == "" && len(c.ISOUrls) == 0 {
if len(c.ISOUrls) != 0 && c.RawSingleISOUrl != "" {
errs = append(
errs, errors.New("One of iso_url or iso_urls must be specified."))
errs, errors.New("Only one of iso_url or iso_urls must be specified"))
return
} else if c.RawSingleISOUrl != "" && len(c.ISOUrls) > 0 {
}
if c.RawSingleISOUrl != "" {
// make sure only array is set
c.ISOUrls = append([]string{c.RawSingleISOUrl}, c.ISOUrls...)
c.RawSingleISOUrl = ""
}
if len(c.ISOUrls) == 0 {
errs = append(
errs, errors.New("Only one of iso_url or iso_urls may be specified."))
errs, errors.New("One of iso_url or iso_urls must be specified"))
return
} else if c.RawSingleISOUrl != "" {
c.ISOUrls = []string{c.RawSingleISOUrl}
}
if c.ISOChecksumType == "" {
errs = append(
errs, errors.New("The iso_checksum_type must be specified."))
} else {
c.ISOChecksumType = strings.ToLower(c.ISOChecksumType)
if c.ISOChecksumType != "none" {
if c.ISOChecksum == "" && c.ISOChecksumURL == "" {
errs = append(
errs, errors.New("Due to large file sizes, an iso_checksum is required"))
return warnings, errs
} else {
if h := HashForType(c.ISOChecksumType); h == nil {
errs = append(
errs, fmt.Errorf("Unsupported checksum type: %s", c.ISOChecksumType))
return warnings, errs
}
// If iso_checksum has no value use iso_checksum_url instead.
if c.ISOChecksum == "" {
if strings.HasSuffix(strings.ToLower(c.ISOChecksumURL), ".iso") {
errs = append(errs, fmt.Errorf("Error parsing checksum:"+
" .iso is not a valid checksum extension"))
return warnings, errs
}
u, err := url.Parse(c.ISOChecksumURL)
if err != nil {
errs = append(errs,
fmt.Errorf("Error parsing checksum: %s", err))
return warnings, errs
}
switch u.Scheme {
case "http", "https":
res, err := http.Get(c.ISOChecksumURL)
c.ISOChecksum = ""
if err != nil {
errs = append(errs,
fmt.Errorf("Error getting checksum from url: %s", c.ISOChecksumURL))
return warnings, errs
}
defer res.Body.Close()
err = c.parseCheckSumFile(bufio.NewReader(res.Body))
if err != nil {
errs = append(errs, err)
return warnings, errs
}
case "file":
path := u.Path
if runtime.GOOS == "windows" && len(path) > 2 && path[0] == '/' && path[2] == ':' {
path = strings.TrimLeft(path, "/")
}
file, err := os.Open(path)
if err != nil {
errs = append(errs, err)
return warnings, errs
}
err = c.parseCheckSumFile(bufio.NewReader(file))
if err != nil {
errs = append(errs, err)
return warnings, errs
}
case "":
break
default:
errs = append(errs,
fmt.Errorf("Error parsing checksum url: %s, scheme not supported: %s", c.ISOChecksumURL, u.Scheme))
return warnings, errs
}
}
}
}
}
c.ISOChecksum = strings.ToLower(c.ISOChecksum)
for i, url := range c.ISOUrls {
url, err := ValidatedURL(url)
if err != nil {
errs = append(
errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err))
} else {
c.ISOUrls[i] = url
}
}
c.ISOChecksumType = strings.ToLower(c.ISOChecksumType)
if c.TargetExtension == "" {
c.TargetExtension = "iso"
@ -135,69 +49,22 @@ func (c *ISOConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs [
warnings = append(warnings,
"A checksum type of 'none' was specified. Since ISO files are so big,\n"+
"a checksum is highly recommended.")
return warnings, errs
}
if c.ISOChecksumURL != "" {
if strings.HasSuffix(strings.ToLower(c.ISOChecksumURL), ".iso") {
errs = append(errs, fmt.Errorf("Error parsing checksum:"+
" .iso is not a valid checksum extension"))
}
// go-getter auto-parses checksum files
c.ISOChecksumType = "file"
c.ISOChecksum = c.ISOChecksumURL
}
if c.ISOChecksum == "" {
errs = append(errs, fmt.Errorf("A checksum must be specified"))
}
return warnings, errs
}
func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error {
u, err := url.Parse(c.ISOUrls[0])
if err != nil {
return err
}
checksumurl, err := url.Parse(c.ISOChecksumURL)
if err != nil {
return err
}
absPath, err := filepath.Abs(u.Path)
if err != nil {
log.Printf("Unable to generate absolute path from provided iso_url: %s", err)
absPath = ""
}
relpath, err := filepath.Rel(filepath.Dir(checksumurl.Path), absPath)
if err != nil {
log.Printf("Unable to determine relative pathing; continuing with abspath.")
relpath = ""
}
filename := filepath.Base(u.Path)
errNotFound := fmt.Errorf("No checksum for %q, %q or %q found at: %s",
filename, relpath, u.Path, c.ISOChecksumURL)
for {
line, err := rd.ReadString('\n')
if err != nil && line == "" {
break
}
parts := strings.Fields(line)
if len(parts) < 2 {
continue
}
options := []string{filename, relpath, "./" + relpath, absPath}
if strings.ToLower(parts[0]) == c.ISOChecksumType {
// BSD-style checksum
for _, match := range options {
if parts[1] == fmt.Sprintf("(%s)", match) {
c.ISOChecksum = parts[3]
return nil
}
}
} else {
// Standard checksum
if parts[1][0] == '*' {
// Binary mode
parts[1] = parts[1][1:]
}
for _, match := range options {
if parts[1] == match {
c.ISOChecksum = parts[0]
return nil
}
}
}
}
return errNotFound
}

View File

@ -3,13 +3,9 @@
package common
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"reflect"
"runtime"
"testing"
)
@ -74,12 +70,9 @@ func TestISOConfigPrepare_ISOChecksum(t *testing.T) {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "foo" {
t.Fatalf("should've lowercased: %s", i.ISOChecksum)
}
}
func TestISOConfigPrepare_ISOChecksumURL(t *testing.T) {
func TestISOConfigPrepare_ISOChecksumURLBad(t *testing.T) {
i := testISOConfig()
i.ISOChecksumURL = "file:///not_read"
@ -89,157 +82,6 @@ func TestISOConfigPrepare_ISOChecksumURL(t *testing.T) {
t.Fatalf("bad: %#v, %#v", warns, err)
}
// Test good - ISOChecksumURL BSD style
i = testISOConfig()
i.ISOChecksum = ""
cs_file, _ := ioutil.TempFile("", "packer-test-")
defer os.Remove(cs_file.Name())
defer cs_file.Close()
ioutil.WriteFile(cs_file.Name(), []byte(cs_bsd_style), 0666)
filePrefix := "file://"
if runtime.GOOS == "windows" {
filePrefix += "/"
}
i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name())
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "baz" {
t.Fatalf("should've found \"baz\" got: %s", i.ISOChecksum)
}
// Test good - ISOChecksumURL GNU style
i = testISOConfig()
i.ISOChecksum = ""
cs_file, _ = ioutil.TempFile("", "packer-test-")
defer os.Remove(cs_file.Name())
defer cs_file.Close()
ioutil.WriteFile(cs_file.Name(), []byte(cs_gnu_style), 0666)
i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name())
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "bar0" {
t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum)
}
// Test good - ISOChecksumURL BSD style no newline
i = testISOConfig()
i.ISOChecksum = ""
cs_file, _ = ioutil.TempFile("", "packer-test-")
defer os.Remove(cs_file.Name())
defer cs_file.Close()
ioutil.WriteFile(cs_file.Name(), []byte(cs_bsd_style_no_newline), 0666)
i.ISOChecksumURL = fmt.Sprintf("file://%s", cs_file.Name())
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "baz" {
t.Fatalf("should've found \"baz\" got: %s", i.ISOChecksum)
}
// Test good - ISOChecksumURL BSD style with relative path
i = testISOConfig()
i.ISOChecksum = ""
cs_dir, _ := ioutil.TempDir("", "packer-testdir-")
cs_file, _ = ioutil.TempFile(cs_dir, "packer-test-")
defer os.RemoveAll(cs_dir) // Removes the file as well
defer cs_file.Close()
ioutil.WriteFile(cs_file.Name(), []byte(cs_bsd_style_subdir), 0666)
i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name())
i.RawSingleISOUrl = fmt.Sprintf("%s%s", cs_dir, "/subdir/the-OS.iso")
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "baz" {
t.Fatalf("should've found \"baz\" got: %s", i.ISOChecksum)
}
// Test good - ISOChecksumURL GNU style no newline
i = testISOConfig()
i.ISOChecksum = ""
cs_file, _ = ioutil.TempFile("", "packer-test-")
defer os.Remove(cs_file.Name())
defer cs_file.Close()
ioutil.WriteFile(cs_file.Name(), []byte(cs_gnu_style_no_newline), 0666)
i.ISOChecksumURL = fmt.Sprintf("file://%s", cs_file.Name())
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "bar0" {
t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum)
}
// Test good - ISOChecksumURL GNU style with query parameters
i = testISOConfig()
i.ISOChecksum = ""
i.RawSingleISOUrl = "http://www.packer.io/the-OS.iso?stuff=boo"
cs_file, _ = ioutil.TempFile("", "packer-test-")
defer os.Remove(cs_file.Name())
defer cs_file.Close()
ioutil.WriteFile(cs_file.Name(), []byte(cs_gnu_style), 0666)
i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name())
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "bar0" {
t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum)
}
// Test good - ISOChecksumURL GNU style with relative path
i = testISOConfig()
i.ISOChecksum = ""
cs_file, _ = ioutil.TempFile(cs_dir, "packer-test-")
defer os.Remove(cs_file.Name())
defer cs_file.Close()
ioutil.WriteFile(cs_file.Name(), []byte(cs_gnu_style_subdir), 0666)
i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name())
i.RawSingleISOUrl = fmt.Sprintf("%s%s", cs_dir, "/subdir/the-OS.iso")
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if i.ISOChecksum != "bar0" {
t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum)
}
// Test that we won't try to read an iso into memory because of a user
// error
i = testISOConfig()
@ -249,6 +91,7 @@ func TestISOConfigPrepare_ISOChecksumURL(t *testing.T) {
if err == nil {
t.Fatalf("should have error because iso is bad filetype: %s", err)
}
}
func TestISOConfigPrepare_ISOChecksumType(t *testing.T) {
@ -260,8 +103,8 @@ func TestISOConfigPrepare_ISOChecksumType(t *testing.T) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test good
@ -279,17 +122,6 @@ func TestISOConfigPrepare_ISOChecksumType(t *testing.T) {
t.Fatalf("should've lowercased: %s", i.ISOChecksumType)
}
// Test unknown
i = testISOConfig()
i.ISOChecksumType = "fake"
warns, err = i.Prepare(nil)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test none
i = testISOConfig()
i.ISOChecksumType = "none"

View File

@ -6,10 +6,12 @@ import (
"encoding/hex"
"fmt"
"log"
"time"
"os"
"github.com/gofrs/flock"
getter "github.com/hashicorp/go-getter"
urlhelper "github.com/hashicorp/go-getter/helper/url"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/helper/useragent"
"github.com/hashicorp/packer/packer"
)
@ -47,129 +49,114 @@ type StepDownload struct {
Extension string
}
func (s *StepDownload) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
cache := state.Get("cache").(packer.Cache)
func (s *StepDownload) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
var checksum []byte
if s.Checksum != "" {
var err error
checksum, err = hex.DecodeString(s.Checksum)
if err != nil {
state.Put("error", fmt.Errorf("Error parsing checksum: %s", err))
return multistep.ActionHalt
}
}
defer ui.Say(fmt.Sprintf("leaving retrieve loop for %s", s.Description))
ui.Say(fmt.Sprintf("Retrieving %s", s.Description))
// First try to use any already downloaded file
// If it fails, proceed to regular download logic
var downloadConfigs = make([]*DownloadConfig, len(s.Url))
var finalPath string
for i, url := range s.Url {
targetPath := s.TargetPath
if targetPath == "" {
// Determine a cache key. This is normally just the URL but
// if we force a certain extension we hash the URL and add
// the extension to force it.
cacheKey := url
if s.Extension != "" {
hash := sha1.Sum([]byte(url))
cacheKey = fmt.Sprintf(
"%s.%s", hex.EncodeToString(hash[:]), s.Extension)
}
log.Printf("Acquiring lock to download: %s", url)
targetPath = cache.Lock(cacheKey)
defer cache.Unlock(cacheKey)
var errs []error
for _, source := range s.Url {
if ctx.Err() != nil {
state.Put("error", fmt.Errorf("Download cancelled: %v", errs))
return multistep.ActionHalt
}
config := &DownloadConfig{
Url: url,
TargetPath: targetPath,
CopyFile: false,
Hash: HashForType(s.ChecksumType),
Checksum: checksum,
UserAgent: useragent.String(),
}
downloadConfigs[i] = config
if match, _ := NewDownloadClient(config, ui).VerifyChecksum(ui, config.TargetPath); match {
ui.Message(fmt.Sprintf("Found already downloaded, initial checksum matched, no download needed: %s", url))
finalPath = config.TargetPath
break
ui.Say(fmt.Sprintf("Trying %s", source))
dst, err := s.download(ctx, ui, source)
if err == nil {
state.Put(s.ResultKey, dst)
return multistep.ActionContinue
}
// may be another url will work
errs = append(errs, err)
}
if finalPath == "" {
for i := range s.Url {
config := downloadConfigs[i]
state.Put("error", fmt.Errorf("Downloading file: %v", errs))
return multistep.ActionHalt
}
path, err, retry := s.download(config, state)
if err != nil {
ui.Message(fmt.Sprintf("Error downloading: %s", err))
}
func (s *StepDownload) download(ctx context.Context, ui packer.Ui, source string) (string, error) {
u, err := urlhelper.Parse(source)
if err != nil {
return "", fmt.Errorf("url parse: %s", err)
}
if checksum := u.Query().Get("checksum"); checksum != "" {
s.Checksum = checksum
}
if s.ChecksumType != "" && s.ChecksumType != "none" {
// add checksum to url query params as go getter will checksum for us
q := u.Query()
q.Set("checksum", s.ChecksumType+":"+s.Checksum)
u.RawQuery = q.Encode()
} else if s.Checksum != "" {
q := u.Query()
q.Set("checksum", s.Checksum)
u.RawQuery = q.Encode()
} else if s.ChecksumType != "none" {
return "", fmt.Errorf("Empty checksum")
}
if !retry {
return multistep.ActionHalt
}
if err == nil {
finalPath = path
break
}
targetPath := s.TargetPath
if targetPath == "" {
// store file under sha1(hash) if set
// hash can sometimes be a checksum url
// otherwise, use sha1(source_url)
var shaSum [20]byte
if s.Checksum != "" {
shaSum = sha1.Sum([]byte(s.Checksum))
} else {
shaSum = sha1.Sum([]byte(u.String()))
}
targetPath = hex.EncodeToString(shaSum[:])
if s.Extension != "" {
targetPath += "." + s.Extension
}
}
targetPath, err = packer.CachePath(targetPath)
if err != nil {
return "", fmt.Errorf("CachePath: %s", err)
}
lockFile := targetPath + ".lock"
if finalPath == "" {
err := fmt.Errorf("%s download failed.", s.Description)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
log.Printf("Acquiring lock for: %s (%s)", u.String(), lockFile)
lock := flock.New(lockFile)
lock.Lock()
defer lock.Unlock()
wd, err := os.Getwd()
if err != nil {
log.Printf("get working directory: %v", err)
// here we ignore the error in case the
// working directory is not needed.
// It would be better if the go-getter
// could guess it only in cases it is
// necessary.
}
state.Put(s.ResultKey, finalPath)
return multistep.ActionContinue
ui.Say(fmt.Sprintf("Trying %s", u.String()))
gc := getter.Client{
Ctx: ctx,
Dst: targetPath,
Src: u.String(),
ProgressListener: ui,
Pwd: wd,
Dir: false,
}
switch err := gc.Get(); err.(type) {
case nil: // success !
ui.Say(fmt.Sprintf("%s => %s", u.String(), targetPath))
return targetPath, nil
case *getter.ChecksumError:
ui.Say(fmt.Sprintf("Checksum did not match, removing %s", targetPath))
if err := os.Remove(targetPath); err != nil {
ui.Error(fmt.Sprintf("Failed to remove cache file. Please remove manually: %s", targetPath))
}
return "", err
default:
ui.Say(fmt.Sprintf("Download failed %s", err))
return "", err
}
}
func (s *StepDownload) Cleanup(multistep.StateBag) {}
func (s *StepDownload) download(config *DownloadConfig, state multistep.StateBag) (string, error, bool) {
var path string
ui := state.Get("ui").(packer.Ui)
// Create download client with config
download := NewDownloadClient(config, ui)
downloadCompleteCh := make(chan error, 1)
go func() {
var err error
path, err = download.Get(ui)
downloadCompleteCh <- err
}()
for {
select {
case err := <-downloadCompleteCh:
if err != nil {
return "", err, true
}
if download.config.CopyFile {
ui.Message(fmt.Sprintf("Transferred: %s", config.Url))
} else {
ui.Message(fmt.Sprintf("Using file in-place: %s", config.Url))
}
return path, nil, true
case <-time.After(1 * time.Second):
if _, ok := state.GetOk(multistep.StateCancelled); ok {
ui.Say("Interrupt received. Cancelling download...")
return "", nil, false
}
}
}
}

View File

@ -1,15 +1,254 @@
package common
import (
"context"
"crypto/sha1"
"encoding/hex"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
urlhelper "github.com/hashicorp/go-getter/helper/url"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer/tmp"
)
func TestStepDownload_Impl(t *testing.T) {
var raw interface{}
raw = new(StepDownload)
if _, ok := raw.(multistep.Step); !ok {
t.Fatalf("download should be a step")
var _ multistep.Step = new(StepDownload)
func toSha1(in string) string {
b := sha1.Sum([]byte(in))
return hex.EncodeToString(b[:])
}
func abs(t *testing.T, path string) string {
path, err := filepath.Abs(path)
if err != nil {
t.Fatal(err)
}
u, err := urlhelper.Parse(path)
if err != nil {
t.Fatal(err)
}
return u.String()
}
func TestStepDownload_Run(t *testing.T) {
srvr := httptest.NewServer(http.FileServer(http.Dir("test-fixtures")))
defer srvr.Close()
cs := map[string]string{
"/root/basic.txt": "f572d396fae9206628714fb2ce00f72e94f2258f",
"/root/another.txt": "7c6e5dd1bacb3b48fdffba2ed096097eb172497d",
}
type fields struct {
Checksum string
ChecksumType string
Description string
ResultKey string
TargetPath string
Url []string
Extension string
}
tests := []struct {
name string
fields fields
want multistep.StepAction
wantFiles []string
}{
{"not passing a checksum fails",
fields{Url: []string{abs(t, "./test-fixtures/root/another.txt")}},
multistep.ActionHalt,
nil,
},
{"none checksum works, without a checksum",
fields{Url: []string{abs(t, "./test-fixtures/root/another.txt")}, ChecksumType: "none"},
multistep.ActionContinue,
[]string{
toSha1(abs(t, "./test-fixtures/root/another.txt")),
toSha1(abs(t, "./test-fixtures/root/another.txt")) + ".lock",
},
},
{"bad checksum removes file - checksum from string - no Checksum Type",
fields{Extension: "txt", Url: []string{abs(t, "./test-fixtures/root/another.txt")}, Checksum: cs["/root/basic.txt"]},
multistep.ActionHalt,
[]string{
toSha1(cs["/root/basic.txt"]) + ".txt.lock", // a lock file is created & deleted on mac for each download
},
},
{"bad checksum removes file - checksum from string - Checksum Type",
fields{Extension: "txt", Url: []string{abs(t, "./test-fixtures/root/another.txt")}, ChecksumType: "sha1", Checksum: cs["/root/basic.txt"]},
multistep.ActionHalt,
[]string{
toSha1(cs["/root/basic.txt"]) + ".txt.lock",
},
},
{"bad checksum removes file - checksum from url - Checksum Type",
fields{Extension: "txt", Url: []string{abs(t, "./test-fixtures/root/basic.txt")}, Checksum: srvr.URL + "/root/another.txt.sha1sum", ChecksumType: "file"},
multistep.ActionHalt,
[]string{
toSha1(srvr.URL+"/root/another.txt.sha1sum") + ".txt.lock",
},
},
{"successfull http dl - checksum from http file - parameter",
fields{Extension: "txt", Url: []string{srvr.URL + "/root/another.txt"}, Checksum: srvr.URL + "/root/another.txt.sha1sum", ChecksumType: "file"},
multistep.ActionContinue,
[]string{
toSha1(srvr.URL+"/root/another.txt.sha1sum") + ".txt",
toSha1(srvr.URL+"/root/another.txt.sha1sum") + ".txt.lock",
},
},
{"successfull http dl - checksum from http file - url",
fields{Extension: "txt", Url: []string{srvr.URL + "/root/another.txt?checksum=file:" + srvr.URL + "/root/another.txt.sha1sum"}},
multistep.ActionContinue,
[]string{
toSha1("file:"+srvr.URL+"/root/another.txt.sha1sum") + ".txt",
toSha1("file:"+srvr.URL+"/root/another.txt.sha1sum") + ".txt.lock",
},
},
{"successfull http dl - checksum from url",
fields{Extension: "txt", Url: []string{srvr.URL + "/root/another.txt?checksum=" + cs["/root/another.txt"]}},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull http dl - checksum from parameter - no checksum type",
fields{Extension: "txt", Url: []string{srvr.URL + "/root/another.txt?"}, Checksum: cs["/root/another.txt"]},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull http dl - checksum from parameter - checksum type",
fields{Extension: "txt", Url: []string{srvr.URL + "/root/another.txt?"}, ChecksumType: "sha1", Checksum: cs["/root/another.txt"]},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull relative symlink - checksum from url",
fields{Extension: "txt", Url: []string{"./test-fixtures/root/another.txt?checksum=" + cs["/root/another.txt"]}},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull relative symlink - checksum from parameter - no checksum type",
fields{Extension: "txt", Url: []string{"./test-fixtures/root/another.txt?"}, Checksum: cs["/root/another.txt"]},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull relative symlink - checksum from parameter - checksum type",
fields{Extension: "txt", Url: []string{"./test-fixtures/root/another.txt?"}, ChecksumType: "sha1", Checksum: cs["/root/another.txt"]},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull absolute symlink - checksum from url",
fields{Extension: "txt", Url: []string{abs(t, "./test-fixtures/root/another.txt") + "?checksum=" + cs["/root/another.txt"]}},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull absolute symlink - checksum from parameter - no checksum type",
fields{Extension: "txt", Url: []string{abs(t, "./test-fixtures/root/another.txt") + "?"}, Checksum: cs["/root/another.txt"]},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"successfull absolute symlink - checksum from parameter - checksum type",
fields{Extension: "txt", Url: []string{abs(t, "./test-fixtures/root/another.txt") + "?"}, ChecksumType: "sha1", Checksum: cs["/root/another.txt"]},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/another.txt"]) + ".txt",
toSha1(cs["/root/another.txt"]) + ".txt.lock",
},
},
{"wrong first 2 urls - absolute urls - checksum from parameter - no checksum type",
fields{
Url: []string{
abs(t, "./test-fixtures/root/another.txt"),
abs(t, "./test-fixtures/root/not_found"),
abs(t, "./test-fixtures/root/basic.txt"),
},
Checksum: cs["/root/basic.txt"],
},
multistep.ActionContinue,
[]string{
toSha1(cs["/root/basic.txt"]),
toSha1(cs["/root/basic.txt"]) + ".lock",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dir := createTempDir(t)
defer os.RemoveAll(dir)
s := &StepDownload{
TargetPath: tt.fields.TargetPath,
Checksum: tt.fields.Checksum,
ChecksumType: tt.fields.ChecksumType,
ResultKey: tt.fields.ResultKey,
Url: tt.fields.Url,
Extension: tt.fields.Extension,
Description: tt.name,
}
defer os.Setenv("PACKER_CACHE_DIR", os.Getenv("PACKER_CACHE_DIR"))
os.Setenv("PACKER_CACHE_DIR", dir)
if got := s.Run(context.Background(), testState(t)); !reflect.DeepEqual(got, tt.want) {
t.Fatalf("StepDownload.Run() = %v, want %v", got, tt.want)
}
files := listFiles(t, dir)
if diff := cmp.Diff(tt.wantFiles, files); diff != "" {
t.Fatalf("file list differs in %s: %s", dir, diff)
}
})
}
}
func createTempDir(t *testing.T) string {
dir, err := tmp.Dir("pkr")
if err != nil {
t.Fatalf("err: %s", err)
}
return dir
}
func listFiles(t *testing.T, dir string) []string {
fs, err := ioutil.ReadDir(dir)
if err != nil {
log.Fatal(err)
}
var files []string
for _, file := range fs {
if file.Name() == "." {
continue
}
files = append(files, file.Name())
}
return files
}

View File

@ -0,0 +1 @@
7c6e5dd1bacb3b48fdffba2ed096097eb172497d another.txt

View File

@ -0,0 +1 @@
f572d396fae9206628714fb2ce00f72e94f2258f basic.txt

39
go.mod
View File

@ -6,7 +6,6 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Azure/go-autorest v10.12.0+incompatible
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 // indirect
github.com/Bowery/prompt v0.0.0-20180817134258-8a1d5376df1c // indirect
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290
github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 // indirect
github.com/Jeffail/gabs v1.1.1 // indirect
@ -31,12 +30,11 @@ require (
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/cenkalti/backoff v2.1.0+incompatible // indirect
github.com/cheggaaa/pb v1.0.26
github.com/cheggaaa/pb v1.0.27
github.com/circonus-labs/circonus-gometrics v2.2.5+incompatible // indirect
github.com/circonus-labs/circonusllhist v0.1.3 // indirect
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect
github.com/creack/goselect v0.0.0-20180210034346-528c74964609 // indirect
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f // indirect
github.com/denverdino/aliyungo v0.0.0-20190220033614-36e2ae938978
github.com/dgrijalva/jwt-go v3.2.0+incompatible
@ -46,22 +44,19 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.3.3 // indirect
github.com/duosecurity/duo_api_golang v0.0.0-20181210160733-61e0defebf22 // indirect
github.com/dustin/go-humanize v0.0.0-20170228161531-259d2a102b87 // indirect
github.com/dylanmei/iso8601 v0.1.0 // indirect
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/go-ini/ini v1.25.4
github.com/go-ldap/ldap v2.5.1+incompatible // indirect
github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/go-test/deep v1.0.1 // indirect
github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b // indirect
github.com/gofrs/flock v0.7.0
github.com/gogo/protobuf v1.2.0 // indirect
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/google/go-cmp v0.0.0-20180328201512-5411ab924f9f
github.com/google/go-github v17.0.0+incompatible // indirect
github.com/google/go-querystring v0.0.0-20151028211038-2a60fc2ba6c1 // indirect
github.com/google/go-cmp v0.2.0
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06
@ -72,7 +67,8 @@ require (
github.com/hashicorp/consul v0.0.0-20180807174550-3e6313bebbf0
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
github.com/hashicorp/go-cleanhttp v0.0.0-20160217214820-875fb671b3dd
github.com/hashicorp/go-cleanhttp v0.5.0
github.com/hashicorp/go-getter v1.2.0
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f // indirect
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
github.com/hashicorp/go-memdb v0.0.0-20181108192425-032f93b25bec // indirect
@ -84,7 +80,7 @@ require (
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
github.com/hashicorp/go-uuid v0.0.0-20160329185618-73d19cdc2bf0
github.com/hashicorp/go-version v0.0.0-20160119211326-7e3c02b30806
github.com/hashicorp/go-version v1.1.0
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 // indirect
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect
github.com/hashicorp/memberlist v0.1.0 // indirect
@ -97,7 +93,6 @@ require (
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee // indirect
github.com/joyent/triton-go v0.0.0-20180116165742-545edbe0d564
github.com/jtolds/gls v4.2.1+incompatible // indirect
github.com/kardianos/govendor v1.0.9 // indirect
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1
github.com/keybase/go-crypto v0.0.0-20181127160227-255a5089e85a // indirect
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77 // indirect
@ -105,30 +100,24 @@ require (
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f // indirect
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 // indirect
github.com/lib/pq v1.0.0 // indirect
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c // indirect
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect
github.com/masterzen/simplexml v0.0.0-20140219194429-95ba30457eb1 // indirect
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7 // indirect
github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767 // indirect
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f
github.com/miekg/dns v1.1.1 // indirect
github.com/mitchellh/cli v0.0.0-20170908181043-65fcae5817c8
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7
github.com/mitchellh/go-homedir v0.0.0-20151025052427-d682a8f0cf13
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.0.0
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
github.com/mitchellh/iochan v0.0.0-20150529224432-87b45ffd0e95
github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc
github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
github.com/mitchellh/reflectwalk v1.0.0
github.com/mna/pigeon v1.0.0 // indirect
github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef // indirect
github.com/moul/gotty-client v0.0.0-20180327180212-b26a57ebc215 // indirect
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
@ -165,22 +154,18 @@ require (
github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 // indirect
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 // indirect
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
github.com/ulikunitz/xz v0.0.0-20180703112113-636d36a76670
github.com/ulikunitz/xz v0.5.5
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311
github.com/xanzy/go-cloudstack v2.4.1+incompatible
golang.org/x/crypto v0.0.0-20180322175230-88942b9c40a4
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
golang.org/x/tools v0.0.0-20190221204921-83362c3779f5 // indirect
google.golang.org/api v0.0.0-20180818000503-e21acd801f91
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06
google.golang.org/api v0.1.0
google.golang.org/appengine v1.4.0 // indirect
google.golang.org/grpc v1.17.0 // indirect
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.27 // indirect
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
gopkg.in/h2non/gock.v1 v1.0.12 // indirect
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516 // indirect

170
go.sum
View File

@ -1,5 +1,13 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 h1:RMTyvS5bjvSWiUcfqfr/E2pxHEMrALvU+E12n6biymg=
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1/go.mod h1:61apmbkVJH4kg+38ftT+/l0XxdUCVnHggqcOTqZRSEE=
github.com/Azure/azure-sdk-for-go v17.3.1+incompatible h1:9Nzge8xxnYm5lVRkvTpG1odiDN0fYDorQwVEaVfg1+g=
@ -10,8 +18,7 @@ github.com/Azure/go-autorest v10.12.0+incompatible h1:6YphwUK+oXbzvCc1fd5VrnxCek
github.com/Azure/go-autorest v10.12.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA=
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/Bowery/prompt v0.0.0-20180817134258-8a1d5376df1c h1:fAMg70P5ydy1uiIj6CdA69h6nmQKbv18VlVOXhKNrcM=
github.com/Bowery/prompt v0.0.0-20180817134258-8a1d5376df1c/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290 h1:K9I21XUHNbYD3GNMmJBN0UKJCpdP+glftwNZ7Bo8kqY=
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 h1:dmc/C8bpE5VkQn65PNbbyACDC8xw8Hpp/NEurdPmQDQ=
@ -34,6 +41,7 @@ github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KM
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f h1:jI4DIE5Vf4oRaHfthB0oRhU+yuYuoOTurDzwAlskP00=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd h1:S3Fr6QnkpW9VRjiEY4psQHhhbbahASuNVj52YIce7lI=
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 h1:BFFG6KP8ASFBg2ptWsJn8p8RDufBjBDKIxLU7BTYGOM=
@ -48,10 +56,13 @@ github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 h1:MBXhrxjNkjdqJysf
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.16.24 h1:I/A3Hwbgs3IEAP6v1bFpHKXiT7wZDoToX9cb00nxZnM=
github.com/aws/aws-sdk-go v1.16.24/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.0.0-20150902231413-36e9cfdd6909 h1:mUVWHQ4tjVv86uJhxSbYqwdz4o+Imcl6HoZtoaqC3zM=
github.com/bgentry/speakeasy v0.0.0-20150902231413-36e9cfdd6909/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 h1:3b+p838vN4sc37brz9W2HDphtSwZFcXZwFLyzm5Vk28=
@ -60,10 +71,11 @@ github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYE
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/cenkalti/backoff v2.1.0+incompatible h1:FIRvWBZrzS4YC7NT5cOuZjexzFvIr+Dbi6aD1cZaNBk=
github.com/cenkalti/backoff v2.1.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cheggaaa/pb v1.0.26 h1:cxVZXxXCTNW7yYwnrTAhJ42LcWrLjp676j+y1AmmLKA=
github.com/cheggaaa/pb v1.0.26/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/cheggaaa/pb v1.0.27 h1:wIkZHkNfC7R6GI5w7l/PdAdzXzlrbcI3p8OAlnkTsnc=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/circonus-labs/circonus-gometrics v2.2.5+incompatible h1:KsuY3ogbxgVv3FNhbLUoT+SE9znoWEUIuChSIT4HukI=
github.com/circonus-labs/circonus-gometrics v2.2.5+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA=
@ -71,22 +83,17 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/goselect v0.0.0-20180210034346-528c74964609 h1:FSxXMd2wCHj6GqgBdo4UtVA9R2aieIDvSniepqyOppU=
github.com/creack/goselect v0.0.0-20180210034346-528c74964609/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f h1:WH0w/R4Yoey+04HhFxqZ6VX6I0d7RMyw5aXQ9UTvQPs=
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
github.com/denverdino/aliyungo v0.0.0-20180417075537-ebad04655e03 h1:NYqwUkb/ypX8OZmBDpPPbZ4npAPjlGFXZ4aSOyRzRL8=
github.com/denverdino/aliyungo v0.0.0-20180417075537-ebad04655e03/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/denverdino/aliyungo v0.0.0-20190220033614-36e2ae938978 h1:oyfbRmu7YnytN4bXhvJPY1HPgUL52j5AxtgGDU0bMVs=
github.com/denverdino/aliyungo v0.0.0-20190220033614-36e2ae938978/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/digitalocean/godo v0.0.0-20170407151542-4c04abe183f4 h1:34XBbvedApvUMZY6E6Q95ksnpl+FtX+KF3ywZzZY6X0=
github.com/digitalocean/godo v0.0.0-20170407151542-4c04abe183f4/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
github.com/digitalocean/godo v1.7.3 h1:0tFPilFBDsVSiAKF8hyzj/MAmRYxNc5MGhAiYZYVrpo=
github.com/digitalocean/godo v1.7.3/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
github.com/dnaeon/go-vcr v1.0.0 h1:1QZ+ahihvRvppcJnFvuoHAdnZTf1PqKjO4Ftr1cfQTo=
@ -99,8 +106,8 @@ github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/duosecurity/duo_api_golang v0.0.0-20181210160733-61e0defebf22 h1:RqZJa9Ohzpyr5OADCKz/8t0vtdD2vczMdjqCbvTYz7o=
github.com/duosecurity/duo_api_golang v0.0.0-20181210160733-61e0defebf22/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo=
github.com/dustin/go-humanize v0.0.0-20170228161531-259d2a102b87 h1:uPzP/9GIqYKvZAmz4IayKMMZiWRWNtGynUREBtTXPXA=
github.com/dustin/go-humanize v0.0.0-20170228161531-259d2a102b87/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
@ -111,8 +118,11 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ldap/ldap v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8=
@ -123,36 +133,50 @@ github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b h1:dnUw9Ih14dCKzbtZxm+pwQRYIb+9ypiwtZgsCQN4zmg=
github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
github.com/gofrs/flock v0.7.0 h1:pGFUjl501gafK9HBt1VGL1KCOd/YhIooID+xgyJCf3g=
github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.0.0-20180328201512-5411ab924f9f h1:e7go61E2Xh2l5KtL3Lh6Tjy0/T+eem0bQsDwETI7SMo=
github.com/google/go-cmp v0.0.0-20180328201512-5411ab924f9f/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v0.0.0-20151028211038-2a60fc2ba6c1 h1:ULA7QZ5KGgwOBeBBCA7Xyv96fmriEfhs0D2KDRslTd8=
github.com/google/go-querystring v0.0.0-20151028211038-2a60fc2ba6c1/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d h1:rXQlD9GXkjA/PQZhmEaF/8Pj/sJfdZJK7GJG0gkS8I0=
github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06 h1:m7Rt/8En7PLrM7PQpykdZBPKUdgZWN6MwiA/ChVIoxs=
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
github.com/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6 h1:Cw/B8Bu7Rryomxf7bjc8zNfIyLgjxsDd91n0eGRWpuo=
github.com/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 h1:JIM+OacoOJRU30xpjMf8sulYqjr0ViA3WDrTX6j/yDI=
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/consul v0.0.0-20180807174550-3e6313bebbf0 h1:vh/G7ew4mi/FcKNrRk4x1N1fWYzYB5/AaSHr9W+wrF4=
@ -161,8 +185,10 @@ github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
github.com/hashicorp/go-cleanhttp v0.0.0-20160217214820-875fb671b3dd h1:g63OFYSdtNzMg+OszHIUmlRweTB2syHVsZDBKoa1km8=
github.com/hashicorp/go-cleanhttp v0.0.0-20160217214820-875fb671b3dd/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-getter v1.2.0 h1:E05bVPilzyh2yXgT6srn7WEkfMZaH+LuX9tDJw/4kaE=
github.com/hashicorp/go-getter v1.2.0/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f h1:Yv9YzBlAETjy6AOX9eLBZ3nshNVRREgerT/3nvxlGho=
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
@ -182,12 +208,14 @@ github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 h1:qCv4
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-uuid v0.0.0-20160329185618-73d19cdc2bf0 h1:YnYV/qHLtjZmhFnWaSsNqVAUhxCuPKRe5wDGpOKYL/U=
github.com/hashicorp/go-uuid v0.0.0-20160329185618-73d19cdc2bf0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v0.0.0-20160119211326-7e3c02b30806 h1:0MKTKHll8VOZ0ciR+yTIEJ8JrT0UUiRDsgtujJT7a8U=
github.com/hashicorp/go-version v0.0.0-20160119211326-7e3c02b30806/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
@ -210,14 +238,14 @@ github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 h1:MIteIo
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775/go.mod h1:R9rU87RxxmcD3DkspW9JqGBXiJyg5MA+WNCtJrBtnXs=
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee h1:AQ/QmCk6x8ECPpf2pkPtA4lyncEEBbs8VFnVXPYKhIs=
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joyent/triton-go v0.0.0-20180116165742-545edbe0d564 h1:+HMa2xWQOm+9ebsl0+XsuLaPuFCxExv3sCXo5psVzYI=
github.com/joyent/triton-go v0.0.0-20180116165742-545edbe0d564/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc=
github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/keybase/go-crypto v0.0.0-20181127160227-255a5089e85a h1:X/UFlwD2/UV0RCy+8ITi4DmxJwk83YUH7bXwkJIHHMo=
@ -236,6 +264,7 @@ github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169/go.mod h1:glhvuHOU9Hy7/8Pwwd
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
@ -252,14 +281,15 @@ github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939 h1:cRFHA33ER97Xy5j
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939/go.mod h1:CfZSN7zwz5gJiFhZJz49Uzk7mEBHIceWmbFmYx7Hf7E=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7 h1:owMyzMR4QR+jSdlfkX9jPU3rsby4++j99BfbtgVr6ZY=
github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767 h1:Nk2R0tWpD2RdkQ+53zE6kWnSGuhQyDlnOs2MPiqVubE=
github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f h1:4P7Ul+TAnk92vTeVkXs6VLjmf1EhrYtDRa03PCYY6VM=
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.1 h1:DVkblRdiScEnEr0LR9nTnEQqHYycjkXW9bOjd+2EL2o=
github.com/miekg/dns v1.1.1/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v0.0.0-20170908181043-65fcae5817c8 h1:ZPmOsqSxGDUqFp0ErYpJDIfaBRBGvQv0qDPr37kZBrk=
@ -268,8 +298,8 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7 h1:PXPMDtfqV+rZJshQHOiwUFqlqErXaAcuWy+/ZmyRfNc=
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7/go.mod h1:g7SZj7ABpStq3tM4zqHiVEG5un/DZ1+qJJKO7qx1EvU=
github.com/mitchellh/go-homedir v0.0.0-20151025052427-d682a8f0cf13 h1:VWOqu/UItbEBupQwKkabbfnoEbgAE4IGjZBH2MFx8jY=
github.com/mitchellh/go-homedir v0.0.0-20151025052427-d682a8f0cf13/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@ -285,14 +315,14 @@ github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 h1:+DAetXqxv/
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mna/pigeon v1.0.0 h1:n46IoStjdzjaXuyBH53j9HZ8CVqGWpC7P5/v8dP4qEY=
github.com/mna/pigeon v1.0.0/go.mod h1:Iym28+kJVnC1hfQvv5MUtI6AiFFzvQjHcvI4RFTG/04=
github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef h1:E/seV1Rtsnr2juBw1Dfz4iDPT3/5s1H/BATx+ePmSyo=
github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef/go.mod h1:LgKrp0Iss/BVwquptq4eIe6HPr0s3t1WHT5x0qOh14U=
github.com/moul/gotty-client v0.0.0-20180327180212-b26a57ebc215 h1:y6FZWUBBt1iPmJyGbGza3ncvVBMKzgd32oFChRZR7Do=
github.com/moul/gotty-client v0.0.0-20180327180212-b26a57ebc215/go.mod h1:CxM/JGtpRrEPve5H04IhxJrGhxgwxMc6jSP2T4YD60w=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
@ -310,6 +340,7 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/oracle/oci-go-sdk v1.8.0 h1:4SO45bKV0I3/Mn1os3ANDZmV0eSE5z5CLdSUIkxtyzs=
github.com/oracle/oci-go-sdk v1.8.0/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/ory/dockertest v3.3.2+incompatible h1:uO+NcwH6GuFof/Uz8yzjNi1g0sGT5SLAJbdBvD8bUYc=
@ -334,16 +365,20 @@ github.com/posener/complete v0.0.0-20170908125245-88e59760adad h1:jTlrcNQXxCpT8N
github.com/posener/complete v0.0.0-20170908125245-88e59760adad/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible h1:ZoVHH6voxW9Onzo6z2yLtocVoN6mBocyDoqoyAMHokE=
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible/go.mod h1:T3/WrziK7fYH3C8ilAFAHe99R452/IzIG3YYkqaOFeQ=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 h1:4qPms2txLWMLXKzqlnYSulKRS4cS9aYgPtAEpUelQok=
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17/go.mod h1:SAEjPB4voP88qmWJXI7mA5m15uNlEnuHLx4Eu2mPGpQ=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/rwtodd/Go.Sed v0.0.0-20170507045331-d6d5d585814e h1:lN+IKs+Jb9uwDOMO4VJZzH9vOjjist0THR5s9akp+Ss=
github.com/rwtodd/Go.Sed v0.0.0-20170507045331-d6d5d585814e/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
@ -354,14 +389,40 @@ github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70 h1:DaqC32ZwO
github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70/go.mod h1:XjlXWPd6VONhsRSEuzGkV8mzRpH7ou1cdLV7IKJk96s=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v0.0.0-20180315010703-90150a8ed11b h1:m1cItYkpJSJ3ZKPEV/XKbZTcCaJHY1qF+R9GisLZ4kw=
github.com/sirupsen/logrus v0.0.0-20180315010703-90150a8ed11b/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346 h1:a014AaXz7AISMePv8xKRffUZZkr5z2XmSDf41gRV3+A=
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 h1:/Bsw4C+DEdqPjt8vAqaC9LAqpAQnaCQQqmolqq3S1T4=
@ -370,46 +431,75 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvAF7xCmNNquyKaYaaVHo1Nnk=
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ulikunitz/xz v0.0.0-20180703112113-636d36a76670 h1:HQWT4ta3wW5GZ790GaqLCS+w1dvuA3rMfEQxLi+UOYU=
github.com/ulikunitz/xz v0.0.0-20180703112113-636d36a76670/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311 h1:s5pyxd5S6wRs2WpEE0xRfWUF46Wbz44h203KnbX0ecI=
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/xanzy/go-cloudstack v2.4.1+incompatible h1:Oc4xa2+I94h1g/QJ+nHoq597nJz2KXzxuQx/weOx0AU=
github.com/xanzy/go-cloudstack v2.4.1+incompatible/go.mod h1:s3eL3z5pNXF5FVybcT+LIVdId8pYn709yv6v5mrkrQE=
golang.org/x/crypto v0.0.0-20180322175230-88942b9c40a4 h1:AJCW0rhPjFKEAoValWpqnRKxX8YV0Xvqfw+dOexCTPc=
golang.org/x/crypto v0.0.0-20180322175230-88942b9c40a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190221204921-83362c3779f5 h1:ev5exjGDsOo0NPTB0qdCcE53BfWl1IICJlhgXgfT9fM=
golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
google.golang.org/api v0.0.0-20180818000503-e21acd801f91 h1:MgYYgjaWMS2qQiDwCznfbqNmEOdSULlvjCvSCvIe/Wo=
google.golang.org/api v0.0.0-20180818000503-e21acd801f91/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
@ -442,4 +532,8 @@ gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@ -145,13 +145,12 @@ func Test(t TestT, c TestCase) {
// Run it! We use a temporary directory for caching and discard
// any UI output. We discard since it shows up in logs anyways.
log.Printf("[DEBUG] Running 'test' build")
cache := &packer.FileCache{CacheDir: os.TempDir()}
ui := &packer.BasicUi{
Reader: os.Stdin,
Writer: ioutil.Discard,
ErrorWriter: ioutil.Discard,
}
artifacts, err := build.Run(ui, cache)
artifacts, err := build.Run(ui)
if err != nil {
t.Fatal(fmt.Sprintf("Run error:\n\n%s", err))
goto TEARDOWN

13
main.go
View File

@ -11,7 +11,6 @@ import (
"log"
"math/rand"
"os"
"path/filepath"
"runtime"
"sync"
"syscall"
@ -160,19 +159,12 @@ func wrappedMain() int {
)
}
cacheDir := os.Getenv("PACKER_CACHE_DIR")
if cacheDir == "" {
cacheDir = "packer_cache"
}
cacheDir, err = filepath.Abs(cacheDir)
cacheDir, err := packer.CachePath()
if err != nil {
fmt.Fprintf(os.Stderr, "Error preparing cache directory: \n\n%s\n", err)
return 1
}
log.Printf("Setting cache directory: %s", cacheDir)
cache := &packer.FileCache{CacheDir: cacheDir}
// Determine if we're in machine-readable mode by mucking around with
// the arguments...
@ -220,8 +212,7 @@ func wrappedMain() int {
},
Version: version.Version,
},
Cache: cache,
Ui: ui,
Ui: ui,
}
cli := &cli.CLI{

View File

@ -53,7 +53,7 @@ type Build interface {
// Run runs the actual builder, returning an artifact implementation
// of what is built. If anything goes wrong, an error is returned.
Run(Ui, Cache) ([]Artifact, error)
Run(Ui) ([]Artifact, error)
// Cancel will cancel a running build. This will block until the build
// is actually completely canceled.
@ -180,7 +180,7 @@ func (b *coreBuild) Prepare() (warn []string, err error) {
}
// Runs the actual build. Prepare must be called prior to running this.
func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) {
func (b *coreBuild) Run(originalUi Ui) ([]Artifact, error) {
if !b.prepareCalled {
panic("Prepare must be called first")
}
@ -235,7 +235,7 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) {
log.Printf("Running builder: %s", b.builderType)
ts := CheckpointReporter.AddSpan(b.builderType, "builder", b.builderConfig)
builderArtifact, err := b.builder.Run(builderUi, hook, cache)
builderArtifact, err := b.builder.Run(builderUi, hook)
ts.End(err)
if err != nil {
return nil, err

View File

@ -172,12 +172,11 @@ func TestBuildPrepare_variables_default(t *testing.T) {
}
func TestBuild_Run(t *testing.T) {
cache := &TestCache{}
ui := testUi()
build := testBuild()
build.Prepare()
artifacts, err := build.Run(ui, cache)
artifacts, err := build.Run(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -218,7 +217,6 @@ func TestBuild_Run(t *testing.T) {
}
func TestBuild_Run_Artifacts(t *testing.T) {
cache := &TestCache{}
ui := testUi()
// Test case: Test that with no post-processors, we only get the
@ -227,7 +225,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
build.postProcessors = [][]coreBuildPostProcessor{}
build.Prepare()
artifacts, err := build.Run(ui, cache)
artifacts, err := build.Run(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -252,7 +250,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}
build.Prepare()
artifacts, err = build.Run(ui, cache)
artifacts, err = build.Run(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -280,7 +278,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}
build.Prepare()
artifacts, err = build.Run(ui, cache)
artifacts, err = build.Run(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -310,7 +308,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}
build.Prepare()
artifacts, err = build.Run(ui, cache)
artifacts, err = build.Run(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -337,7 +335,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}
build.Prepare()
artifacts, err = build.Run(ui, cache)
artifacts, err = build.Run(ui)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -365,7 +363,7 @@ func TestBuild_RunBeforePrepare(t *testing.T) {
}
}()
testBuild().Run(testUi(), &TestCache{})
testBuild().Run(testUi())
}
func TestBuild_Cancel(t *testing.T) {

View File

@ -28,7 +28,7 @@ type Builder interface {
Prepare(...interface{}) ([]string, error)
// Run is where the actual build should take place. It takes a Build and a Ui.
Run(ui Ui, hook Hook, cache Cache) (Artifact, error)
Run(ui Ui, hook Hook) (Artifact, error)
// Cancel cancels a possibly running Builder. This should block until
// the builder actually cancels and cleans up after itself.

View File

@ -16,7 +16,6 @@ type MockBuilder struct {
PrepareCalled bool
PrepareConfig []interface{}
RunCalled bool
RunCache Cache
RunHook Hook
RunUi Ui
CancelCalled bool
@ -28,11 +27,10 @@ func (tb *MockBuilder) Prepare(config ...interface{}) ([]string, error) {
return tb.PrepareWarnings, nil
}
func (tb *MockBuilder) Run(ui Ui, h Hook, c Cache) (Artifact, error) {
func (tb *MockBuilder) Run(ui Ui, h Hook) (Artifact, error) {
tb.RunCalled = true
tb.RunHook = h
tb.RunUi = ui
tb.RunCache = c
if tb.RunErrResult {
return nil, errors.New("foo")

View File

@ -1,120 +1,30 @@
package packer
import (
"crypto/sha256"
"encoding/hex"
"log"
"os"
"path/filepath"
"strings"
"sync"
)
// Cache implements a caching interface where files can be stored for
// re-use between multiple runs.
type Cache interface {
// Lock takes a key and returns the path where the file can be written to.
// Packer guarantees that no other process will write to this file while
// the lock is held.
//
// If the key has an extension (e.g., file.ext), the resulting path
// will have that extension as well.
//
// The cache will block and wait for the lock.
Lock(string) string
var DefaultCacheDir = "packer_cache"
// Unlock will unlock a certain cache key. Be very careful that this
// is only called once per lock obtained.
Unlock(string)
// RLock returns the path to a key in the cache and locks it for reading.
// The second return parameter is whether the key existed or not.
// This will block if any locks are held for writing. No lock will be
// held if the key doesn't exist.
RLock(string) (string, bool)
// RUnlock will unlock a key for reading.
RUnlock(string)
}
// FileCache implements a Cache by caching the data directly to a cache
// directory.
type FileCache struct {
CacheDir string
l sync.Mutex
rw map[string]*sync.RWMutex
}
func (f *FileCache) Lock(key string) string {
hashKey := f.hashKey(key)
rw := f.rwLock(hashKey)
rw.Lock()
return f.cachePath(key, hashKey)
}
func (f *FileCache) Unlock(key string) {
hashKey := f.hashKey(key)
rw := f.rwLock(hashKey)
rw.Unlock()
}
func (f *FileCache) RLock(key string) (string, bool) {
hashKey := f.hashKey(key)
rw := f.rwLock(hashKey)
rw.RLock()
return f.cachePath(key, hashKey), true
}
func (f *FileCache) RUnlock(key string) {
hashKey := f.hashKey(key)
rw := f.rwLock(hashKey)
rw.RUnlock()
}
func (f *FileCache) cachePath(key string, hashKey string) string {
if endIndex := strings.Index(key, "?"); endIndex > -1 {
key = key[:endIndex]
// CachePath returns an absolute path to a cache file or directory
//
// When the directory is not absolute, CachePath will try to get
// current working directory to be able to return a full path.
//
// CachePath can error in case it cannot find the cwd.
//
// ex:
// PACKER_CACHE_DIR="" CacheDir() => "./packer_cache/
// PACKER_CACHE_DIR="" CacheDir("foo") => "./packer_cache/foo
// PACKER_CACHE_DIR="bar" CacheDir("foo") => "./bar/foo
// PACKER_CACHE_DIR="/home/there" CacheDir("foo", "bar") => "/home/there/foo/bar
func CachePath(paths ...string) (string, error) {
cacheDir := DefaultCacheDir
if cd := os.Getenv("PACKER_CACHE_DIR"); cd != "" {
cacheDir = cd
}
suffix := ""
dotIndex := strings.LastIndex(key, ".")
if dotIndex > -1 {
if slashIndex := strings.LastIndex(key, "/"); slashIndex <= dotIndex {
suffix = key[dotIndex:]
}
}
// Make the cache directory. We ignore errors here, but
// log them in case something happens.
if err := os.MkdirAll(f.CacheDir, 0755); err != nil {
log.Printf(
"[ERR] Error making cacheDir: %s %s", f.CacheDir, err)
}
return filepath.Join(f.CacheDir, hashKey+suffix)
}
func (f *FileCache) hashKey(key string) string {
sha := sha256.New()
sha.Write([]byte(key))
return hex.EncodeToString(sha.Sum(nil))
}
func (f *FileCache) rwLock(hashKey string) *sync.RWMutex {
f.l.Lock()
defer f.l.Unlock()
if f.rw == nil {
f.rw = make(map[string]*sync.RWMutex)
}
if result, ok := f.rw[hashKey]; ok {
return result
}
var result sync.RWMutex
f.rw[hashKey] = &result
return &result
paths = append([]string{cacheDir}, paths...)
return filepath.Abs(filepath.Join(paths...))
}

View File

@ -1,84 +1,52 @@
package packer
import (
"io/ioutil"
"os"
"strings"
"path/filepath"
"testing"
"github.com/hashicorp/packer/packer/tmp"
)
type TestCache struct{}
func (TestCache) Lock(string) string {
return ""
}
func (TestCache) Unlock(string) {}
func (TestCache) RLock(string) (string, bool) {
return "", false
}
func (TestCache) RUnlock(string) {}
func TestFileCache_Implements(t *testing.T) {
var raw interface{}
raw = &FileCache{}
if _, ok := raw.(Cache); !ok {
t.Fatal("FileCache must be a Cache")
}
}
func TestFileCache(t *testing.T) {
cacheDir, err := tmp.Dir("packer")
func TestCachePath(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatalf("error creating temporary dir: %s", err)
t.Fatalf("Getwd: %v", err)
}
defer os.RemoveAll(cacheDir)
tmp := os.TempDir()
cache := &FileCache{CacheDir: cacheDir}
// reset env
cd := os.Getenv("PACKER_CACHE_DIR")
os.Setenv("PACKER_CACHE_DIR", "")
defer func() {
os.Setenv("PACKER_CACHE_DIR", cd)
}()
// Test path with no extension (GH-716)
path := cache.Lock("/foo.bar/baz")
defer cache.Unlock("/foo.bar/baz")
if strings.Contains(path, ".bar") {
t.Fatalf("bad: %s", path)
type args struct {
paths []string
}
// Test paths with a ?
path = cache.Lock("foo.ext?foo=bar.foo")
defer cache.Unlock("foo.ext?foo=bar.foo")
if !strings.HasSuffix(path, ".ext") {
t.Fatalf("bad extension with question mark: %s", path)
tests := []struct {
name string
args args
env map[string]string
want string
wantErr bool
}{
{"base", args{}, nil, filepath.Join(wd, "packer_cache"), false},
{"base and path", args{[]string{"a", "b"}}, nil, filepath.Join(wd, "packer_cache", "a", "b"), false},
{"env and path", args{[]string{"a", "b"}}, map[string]string{"PACKER_CACHE_DIR": tmp}, filepath.Join(tmp, "a", "b"), false},
}
// Test normal paths
path = cache.Lock("foo.iso")
if !strings.HasSuffix(path, ".iso") {
t.Fatalf("path doesn't end with suffix '%s': '%s'", ".iso", path)
}
err = ioutil.WriteFile(path, []byte("data"), 0666)
if err != nil {
t.Fatalf("error writing: %s", err)
}
cache.Unlock("foo.iso")
path, ok := cache.RLock("foo.iso")
if !ok {
t.Fatal("cache says key doesn't exist")
}
defer cache.RUnlock("foo.iso")
data, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("error reading file: %s", err)
}
if string(data) != "data" {
t.Fatalf("unknown data: %s", data)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for k, v := range tt.env {
os.Setenv(k, v)
}
got, err := CachePath(tt.args.paths...)
if (err != nil) != tt.wantErr {
t.Errorf("CachePath() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("CachePath() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -67,7 +67,7 @@ func TestCoreBuild_basic(t *testing.T) {
t.Fatalf("err: %s", err)
}
artifact, err := build.Run(nil, nil)
artifact, err := build.Run(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -97,7 +97,7 @@ func TestCoreBuild_basicInterpolated(t *testing.T) {
t.Fatalf("err: %s", err)
}
artifact, err := build.Run(nil, nil)
artifact, err := build.Run(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -230,7 +230,7 @@ func TestCoreBuild_prov(t *testing.T) {
t.Fatalf("err: %s", err)
}
artifact, err := build.Run(nil, nil)
artifact, err := build.Run(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -264,7 +264,7 @@ func TestCoreBuild_provSkip(t *testing.T) {
t.Fatalf("err: %s", err)
}
artifact, err := build.Run(nil, nil)
artifact, err := build.Run(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -298,7 +298,7 @@ func TestCoreBuild_provSkipInclude(t *testing.T) {
t.Fatalf("err: %s", err)
}
artifact, err := build.Run(nil, nil)
artifact, err := build.Run(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -332,7 +332,7 @@ func TestCoreBuild_provOverride(t *testing.T) {
t.Fatalf("err: %s", err)
}
artifact, err := build.Run(nil, nil)
artifact, err := build.Run(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -381,7 +381,7 @@ func TestCoreBuild_postProcess(t *testing.T) {
t.Fatalf("err: %s", err)
}
artifact, err := build.Run(ui, nil)
artifact, err := build.Run(ui)
if err != nil {
t.Fatalf("err: %s", err)
}

View File

@ -20,13 +20,13 @@ func (b *cmdBuilder) Prepare(config ...interface{}) ([]string, error) {
return b.builder.Prepare(config...)
}
func (b *cmdBuilder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *cmdBuilder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
defer func() {
r := recover()
b.checkExit(r, nil)
}()
return b.builder.Run(ui, hook, cache)
return b.builder.Run(ui, hook)
}
func (b *cmdBuilder) Cancel() {

View File

@ -1,170 +1,89 @@
package packer
import (
"fmt"
"io"
"path/filepath"
"sync"
"github.com/cheggaaa/pb"
pb "github.com/cheggaaa/pb"
)
// ProgressBar allows to graphically display
// a self refreshing progress bar.
type ProgressBar interface {
Start(total int64)
Add(current int64)
NewProxyReader(r io.Reader) (proxy io.Reader)
Finish()
}
// StackableProgressBar is a progress bar that
// allows to track multiple downloads at once.
// Every call to Start increments a counter that
// will display the number of current loadings.
// Every call to Start will add total to an internal
// total that is the total displayed.
// First call to Start will start a goroutine
// that is waiting for every download to be finished.
// Last call to Finish triggers a cleanup.
// When all active downloads are finished
// StackableProgressBar will clean itself to a default
// state.
type StackableProgressBar struct {
mtx sync.Mutex // locks in Start, Finish, Add & NewProxyReader
Bar BasicProgressBar
items int32
total int64
started bool
ConfigProgressbarFN func(*pb.ProgressBar)
}
var _ ProgressBar = new(StackableProgressBar)
func defaultProgressbarConfigFn(bar *pb.ProgressBar) {
func ProgressBarConfig(bar *pb.ProgressBar, prefix string) {
bar.SetUnits(pb.U_BYTES)
bar.Prefix(prefix)
}
func (spb *StackableProgressBar) start() {
bar := pb.New(0)
if spb.ConfigProgressbarFN == nil {
spb.ConfigProgressbarFN = defaultProgressbarConfigFn
var defaultUiProgressBar = &uiProgressBar{}
// uiProgressBar is a self managed progress bar singleton.
// decorate your struct with a *uiProgressBar to
// give it TrackProgress capabilities.
// In TrackProgress if uiProgressBar is nil
// defaultUiProgressBar will be used as
// the progress bar.
type uiProgressBar struct {
lock sync.Mutex
pool *pb.Pool
pbs int
}
func (p *uiProgressBar) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) io.ReadCloser {
if p == nil {
return defaultUiProgressBar.TrackProgress(src, currentSize, totalSize, stream)
}
spb.ConfigProgressbarFN(bar)
p.lock.Lock()
defer p.lock.Unlock()
bar.Start()
spb.Bar.ProgressBar = bar
spb.started = true
}
newPb := pb.New64(totalSize)
newPb.Set64(currentSize)
ProgressBarConfig(newPb, filepath.Base(src))
func (spb *StackableProgressBar) Start(total int64) {
spb.mtx.Lock()
spb.total += total
spb.items++
if !spb.started {
spb.start()
if p.pool == nil {
pool := pb.NewPool()
err := pool.Start()
if err != nil {
// here, we probably cannot lock
// stdout, so let's just return
// stream to avoid any error.
return stream
}
p.pool = pool
}
spb.Bar.SetTotal64(spb.total)
spb.prefix()
spb.mtx.Unlock()
}
p.pool.Add(newPb)
reader := newPb.NewProxyReader(stream)
func (spb *StackableProgressBar) Add(total int64) {
spb.mtx.Lock()
defer spb.mtx.Unlock()
if spb.Bar.ProgressBar != nil {
spb.Bar.Add(total)
p.pbs++
return &readCloser{
Reader: reader,
close: func() error {
p.lock.Lock()
defer p.lock.Unlock()
newPb.Finish()
p.pbs--
if p.pbs <= 0 {
p.pool.Stop()
p.pool = nil
}
return nil
},
}
}
func (spb *StackableProgressBar) NewProxyReader(r io.Reader) io.Reader {
spb.mtx.Lock()
defer spb.mtx.Unlock()
return spb.Bar.NewProxyReader(r)
}
func (spb *StackableProgressBar) prefix() {
spb.Bar.ProgressBar.Prefix(fmt.Sprintf("%d items: ", spb.items))
}
func (spb *StackableProgressBar) Finish() {
spb.mtx.Lock()
defer spb.mtx.Unlock()
if spb.items > 0 {
spb.items--
}
if spb.items == 0 && spb.Bar.ProgressBar != nil {
// slef cleanup
spb.Bar.ProgressBar.Finish()
spb.Bar.ProgressBar = nil
spb.started = false
spb.total = 0
return
}
}
// BasicProgressBar is packer's basic progress bar.
// Current implementation will always try to keep
// itself at the bottom of a terminal.
type BasicProgressBar struct {
*pb.ProgressBar
}
var _ ProgressBar = new(BasicProgressBar)
func (bpb *BasicProgressBar) Start(total int64) {
bpb.SetTotal64(total)
bpb.ProgressBar.Start()
}
func (bpb *BasicProgressBar) Add(current int64) {
bpb.ProgressBar.Add64(current)
}
func (bpb *BasicProgressBar) NewProxyReader(r io.Reader) io.Reader {
return &ProxyReader{
Reader: r,
ProgressBar: bpb,
}
}
func (bpb *BasicProgressBar) NewProxyReadCloser(r io.ReadCloser) io.ReadCloser {
return &ProxyReader{
Reader: r,
ProgressBar: bpb,
}
}
// NoopProgressBar is a silent progress bar.
type NoopProgressBar struct {
}
var _ ProgressBar = new(NoopProgressBar)
func (npb *NoopProgressBar) Start(int64) {}
func (npb *NoopProgressBar) Add(int64) {}
func (npb *NoopProgressBar) Finish() {}
func (npb *NoopProgressBar) NewProxyReader(r io.Reader) io.Reader { return r }
func (npb *NoopProgressBar) NewProxyReadCloser(r io.ReadCloser) io.ReadCloser { return r }
// ProxyReader implements io.ReadCloser but sends
// count of read bytes to a progress bar
type ProxyReader struct {
type readCloser struct {
io.Reader
ProgressBar
close func() error
}
func (r *ProxyReader) Read(p []byte) (n int, err error) {
n, err = r.Reader.Read(p)
r.ProgressBar.Add(int64(n))
return
}
func (c *readCloser) Close() error { return c.close() }
// Close the reader if it implements io.Closer
func (r *ProxyReader) Close() (err error) {
if closer, ok := r.Reader.(io.Closer); ok {
return closer.Close()
}
return
// NoopProgressTracker is a progress tracker
// that displays nothing.
type NoopProgressTracker struct{}
// TrackProgress returns stream
func (*NoopProgressTracker) TrackProgress(_ string, _, _ int64, stream io.ReadCloser) io.ReadCloser {
return stream
}

View File

@ -1,71 +1,58 @@
package packer
import (
"sync"
"bytes"
"io/ioutil"
"testing"
"time"
"github.com/cheggaaa/pb"
"golang.org/x/sync/errgroup"
)
func speedyProgressBar(bar *pb.ProgressBar) {
bar.SetUnits(pb.U_BYTES)
bar.SetRefreshRate(1 * time.Millisecond)
bar.NotPrint = true
bar.Format("[\x00=\x00>\x00-\x00]")
// The following tests rarelly just happen. So we run them 100 times.
func TestProgressTracking_open_close(t *testing.T) {
var bar *uiProgressBar
tracker := bar.TrackProgress("1,", 1, 42, ioutil.NopCloser(nil))
tracker.Close()
tracker = bar.TrackProgress("2,", 1, 42, ioutil.NopCloser(nil))
tracker.Close()
}
func TestStackableProgressBar_race(t *testing.T) {
bar := &StackableProgressBar{
ConfigProgressbarFN: speedyProgressBar,
}
func TestProgressTracking_multi_open_close(t *testing.T) {
var bar *uiProgressBar
g := errgroup.Group{}
start42Fn := func() { bar.Start(42) }
finishFn := func() { bar.Finish() }
add21 := func() { bar.Add(21) }
// prefix := func() { bar.prefix() }
type fields struct {
pre func()
calls []func()
post func()
for i := 0; i < 100; i++ {
g.Go(func() error {
tracker := bar.TrackProgress("file,", 1, 42, ioutil.NopCloser(nil))
return tracker.Close()
})
}
tests := []struct {
name string
fields fields
iterations int
}{
{"all public", fields{nil, []func(){start42Fn, finishFn, add21, add21}, finishFn}, 300},
{"add", fields{start42Fn, []func(){add21}, finishFn}, 300},
{"add start", fields{start42Fn, []func(){start42Fn, add21}, finishFn}, 300},
if err := g.Wait(); err != nil {
t.Fatal(err)
}
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
for i := 0; i < tt.iterations; i++ {
if tt.fields.pre != nil {
tt.fields.pre()
}
var startWg, endWg sync.WaitGroup
startWg.Add(1)
endWg.Add(len(tt.fields.calls))
for _, call := range tt.fields.calls {
call := call
go func() {
defer endWg.Done()
startWg.Wait()
call()
}()
}
startWg.Done() // everyone is initialized, let's unlock everyone at the same time.
// ....
endWg.Wait() // wait for all calls to return.
if tt.fields.post != nil {
tt.fields.post()
}
func TestProgressTracking_races(t *testing.T) {
var bar *uiProgressBar
g := errgroup.Group{}
for i := 0; i < 100; i++ {
g.Go(func() error {
txt := []byte("foobarbaz dolores")
b := bytes.NewReader(txt)
tracker := bar.TrackProgress("file,", 1, 42, ioutil.NopCloser(b))
for i := 0; i < 42; i++ {
tracker.Read([]byte("i"))
}
return tracker.Close()
})
}
if err := g.Wait(); err != nil {
t.Fatal(err)
}
}

View File

@ -43,10 +43,9 @@ func (b *build) Prepare() ([]string, error) {
return resp.Warnings, err
}
func (b *build) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error) {
func (b *build) Run(ui packer.Ui) ([]packer.Artifact, error) {
nextId := b.mux.NextId()
server := newServerWithMux(b.mux, nextId)
server.RegisterCache(cache)
server.RegisterUi(ui)
go server.Serve()
@ -113,7 +112,7 @@ func (b *BuildServer) Run(streamId uint32, reply *[]uint32) error {
}
defer client.Close()
artifacts, err := b.build.Run(client.Ui(), client.Cache())
artifacts, err := b.build.Run(client.Ui())
if err != nil {
return NewBasicError(err)
}

View File

@ -15,7 +15,6 @@ type testBuild struct {
prepareCalled bool
prepareWarnings []string
runCalled bool
runCache packer.Cache
runUi packer.Ui
setDebugCalled bool
setForceCalled bool
@ -35,9 +34,8 @@ func (b *testBuild) Prepare() ([]string, error) {
return b.prepareWarnings, nil
}
func (b *testBuild) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error) {
func (b *testBuild) Run(ui packer.Ui) ([]packer.Artifact, error) {
b.runCalled = true
b.runCache = cache
b.runUi = ui
if b.errRunResult {
@ -84,9 +82,8 @@ func TestBuild(t *testing.T) {
}
// Test Run
cache := new(testCache)
ui := new(testUi)
artifacts, err := bClient.Run(ui, cache)
artifacts, err := bClient.Run(ui)
if !b.runCalled {
t.Fatal("run should be called")
}
@ -105,7 +102,7 @@ func TestBuild(t *testing.T) {
// Test run with an error
b.errRunResult = true
_, err = bClient.Run(ui, cache)
_, err = bClient.Run(ui)
if err == nil {
t.Fatal("should error")
}

View File

@ -44,10 +44,9 @@ func (b *builder) Prepare(config ...interface{}) ([]string, error) {
return resp.Warnings, err
}
func (b *builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
func (b *builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
nextId := b.mux.NextId()
server := newServerWithMux(b.mux, nextId)
server.RegisterCache(cache)
server.RegisterHook(hook)
server.RegisterUi(ui)
go server.Serve()
@ -91,7 +90,7 @@ func (b *BuilderServer) Run(streamId uint32, reply *uint32) error {
}
defer client.Close()
artifact, err := b.builder.Run(client.Ui(), client.Hook(), client.Cache())
artifact, err := b.builder.Run(client.Ui(), client.Hook())
if err != nil {
return NewBasicError(err)
}

View File

@ -67,10 +67,9 @@ func TestBuilderRun(t *testing.T) {
bClient := client.Builder()
// Test Run
cache := new(testCache)
hook := &packer.MockHook{}
ui := &testUi{}
artifact, err := bClient.Run(ui, hook, cache)
artifact, err := bClient.Run(ui, hook)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -94,10 +93,9 @@ func TestBuilderRun_nilResult(t *testing.T) {
server.RegisterBuilder(b)
bClient := client.Builder()
cache := new(testCache)
hook := &packer.MockHook{}
ui := &testUi{}
artifact, err := bClient.Run(ui, hook, cache)
artifact, err := bClient.Run(ui, hook)
if artifact != nil {
t.Fatalf("bad: %#v", artifact)
}
@ -116,10 +114,9 @@ func TestBuilderRun_ErrResult(t *testing.T) {
b.RunErrResult = true
cache := new(testCache)
hook := &packer.MockHook{}
ui := &testUi{}
artifact, err := bClient.Run(ui, hook, cache)
artifact, err := bClient.Run(ui, hook)
if artifact != nil {
t.Fatalf("bad: %#v", artifact)
}

View File

@ -1,79 +0,0 @@
package rpc
import (
"log"
"net/rpc"
"github.com/hashicorp/packer/packer"
)
// An implementation of packer.Cache where the cache is actually executed
// over an RPC connection.
type cache struct {
client *rpc.Client
}
// CacheServer wraps a packer.Cache implementation and makes it exportable
// as part of a Golang RPC server.
type CacheServer struct {
cache packer.Cache
}
type CacheRLockResponse struct {
Path string
Exists bool
}
func (c *cache) Lock(key string) (result string) {
if err := c.client.Call("Cache.Lock", key, &result); err != nil {
log.Printf("[ERR] Cache.Lock error: %s", err)
return
}
return
}
func (c *cache) RLock(key string) (string, bool) {
var result CacheRLockResponse
if err := c.client.Call("Cache.RLock", key, &result); err != nil {
log.Printf("[ERR] Cache.RLock error: %s", err)
return "", false
}
return result.Path, result.Exists
}
func (c *cache) Unlock(key string) {
if err := c.client.Call("Cache.Unlock", key, new(interface{})); err != nil {
log.Printf("[ERR] Cache.Unlock error: %s", err)
return
}
}
func (c *cache) RUnlock(key string) {
if err := c.client.Call("Cache.RUnlock", key, new(interface{})); err != nil {
log.Printf("[ERR] Cache.RUnlock error: %s", err)
return
}
}
func (c *CacheServer) Lock(key string, result *string) error {
*result = c.cache.Lock(key)
return nil
}
func (c *CacheServer) Unlock(key string, result *interface{}) error {
c.cache.Unlock(key)
return nil
}
func (c *CacheServer) RLock(key string, result *CacheRLockResponse) error {
path, exists := c.cache.RLock(key)
*result = CacheRLockResponse{path, exists}
return nil
}
func (c *CacheServer) RUnlock(key string, result *interface{}) error {
c.cache.RUnlock(key)
return nil
}

View File

@ -1,93 +0,0 @@
package rpc
import (
"testing"
"github.com/hashicorp/packer/packer"
)
type testCache struct {
lockCalled bool
lockKey string
unlockCalled bool
unlockKey string
rlockCalled bool
rlockKey string
runlockCalled bool
runlockKey string
}
func (t *testCache) Lock(key string) string {
t.lockCalled = true
t.lockKey = key
return "foo"
}
func (t *testCache) RLock(key string) (string, bool) {
t.rlockCalled = true
t.rlockKey = key
return "foo", true
}
func (t *testCache) Unlock(key string) {
t.unlockCalled = true
t.unlockKey = key
}
func (t *testCache) RUnlock(key string) {
t.runlockCalled = true
t.runlockKey = key
}
func TestCache_Implements(t *testing.T) {
var _ packer.Cache = new(cache)
}
func TestCacheRPC(t *testing.T) {
// Create the interface to test
c := new(testCache)
// Start the server
client, server := testClientServer(t)
defer client.Close()
defer server.Close()
server.RegisterCache(c)
cacheClient := client.Cache()
// Test Lock
cacheClient.Lock("foo")
if !c.lockCalled {
t.Fatal("should be called")
}
if c.lockKey != "foo" {
t.Fatalf("bad: %s", c.lockKey)
}
// Test Unlock
cacheClient.Unlock("foo")
if !c.unlockCalled {
t.Fatal("should be called")
}
if c.unlockKey != "foo" {
t.Fatalf("bad: %s", c.unlockKey)
}
// Test RLock
cacheClient.RLock("foo")
if !c.rlockCalled {
t.Fatal("should be called")
}
if c.rlockKey != "foo" {
t.Fatalf("bad: %s", c.rlockKey)
}
// Test RUnlock
cacheClient.RUnlock("foo")
if !c.runlockCalled {
t.Fatal("should be called")
}
if c.runlockKey != "foo" {
t.Fatalf("bad: %s", c.runlockKey)
}
}

View File

@ -88,12 +88,6 @@ func (c *Client) Builder() packer.Builder {
}
}
func (c *Client) Cache() packer.Cache {
return &cache{
client: c.client,
}
}
func (c *Client) Communicator() packer.Communicator {
return &communicator{
client: c.client,

View File

@ -78,12 +78,6 @@ func (s *Server) RegisterBuilder(b packer.Builder) {
})
}
func (s *Server) RegisterCache(c packer.Cache) {
s.server.RegisterName(DefaultCacheEndpoint, &CacheServer{
cache: c,
})
}
func (s *Server) RegisterCommunicator(c packer.Communicator) {
s.server.RegisterName(DefaultCommunicatorEndpoint, &CommunicatorServer{
c: c,

View File

@ -1,7 +1,6 @@
package rpc
import (
"io"
"log"
"net/rpc"
@ -64,31 +63,6 @@ func (u *Ui) Say(message string) {
}
}
func (u *Ui) ProgressBar() packer.ProgressBar {
if err := u.client.Call("Ui.ProgressBar", new(interface{}), new(interface{})); err != nil {
log.Printf("Error in Ui.ProgressBar RPC call: %s", err)
}
return u // Ui is also a progress bar !!
}
var _ packer.ProgressBar = new(Ui)
func (pb *Ui) Start(total int64) {
pb.client.Call("Ui.Start", total, new(interface{}))
}
func (pb *Ui) Add(current int64) {
pb.client.Call("Ui.Add", current, new(interface{}))
}
func (pb *Ui) Finish() {
pb.client.Call("Ui.Finish", nil, new(interface{}))
}
func (pb *Ui) NewProxyReader(r io.Reader) io.Reader {
return &packer.ProxyReader{Reader: r, ProgressBar: pb}
}
func (u *UiServer) Ask(query string, reply *string) (err error) {
*reply, err = u.ui.Ask(query)
return
@ -120,26 +94,3 @@ func (u *UiServer) Say(message *string, reply *interface{}) error {
*reply = nil
return nil
}
func (u *UiServer) ProgressBar(_ *string, reply *interface{}) error {
// No-op for now, this function might be
// used in the future if we want to use
// different progress bars with identifiers.
u.ui.ProgressBar()
return nil
}
func (pb *UiServer) Finish(_ string, _ *interface{}) error {
pb.ui.ProgressBar().Finish()
return nil
}
func (pb *UiServer) Start(total int64, _ *interface{}) error {
pb.ui.ProgressBar().Start(total)
return nil
}
func (pb *UiServer) Add(current int64, _ *interface{}) error {
pb.ui.ProgressBar().Add(current)
return nil
}

View File

@ -0,0 +1,99 @@
package rpc
import (
"io"
"log"
"net/rpc"
"github.com/hashicorp/packer/common/random"
)
// TrackProgress starts a pair of ProgressTrackingClient and ProgressProgressTrackingServer
// that will send the size of each read bytes of stream.
// In order to track an operation on the terminal side.
func (u *Ui) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) io.ReadCloser {
pl := &TrackProgressParameters{
Src: src,
CurrentSize: currentSize,
TotalSize: totalSize,
}
var trackingID string
if err := u.client.Call("Ui.NewTrackProgress", pl, &trackingID); err != nil {
log.Printf("Error in Ui.NewTrackProgress RPC call: %s", err)
return stream
}
cli := &ProgressTrackingClient{
id: trackingID,
client: u.client,
stream: stream,
}
return cli
}
type ProgressTrackingClient struct {
id string
client *rpc.Client
stream io.ReadCloser
}
// Read will send len(b) over the wire instead of it's content
func (u *ProgressTrackingClient) Read(b []byte) (read int, err error) {
defer func() {
if err := u.client.Call("Ui"+u.id+".Add", read, new(interface{})); err != nil {
log.Printf("Error in ProgressTrackingClient.Read RPC call: %s", err)
}
}()
return u.stream.Read(b)
}
func (u *ProgressTrackingClient) Close() error {
log.Printf("closing")
if err := u.client.Call("Ui"+u.id+".Close", nil, new(interface{})); err != nil {
log.Printf("Error in ProgressTrackingClient.Close RPC call: %s", err)
}
return u.stream.Close()
}
type TrackProgressParameters struct {
Src string
TotalSize int64
CurrentSize int64
}
func (ui *UiServer) NewTrackProgress(pl *TrackProgressParameters, reply *string) error {
// keep identifier as is for now
srvr := &ProgressTrackingServer{
id: *reply,
}
*reply = pl.Src + random.AlphaNum(6)
srvr.stream = ui.ui.TrackProgress(pl.Src, pl.CurrentSize, pl.TotalSize, nopReadCloser{})
err := ui.register("Ui"+*reply, srvr)
if err != nil {
log.Printf("failed to register ProgressTrackingServer at %s: %s", *reply, err)
return err
}
return nil
}
type ProgressTrackingServer struct {
id string
stream io.ReadCloser
}
func (t *ProgressTrackingServer) Add(size int, _ *interface{}) error {
stubBytes := make([]byte, size, size)
t.stream.Read(stubBytes)
return nil
}
func (t *ProgressTrackingServer) Close(_, _ *interface{}) error {
t.stream.Close()
return nil
}
type nopReadCloser struct {
}
func (nopReadCloser) Close() error { return nil }
func (nopReadCloser) Read(b []byte) (int, error) { return len(b), nil }

View File

@ -1,11 +1,11 @@
package rpc
import (
"bytes"
"io"
"io/ioutil"
"reflect"
"testing"
"github.com/hashicorp/packer/packer"
)
type testUi struct {
@ -21,11 +21,9 @@ type testUi struct {
sayCalled bool
sayMessage string
progressBarCalled bool
progressBarStartCalled bool
progressBarAddCalled bool
progressBarFinishCalled bool
progressBarNewProxyReaderCalled bool
trackProgressCalled bool
progressBarAddCalled bool
progressBarCloseCalled bool
}
func (u *testUi) Ask(query string) (string, error) {
@ -55,27 +53,28 @@ func (u *testUi) Say(message string) {
u.sayMessage = message
}
func (u *testUi) ProgressBar() packer.ProgressBar {
u.progressBarCalled = true
return u
func (u *testUi) TrackProgress(_ string, _, _ int64, stream io.ReadCloser) (body io.ReadCloser) {
u.trackProgressCalled = true
return &readCloser{
read: func(p []byte) (int, error) {
u.progressBarAddCalled = true
return stream.Read(p)
},
close: func() error {
u.progressBarCloseCalled = true
return stream.Close()
},
}
}
func (u *testUi) Start(int64) {
u.progressBarStartCalled = true
type readCloser struct {
read func([]byte) (int, error)
close func() error
}
func (u *testUi) Add(int64) {
u.progressBarAddCalled = true
}
func (u *testUi) Finish() {
u.progressBarFinishCalled = true
}
func (u *testUi) NewProxyReader(r io.Reader) io.Reader {
u.progressBarNewProxyReaderCalled = true
return r
}
func (c *readCloser) Close() error { return c.close() }
func (c *readCloser) Read(p []byte) (int, error) { return c.read(p) }
func TestUiRPC(t *testing.T) {
// Create the UI to test
@ -119,24 +118,23 @@ func TestUiRPC(t *testing.T) {
t.Fatalf("bad: %#v", ui.errorMessage)
}
bar := uiClient.ProgressBar()
if ui.progressBarCalled != true {
t.Errorf("ProgressBar not called.")
ctt := []byte("foo bar baz !!!")
rc := ioutil.NopCloser(bytes.NewReader(ctt))
stream := uiClient.TrackProgress("stuff.txt", 0, int64(len(ctt)), rc)
if ui.trackProgressCalled != true {
t.Errorf("ProgressBastream not called.")
}
bar.Start(100)
if ui.progressBarStartCalled != true {
t.Errorf("progressBar.Start not called.")
}
bar.Add(1)
b := []byte{0}
stream.Read(b) // output ignored
if ui.progressBarAddCalled != true {
t.Errorf("progressBar.Add not called.")
t.Errorf("Add not called.")
}
bar.Finish()
if ui.progressBarFinishCalled != true {
t.Errorf("progressBar.Finish not called.")
stream.Close()
if ui.progressBarCloseCalled != true {
t.Errorf("close not called.")
}
uiClient.Machine("foo", "bar", "baz")

View File

@ -14,6 +14,8 @@ import (
"syscall"
"time"
"unicode"
getter "github.com/hashicorp/go-getter"
)
type UiColor uint
@ -36,10 +38,12 @@ type Ui interface {
Message(string)
Error(string)
Machine(string, ...string)
ProgressBar() ProgressBar
getter.ProgressTracker
}
type NoopUi struct{}
type NoopUi struct {
NoopProgressTracker
}
var _ Ui = new(NoopUi)
@ -48,56 +52,17 @@ func (*NoopUi) Say(string) { return }
func (*NoopUi) Message(string) { return }
func (*NoopUi) Error(string) { return }
func (*NoopUi) Machine(string, ...string) { return }
func (*NoopUi) ProgressBar() ProgressBar { return new(NoopProgressBar) }
// ColoredUi is a UI that is colored using terminal colors.
type ColoredUi struct {
Color UiColor
ErrorColor UiColor
Ui Ui
*uiProgressBar
}
var _ Ui = new(ColoredUi)
// TargetedUI is a UI that wraps another UI implementation and modifies
// the output to indicate a specific target. Specifically, all Say output
// is prefixed with the target name. Message output is not prefixed but
// is offset by the length of the target so that output is lined up properly
// with Say output. Machine-readable output has the proper target set.
type TargetedUI struct {
Target string
Ui Ui
}
var _ Ui = new(TargetedUI)
// The BasicUI is a UI that reads and writes from a standard Go reader
// and writer. It is safe to be called from multiple goroutines. Machine
// readable output is simply logged for this UI.
type BasicUi struct {
Reader io.Reader
Writer io.Writer
ErrorWriter io.Writer
l sync.Mutex
interrupted bool
TTY TTY
StackableProgressBar
}
var _ Ui = new(BasicUi)
func (bu *BasicUi) ProgressBar() ProgressBar {
return &bu.StackableProgressBar
}
// MachineReadableUi is a UI that only outputs machine-readable output
// to the given Writer.
type MachineReadableUi struct {
Writer io.Writer
}
var _ Ui = new(MachineReadableUi)
func (u *ColoredUi) Ask(query string) (string, error) {
return u.Ui.Ask(u.colorize(query, u.Color, true))
}
@ -124,10 +89,6 @@ func (u *ColoredUi) Machine(t string, args ...string) {
u.Ui.Machine(t, args...)
}
func (u *ColoredUi) ProgressBar() ProgressBar {
return u.Ui.ProgressBar() //TODO(adrien): color me
}
func (u *ColoredUi) colorize(message string, color UiColor, bold bool) string {
if !u.supportsColors() {
return message
@ -160,6 +121,19 @@ func (u *ColoredUi) supportsColors() bool {
return cygwin
}
// TargetedUI is a UI that wraps another UI implementation and modifies
// the output to indicate a specific target. Specifically, all Say output
// is prefixed with the target name. Message output is not prefixed but
// is offset by the length of the target so that output is lined up properly
// with Say output. Machine-readable output has the proper target set.
type TargetedUI struct {
Target string
Ui Ui
*uiProgressBar
}
var _ Ui = new(TargetedUI)
func (u *TargetedUI) Ask(query string) (string, error) {
return u.Ui.Ask(u.prefixLines(true, query))
}
@ -181,10 +155,6 @@ func (u *TargetedUI) Machine(t string, args ...string) {
u.Ui.Machine(fmt.Sprintf("%s,%s", u.Target, t), args...)
}
func (u *TargetedUI) ProgressBar() ProgressBar {
return u.Ui.ProgressBar()
}
func (u *TargetedUI) prefixLines(arrow bool, message string) string {
arrowText := "==>"
if !arrow {
@ -200,6 +170,21 @@ func (u *TargetedUI) prefixLines(arrow bool, message string) string {
return strings.TrimRightFunc(result.String(), unicode.IsSpace)
}
// The BasicUI is a UI that reads and writes from a standard Go reader
// and writer. It is safe to be called from multiple goroutines. Machine
// readable output is simply logged for this UI.
type BasicUi struct {
Reader io.Reader
Writer io.Writer
ErrorWriter io.Writer
l sync.Mutex
interrupted bool
TTY TTY
*uiProgressBar
}
var _ Ui = new(BasicUi)
func (rw *BasicUi) Ask(query string) (string, error) {
rw.l.Lock()
defer rw.l.Unlock()
@ -289,6 +274,15 @@ func (rw *BasicUi) Machine(t string, args ...string) {
log.Printf("machine readable: %s %#v", t, args)
}
// MachineReadableUi is a UI that only outputs machine-readable output
// to the given Writer.
type MachineReadableUi struct {
Writer io.Writer
NoopProgressTracker
}
var _ Ui = new(MachineReadableUi)
func (u *MachineReadableUi) Ask(query string) (string, error) {
return "", errors.New("machine-readable UI can't ask")
}
@ -335,14 +329,11 @@ func (u *MachineReadableUi) Machine(category string, args ...string) {
}
}
func (u *MachineReadableUi) ProgressBar() ProgressBar {
return new(NoopProgressBar)
}
// TimestampedUi is a UI that wraps another UI implementation and
// prefixes each message with an RFC3339 timestamp
type TimestampedUi struct {
Ui Ui
*uiProgressBar
}
var _ Ui = new(TimestampedUi)
@ -367,8 +358,6 @@ func (u *TimestampedUi) Machine(message string, args ...string) {
u.Ui.Machine(message, args...)
}
func (u *TimestampedUi) ProgressBar() ProgressBar { return u.Ui.ProgressBar() }
func (u *TimestampedUi) timestampLine(string string) string {
return fmt.Sprintf("%v: %v", time.Now().Format(time.RFC3339), string)
}
@ -378,6 +367,7 @@ func (u *TimestampedUi) timestampLine(string string) string {
type SafeUi struct {
Sem chan int
Ui Ui
*uiProgressBar
}
var _ Ui = new(SafeUi)
@ -413,7 +403,3 @@ func (u *SafeUi) Machine(t string, args ...string) {
u.Ui.Machine(t, args...)
<-u.Sem
}
func (u *SafeUi) ProgressBar() ProgressBar {
return new(NoopProgressBar)
}

View File

@ -48,7 +48,7 @@ func (tty *testTTY) ReadString() (string, error) {
func TestColoredUi(t *testing.T) {
bufferUi := testUi()
ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi}
ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi, defaultUiProgressBar}
if !ui.supportsColors() {
t.Skip("skipping for ui without color support")
@ -80,7 +80,7 @@ func TestColoredUi(t *testing.T) {
func TestColoredUi_noColorEnv(t *testing.T) {
bufferUi := testUi()
ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi}
ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi, defaultUiProgressBar}
// Set the env var to get rid of the color
oldenv := os.Getenv("PACKER_NO_COLOR")

View File

@ -46,7 +46,6 @@ func TestChecksumSHA1(t *testing.T) {
func setup(t *testing.T) (packer.Ui, packer.Artifact, error) {
// Create fake UI and Cache
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
// Create config for file builder
const fileConfig = `{"builders":[{"type":"file","target":"package.txt","content":"Hello world!"}]}`
@ -68,7 +67,7 @@ func setup(t *testing.T) (packer.Ui, packer.Artifact, error) {
}
// Run the file builder
artifact, err := builder.Run(ui, nil, cache)
artifact, err := builder.Run(ui, nil)
if err != nil {
return nil, nil, fmt.Errorf("Failed to build artifact: %s", err)
}

View File

@ -186,7 +186,6 @@ func TestCompressInterpolation(t *testing.T) {
func setup(t *testing.T) (packer.Ui, packer.Artifact, error) {
// Create fake UI and Cache
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
// Create config for file builder
const fileConfig = `{"builders":[{"type":"file","target":"package.txt","content":"Hello world!"}]}`
@ -208,7 +207,7 @@ func setup(t *testing.T) (packer.Ui, packer.Artifact, error) {
}
// Run the file builder
artifact, err := builder.Run(ui, nil, cache)
artifact, err := builder.Run(ui, nil)
if err != nil {
return nil, nil, fmt.Errorf("Failed to build artifact: %s", err)
}

View File

@ -328,7 +328,6 @@ func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateStrin
}
ui := packer.TestUi(t)
cache := &packer.FileCache{CacheDir: os.TempDir()}
tpl, err := template.Parse(strings.NewReader(templateString))
if err != nil {
@ -375,7 +374,7 @@ func testProvisionerProvisionDockerWithPlaybookFiles(t *testing.T, templateStrin
}
hook := &packer.DispatchHook{Mapping: hooks}
artifact, err := builder.Run(ui, hook, cache)
artifact, err := builder.Run(ui, hook)
if err != nil {
t.Fatalf("Error running build %s", err)
}

View File

@ -126,11 +126,6 @@ func (p *Provisioner) ProvisionDownload(ui packer.Ui, comm packer.Communicator)
}
defer f.Close()
// Get a default progress bar
pb := packer.NoopProgressBar{}
pb.Start(0) // TODO: find size ? Remove ?
defer pb.Finish()
// Create MultiWriter for the current progress
pf := io.MultiWriter(f)
@ -175,13 +170,8 @@ func (p *Provisioner) ProvisionUpload(ui packer.Ui, comm packer.Communicator) er
dst = dst + filepath.Base(src)
}
// Get a default progress bar
bar := ui.ProgressBar()
bar.Start(info.Size())
defer bar.Finish()
// Create ProxyReader for the current progress
pf := bar.NewProxyReader(f)
pf := ui.TrackProgress(filepath.Base(src), 0, info.Size(), f)
defer pf.Close()
// Upload the file
if err = comm.Upload(dst, pf, &fi); err != nil {

15
vendor/cloud.google.com/go/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,15 @@
# This is the official list of cloud authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Filippo Valsorda <hi@filippo.io>
Google Inc.
Ingo Oeser <nightlyone@googlemail.com>
Palm Stone Games, Inc.
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Tyler Treat <ttreat31@gmail.com>

40
vendor/cloud.google.com/go/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,40 @@
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
# Keep the list alphabetically sorted.
Alexis Hunt <lexer@google.com>
Andreas Litt <andreas.litt@gmail.com>
Andrew Gerrand <adg@golang.org>
Brad Fitzpatrick <bradfitz@golang.org>
Burcu Dogan <jbd@google.com>
Dave Day <djd@golang.org>
David Sansome <me@davidsansome.com>
David Symonds <dsymonds@golang.org>
Filippo Valsorda <hi@filippo.io>
Glenn Lewis <gmlewis@google.com>
Ingo Oeser <nightlyone@googlemail.com>
James Hall <james.hall@shopify.com>
Johan Euphrosine <proppy@google.com>
Jonathan Amsterdam <jba@google.com>
Kunpei Sakai <namusyaka@gmail.com>
Luna Duclos <luna.duclos@palmstonegames.com>
Magnus Hiie <magnus.hiie@gmail.com>
Mario Castro <mariocaster@gmail.com>
Michael McGreevy <mcgreevy@golang.org>
Omar Jarjur <ojarjur@google.com>
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Sarah Adams <shadams@google.com>
Thanatat Tamtan <acoshift@gmail.com>
Toby Burress <kurin@google.com>
Tuo Shan <shantuo@google.com>
Tyler Treat <ttreat31@gmail.com>

View File

@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 Google Inc.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

513
vendor/cloud.google.com/go/compute/metadata/metadata.go generated vendored Normal file
View File

@ -0,0 +1,513 @@
// Copyright 2014 Google LLC
//
// 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 metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package metadata // import "cloud.google.com/go/compute/metadata"
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strings"
"sync"
"time"
)
const (
// metadataIP is the documented metadata server IP address.
metadataIP = "169.254.169.254"
// metadataHostEnv is the environment variable specifying the
// GCE metadata hostname. If empty, the default value of
// metadataIP ("169.254.169.254") is used instead.
// This is variable name is not defined by any spec, as far as
// I know; it was made up for the Go package.
metadataHostEnv = "GCE_METADATA_HOST"
userAgent = "gcloud-golang/0.1"
)
type cachedValue struct {
k string
trim bool
mu sync.Mutex
v string
}
var (
projID = &cachedValue{k: "project/project-id", trim: true}
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
instID = &cachedValue{k: "instance/id", trim: true}
)
var (
defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 2 * time.Second,
},
}}
subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}}
)
// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type NotDefinedError string
func (suffix NotDefinedError) Error() string {
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}
func (c *cachedValue) get(cl *Client) (v string, err error) {
defer c.mu.Unlock()
c.mu.Lock()
if c.v != "" {
return c.v, nil
}
if c.trim {
v, err = cl.getTrimmed(c.k)
} else {
v, err = cl.Get(c.k)
}
if err == nil {
c.v = v
}
return
}
var (
onGCEOnce sync.Once
onGCE bool
)
// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
onGCEOnce.Do(initOnGCE)
return onGCE
}
func initOnGCE() {
onGCE = testOnGCE()
}
func testOnGCE() bool {
// The user explicitly said they're on GCE, so trust them.
if os.Getenv(metadataHostEnv) != "" {
return true
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
resc := make(chan bool, 2)
// Try two strategies in parallel.
// See https://github.com/googleapis/google-cloud-go/issues/194
go func() {
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
req.Header.Set("User-Agent", userAgent)
res, err := defaultClient.hc.Do(req.WithContext(ctx))
if err != nil {
resc <- false
return
}
defer res.Body.Close()
resc <- res.Header.Get("Metadata-Flavor") == "Google"
}()
go func() {
addrs, err := net.LookupHost("metadata.google.internal")
if err != nil || len(addrs) == 0 {
resc <- false
return
}
resc <- strsContains(addrs, metadataIP)
}()
tryHarder := systemInfoSuggestsGCE()
if tryHarder {
res := <-resc
if res {
// The first strategy succeeded, so let's use it.
return true
}
// Wait for either the DNS or metadata server probe to
// contradict the other one and say we are running on
// GCE. Give it a lot of time to do so, since the system
// info already suggests we're running on a GCE BIOS.
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case res = <-resc:
return res
case <-timer.C:
// Too slow. Who knows what this system is.
return false
}
}
// There's no hint from the system info that we're running on
// GCE, so use the first probe's result as truth, whether it's
// true or false. The goal here is to optimize for speed for
// users who are NOT running on GCE. We can't assume that
// either a DNS lookup or an HTTP request to a blackholed IP
// address is fast. Worst case this should return when the
// metaClient's Transport.ResponseHeaderTimeout or
// Transport.Dial.Timeout fires (in two seconds).
return <-resc
}
// systemInfoSuggestsGCE reports whether the local system (without
// doing network requests) suggests that we're running on GCE. If this
// returns true, testOnGCE tries a bit harder to reach its metadata
// server.
func systemInfoSuggestsGCE() bool {
if runtime.GOOS != "linux" {
// We don't have any non-Linux clues available, at least yet.
return false
}
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
name := strings.TrimSpace(string(slurp))
return name == "Google" || name == "Google Compute Engine"
}
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
// ResponseHeaderTimeout).
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn)
}
// Get calls Client.Get on the default client.
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return defaultClient.ProjectID() }
// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) { return defaultClient.InternalIP() }
// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) { return defaultClient.Hostname() }
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) { return defaultClient.InstanceID() }
// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) { return defaultClient.InstanceName() }
// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) { return defaultClient.Zone() }
// InstanceAttributes calls Client.InstanceAttributes on the default client.
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
// ProjectAttributes calls Client.ProjectAttributes on the default client.
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
func InstanceAttributeValue(attr string) (string, error) {
return defaultClient.InstanceAttributeValue(attr)
}
// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
func ProjectAttributeValue(attr string) (string, error) {
return defaultClient.ProjectAttributeValue(attr)
}
// Scopes calls Client.Scopes on the default client.
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
func strsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}
// A Client provides metadata.
type Client struct {
hc *http.Client
}
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
// will use the given http.Client instead of the default client.
func NewClient(c *http.Client) *Client {
return &Client{hc: c}
}
// getETag returns a value from the metadata service as well as the associated ETag.
// This func is otherwise equivalent to Get.
func (c *Client) getETag(suffix string) (value, etag string, err error) {
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host := os.Getenv(metadataHostEnv)
if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host = metadataIP
}
u := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", u, nil)
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}
if res.StatusCode != 200 {
return "", "", &Error{Code: res.StatusCode, Message: string(all)}
}
return string(all), res.Header.Get("Etag"), nil
}
// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func (c *Client) Get(suffix string) (string, error) {
val, _, err := c.getETag(suffix)
return val, err
}
func (c *Client) getTrimmed(suffix string) (s string, err error) {
s, err = c.Get(suffix)
s = strings.TrimSpace(s)
return
}
func (c *Client) lines(suffix string) ([]string, error) {
j, err := c.Get(suffix)
if err != nil {
return nil, err
}
s := strings.Split(strings.TrimSpace(j), "\n")
for i := range s {
s[i] = strings.TrimSpace(s[i])
}
return s, nil
}
// ProjectID returns the current instance's project ID string.
func (c *Client) ProjectID() (string, error) { return projID.get(c) }
// NumericProjectID returns the current instance's numeric project ID.
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
// InstanceID returns the current VM's numeric instance ID.
func (c *Client) InstanceID() (string, error) { return instID.get(c) }
// InternalIP returns the instance's primary internal IP address.
func (c *Client) InternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/ip")
}
// ExternalIP returns the instance's primary external (public) IP address.
func (c *Client) ExternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func (c *Client) Hostname() (string, error) {
return c.getTrimmed("instance/hostname")
}
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func (c *Client) InstanceTags() ([]string, error) {
var s []string
j, err := c.Get("instance/tags")
if err != nil {
return nil, err
}
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
return nil, err
}
return s, nil
}
// InstanceName returns the current VM's instance ID string.
func (c *Client) InstanceName() (string, error) {
host, err := c.Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
}
// Zone returns the current VM's zone, such as "us-central1-b".
func (c *Client) Zone() (string, error) {
zone, err := c.getTrimmed("instance/zone")
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if err != nil {
return "", err
}
return zone[strings.LastIndex(zone, "/")+1:], nil
}
// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
return c.Get("instance/attributes/" + attr)
}
// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
return c.Get("project/attributes/" + attr)
}
// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
}
// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
const failedSubscribeSleep = time.Second * 5
// First check to see if the metadata value exists at all.
val, lastETag, err := c.getETag(suffix)
if err != nil {
return err
}
if err := fn(val, true); err != nil {
return err
}
ok := true
if strings.ContainsRune(suffix, '?') {
suffix += "&wait_for_change=true&last_etag="
} else {
suffix += "?wait_for_change=true&last_etag="
}
for {
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
if err != nil {
if _, deleted := err.(NotDefinedError); !deleted {
time.Sleep(failedSubscribeSleep)
continue // Retry on other errors.
}
ok = false
}
lastETag = etag
if err := fn(val, ok); err != nil || !ok {
return err
}
}
}
// Error contains an error response from the server.
type Error struct {
// Code is the HTTP response status code.
Code int
// Message is the server response message.
Message string
}
func (e *Error) Error() string {
return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
}

315
vendor/cloud.google.com/go/iam/iam.go generated vendored Normal file
View File

@ -0,0 +1,315 @@
// Copyright 2016 Google LLC
//
// 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 iam supports the resource-specific operations of Google Cloud
// IAM (Identity and Access Management) for the Google Cloud Libraries.
// See https://cloud.google.com/iam for more about IAM.
//
// Users of the Google Cloud Libraries will typically not use this package
// directly. Instead they will begin with some resource that supports IAM, like
// a pubsub topic, and call its IAM method to get a Handle for that resource.
package iam
import (
"context"
"fmt"
"time"
gax "github.com/googleapis/gax-go/v2"
pb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
)
// client abstracts the IAMPolicy API to allow multiple implementations.
type client interface {
Get(ctx context.Context, resource string) (*pb.Policy, error)
Set(ctx context.Context, resource string, p *pb.Policy) error
Test(ctx context.Context, resource string, perms []string) ([]string, error)
}
// grpcClient implements client for the standard gRPC-based IAMPolicy service.
type grpcClient struct {
c pb.IAMPolicyClient
}
var withRetry = gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.DeadlineExceeded,
codes.Unavailable,
}, gax.Backoff{
Initial: 100 * time.Millisecond,
Max: 60 * time.Second,
Multiplier: 1.3,
})
})
func (g *grpcClient) Get(ctx context.Context, resource string) (*pb.Policy, error) {
var proto *pb.Policy
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", resource))
ctx = insertMetadata(ctx, md)
err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
var err error
proto, err = g.c.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{Resource: resource})
return err
}, withRetry)
if err != nil {
return nil, err
}
return proto, nil
}
func (g *grpcClient) Set(ctx context.Context, resource string, p *pb.Policy) error {
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", resource))
ctx = insertMetadata(ctx, md)
return gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
_, err := g.c.SetIamPolicy(ctx, &pb.SetIamPolicyRequest{
Resource: resource,
Policy: p,
})
return err
}, withRetry)
}
func (g *grpcClient) Test(ctx context.Context, resource string, perms []string) ([]string, error) {
var res *pb.TestIamPermissionsResponse
md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", resource))
ctx = insertMetadata(ctx, md)
err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
var err error
res, err = g.c.TestIamPermissions(ctx, &pb.TestIamPermissionsRequest{
Resource: resource,
Permissions: perms,
})
return err
}, withRetry)
if err != nil {
return nil, err
}
return res.Permissions, nil
}
// A Handle provides IAM operations for a resource.
type Handle struct {
c client
resource string
}
// InternalNewHandle is for use by the Google Cloud Libraries only.
//
// InternalNewHandle returns a Handle for resource.
// The conn parameter refers to a server that must support the IAMPolicy service.
func InternalNewHandle(conn *grpc.ClientConn, resource string) *Handle {
return InternalNewHandleGRPCClient(pb.NewIAMPolicyClient(conn), resource)
}
// InternalNewHandleGRPCClient is for use by the Google Cloud Libraries only.
//
// InternalNewHandleClient returns a Handle for resource using the given
// grpc service that implements IAM as a mixin
func InternalNewHandleGRPCClient(c pb.IAMPolicyClient, resource string) *Handle {
return InternalNewHandleClient(&grpcClient{c: c}, resource)
}
// InternalNewHandleClient is for use by the Google Cloud Libraries only.
//
// InternalNewHandleClient returns a Handle for resource using the given
// client implementation.
func InternalNewHandleClient(c client, resource string) *Handle {
return &Handle{
c: c,
resource: resource,
}
}
// Policy retrieves the IAM policy for the resource.
func (h *Handle) Policy(ctx context.Context) (*Policy, error) {
proto, err := h.c.Get(ctx, h.resource)
if err != nil {
return nil, err
}
return &Policy{InternalProto: proto}, nil
}
// SetPolicy replaces the resource's current policy with the supplied Policy.
//
// If policy was created from a prior call to Get, then the modification will
// only succeed if the policy has not changed since the Get.
func (h *Handle) SetPolicy(ctx context.Context, policy *Policy) error {
return h.c.Set(ctx, h.resource, policy.InternalProto)
}
// TestPermissions returns the subset of permissions that the caller has on the resource.
func (h *Handle) TestPermissions(ctx context.Context, permissions []string) ([]string, error) {
return h.c.Test(ctx, h.resource, permissions)
}
// A RoleName is a name representing a collection of permissions.
type RoleName string
// Common role names.
const (
Owner RoleName = "roles/owner"
Editor RoleName = "roles/editor"
Viewer RoleName = "roles/viewer"
)
const (
// AllUsers is a special member that denotes all users, even unauthenticated ones.
AllUsers = "allUsers"
// AllAuthenticatedUsers is a special member that denotes all authenticated users.
AllAuthenticatedUsers = "allAuthenticatedUsers"
)
// A Policy is a list of Bindings representing roles
// granted to members.
//
// The zero Policy is a valid policy with no bindings.
type Policy struct {
// TODO(jba): when type aliases are available, put Policy into an internal package
// and provide an exported alias here.
// This field is exported for use by the Google Cloud Libraries only.
// It may become unexported in a future release.
InternalProto *pb.Policy
}
// Members returns the list of members with the supplied role.
// The return value should not be modified. Use Add and Remove
// to modify the members of a role.
func (p *Policy) Members(r RoleName) []string {
b := p.binding(r)
if b == nil {
return nil
}
return b.Members
}
// HasRole reports whether member has role r.
func (p *Policy) HasRole(member string, r RoleName) bool {
return memberIndex(member, p.binding(r)) >= 0
}
// Add adds member member to role r if it is not already present.
// A new binding is created if there is no binding for the role.
func (p *Policy) Add(member string, r RoleName) {
b := p.binding(r)
if b == nil {
if p.InternalProto == nil {
p.InternalProto = &pb.Policy{}
}
p.InternalProto.Bindings = append(p.InternalProto.Bindings, &pb.Binding{
Role: string(r),
Members: []string{member},
})
return
}
if memberIndex(member, b) < 0 {
b.Members = append(b.Members, member)
return
}
}
// Remove removes member from role r if it is present.
func (p *Policy) Remove(member string, r RoleName) {
bi := p.bindingIndex(r)
if bi < 0 {
return
}
bindings := p.InternalProto.Bindings
b := bindings[bi]
mi := memberIndex(member, b)
if mi < 0 {
return
}
// Order doesn't matter for bindings or members, so to remove, move the last item
// into the removed spot and shrink the slice.
if len(b.Members) == 1 {
// Remove binding.
last := len(bindings) - 1
bindings[bi] = bindings[last]
bindings[last] = nil
p.InternalProto.Bindings = bindings[:last]
return
}
// Remove member.
// TODO(jba): worry about multiple copies of m?
last := len(b.Members) - 1
b.Members[mi] = b.Members[last]
b.Members[last] = ""
b.Members = b.Members[:last]
}
// Roles returns the names of all the roles that appear in the Policy.
func (p *Policy) Roles() []RoleName {
if p.InternalProto == nil {
return nil
}
var rns []RoleName
for _, b := range p.InternalProto.Bindings {
rns = append(rns, RoleName(b.Role))
}
return rns
}
// binding returns the Binding for the suppied role, or nil if there isn't one.
func (p *Policy) binding(r RoleName) *pb.Binding {
i := p.bindingIndex(r)
if i < 0 {
return nil
}
return p.InternalProto.Bindings[i]
}
func (p *Policy) bindingIndex(r RoleName) int {
if p.InternalProto == nil {
return -1
}
for i, b := range p.InternalProto.Bindings {
if b.Role == string(r) {
return i
}
}
return -1
}
// memberIndex returns the index of m in b's Members, or -1 if not found.
func memberIndex(m string, b *pb.Binding) int {
if b == nil {
return -1
}
for i, mm := range b.Members {
if mm == m {
return i
}
}
return -1
}
// insertMetadata inserts metadata into the given context
func insertMetadata(ctx context.Context, mds ...metadata.MD) context.Context {
out, _ := metadata.FromOutgoingContext(ctx)
out = out.Copy()
for _, md := range mds {
for k, v := range md {
out[k] = append(out[k], v...)
}
}
return metadata.NewOutgoingContext(ctx, out)
}

54
vendor/cloud.google.com/go/internal/annotate.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2017 Google LLC
//
// 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 internal
import (
"fmt"
"google.golang.org/api/googleapi"
"google.golang.org/grpc/status"
)
// Annotate prepends msg to the error message in err, attempting
// to preserve other information in err, like an error code.
//
// Annotate panics if err is nil.
//
// Annotate knows about these error types:
// - "google.golang.org/grpc/status".Status
// - "google.golang.org/api/googleapi".Error
// If the error is not one of these types, Annotate behaves
// like
// fmt.Errorf("%s: %v", msg, err)
func Annotate(err error, msg string) error {
if err == nil {
panic("Annotate called with nil")
}
if s, ok := status.FromError(err); ok {
p := s.Proto()
p.Message = msg + ": " + p.Message
return status.ErrorProto(p)
}
if g, ok := err.(*googleapi.Error); ok {
g.Message = msg + ": " + g.Message
return g
}
return fmt.Errorf("%s: %v", msg, err)
}
// Annotatef uses format and args to format a string, then calls Annotate.
func Annotatef(err error, format string, args ...interface{}) error {
return Annotate(err, fmt.Sprintf(format, args...))
}

View File

@ -0,0 +1,108 @@
// Copyright 2016 Google LLC
//
// 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 optional provides versions of primitive types that can
// be nil. These are useful in methods that update some of an API object's
// fields.
package optional
import (
"fmt"
"strings"
"time"
)
type (
// Bool is either a bool or nil.
Bool interface{}
// String is either a string or nil.
String interface{}
// Int is either an int or nil.
Int interface{}
// Uint is either a uint or nil.
Uint interface{}
// Float64 is either a float64 or nil.
Float64 interface{}
// Duration is either a time.Duration or nil.
Duration interface{}
)
// ToBool returns its argument as a bool.
// It panics if its argument is nil or not a bool.
func ToBool(v Bool) bool {
x, ok := v.(bool)
if !ok {
doPanic("Bool", v)
}
return x
}
// ToString returns its argument as a string.
// It panics if its argument is nil or not a string.
func ToString(v String) string {
x, ok := v.(string)
if !ok {
doPanic("String", v)
}
return x
}
// ToInt returns its argument as an int.
// It panics if its argument is nil or not an int.
func ToInt(v Int) int {
x, ok := v.(int)
if !ok {
doPanic("Int", v)
}
return x
}
// ToUint returns its argument as a uint.
// It panics if its argument is nil or not a uint.
func ToUint(v Uint) uint {
x, ok := v.(uint)
if !ok {
doPanic("Uint", v)
}
return x
}
// ToFloat64 returns its argument as a float64.
// It panics if its argument is nil or not a float64.
func ToFloat64(v Float64) float64 {
x, ok := v.(float64)
if !ok {
doPanic("Float64", v)
}
return x
}
// ToDuration returns its argument as a time.Duration.
// It panics if its argument is nil or not a time.Duration.
func ToDuration(v Duration) time.Duration {
x, ok := v.(time.Duration)
if !ok {
doPanic("Duration", v)
}
return x
}
func doPanic(capType string, v interface{}) {
panic(fmt.Sprintf("optional.%s value should be %s, got %T", capType, strings.ToLower(capType), v))
}

54
vendor/cloud.google.com/go/internal/retry.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2016 Google LLC
//
// 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 internal
import (
"context"
"time"
gax "github.com/googleapis/gax-go/v2"
)
// Retry calls the supplied function f repeatedly according to the provided
// backoff parameters. It returns when one of the following occurs:
// When f's first return value is true, Retry immediately returns with f's second
// return value.
// When the provided context is done, Retry returns with an error that
// includes both ctx.Error() and the last error returned by f.
func Retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error)) error {
return retry(ctx, bo, f, gax.Sleep)
}
func retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error),
sleep func(context.Context, time.Duration) error) error {
var lastErr error
for {
stop, err := f()
if stop {
return err
}
// Remember the last "real" error from f.
if err != nil && err != context.Canceled && err != context.DeadlineExceeded {
lastErr = err
}
p := bo.Pause()
if cerr := sleep(ctx, p); cerr != nil {
if lastErr != nil {
return Annotatef(lastErr, "retry failed with %v; last error", cerr)
}
return cerr
}
}
}

84
vendor/cloud.google.com/go/internal/trace/trace.go generated vendored Normal file
View File

@ -0,0 +1,84 @@
// Copyright 2018 Google LLC
//
// 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 trace
import (
"context"
"go.opencensus.io/trace"
"google.golang.org/api/googleapi"
"google.golang.org/genproto/googleapis/rpc/code"
"google.golang.org/grpc/status"
)
// StartSpan adds a span to the trace with the given name.
func StartSpan(ctx context.Context, name string) context.Context {
ctx, _ = trace.StartSpan(ctx, name)
return ctx
}
// EndSpan ends a span with the given error.
func EndSpan(ctx context.Context, err error) {
span := trace.FromContext(ctx)
if err != nil {
span.SetStatus(toStatus(err))
}
span.End()
}
// ToStatus interrogates an error and converts it to an appropriate
// OpenCensus status.
func toStatus(err error) trace.Status {
if err2, ok := err.(*googleapi.Error); ok {
return trace.Status{Code: httpStatusCodeToOCCode(err2.Code), Message: err2.Message}
} else if s, ok := status.FromError(err); ok {
return trace.Status{Code: int32(s.Code()), Message: s.Message()}
} else {
return trace.Status{Code: int32(code.Code_UNKNOWN), Message: err.Error()}
}
}
// TODO (deklerk): switch to using OpenCensus function when it becomes available.
// Reference: https://github.com/googleapis/googleapis/blob/26b634d2724ac5dd30ae0b0cbfb01f07f2e4050e/google/rpc/code.proto
func httpStatusCodeToOCCode(httpStatusCode int) int32 {
switch httpStatusCode {
case 200:
return int32(code.Code_OK)
case 499:
return int32(code.Code_CANCELLED)
case 500:
return int32(code.Code_UNKNOWN) // Could also be Code_INTERNAL, Code_DATA_LOSS
case 400:
return int32(code.Code_INVALID_ARGUMENT) // Could also be Code_OUT_OF_RANGE
case 504:
return int32(code.Code_DEADLINE_EXCEEDED)
case 404:
return int32(code.Code_NOT_FOUND)
case 409:
return int32(code.Code_ALREADY_EXISTS) // Could also be Code_ABORTED
case 403:
return int32(code.Code_PERMISSION_DENIED)
case 401:
return int32(code.Code_UNAUTHENTICATED)
case 429:
return int32(code.Code_RESOURCE_EXHAUSTED)
case 501:
return int32(code.Code_UNIMPLEMENTED)
case 503:
return int32(code.Code_UNAVAILABLE)
default:
return int32(code.Code_UNKNOWN)
}
}

View File

@ -0,0 +1,6 @@
#!/bin/bash
today=$(date +%Y%m%d)
sed -i -r -e 's/const Repo = "([0-9]{8})"/const Repo = "'$today'"/' $GOFILE

71
vendor/cloud.google.com/go/internal/version/version.go generated vendored Normal file
View File

@ -0,0 +1,71 @@
// Copyright 2016 Google LLC
//
// 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.
//go:generate ./update_version.sh
// Package version contains version information for Google Cloud Client
// Libraries for Go, as reported in request headers.
package version
import (
"runtime"
"strings"
"unicode"
)
// Repo is the current version of the client libraries in this
// repo. It should be a date in YYYYMMDD format.
const Repo = "20180226"
// Go returns the Go runtime version. The returned string
// has no whitespace.
func Go() string {
return goVersion
}
var goVersion = goVer(runtime.Version())
const develPrefix = "devel +"
func goVer(s string) string {
if strings.HasPrefix(s, develPrefix) {
s = s[len(develPrefix):]
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
s = s[:p]
}
return s
}
if strings.HasPrefix(s, "go1") {
s = s[2:]
var prerelease string
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
s, prerelease = s[:p], s[p:]
}
if strings.HasSuffix(s, ".") {
s += "0"
} else if strings.Count(s, ".") < 2 {
s += ".0"
}
if prerelease != "" {
s += "-" + prerelease
}
return s
}
return ""
}
func notSemverRune(r rune) bool {
return !strings.ContainsRune("0123456789.", r)
}

335
vendor/cloud.google.com/go/storage/acl.go generated vendored Normal file
View File

@ -0,0 +1,335 @@
// Copyright 2014 Google LLC
//
// 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 storage
import (
"context"
"net/http"
"reflect"
"cloud.google.com/go/internal/trace"
"google.golang.org/api/googleapi"
raw "google.golang.org/api/storage/v1"
)
// ACLRole is the level of access to grant.
type ACLRole string
const (
RoleOwner ACLRole = "OWNER"
RoleReader ACLRole = "READER"
RoleWriter ACLRole = "WRITER"
)
// ACLEntity refers to a user or group.
// They are sometimes referred to as grantees.
//
// It could be in the form of:
// "user-<userId>", "user-<email>", "group-<groupId>", "group-<email>",
// "domain-<domain>" and "project-team-<projectId>".
//
// Or one of the predefined constants: AllUsers, AllAuthenticatedUsers.
type ACLEntity string
const (
AllUsers ACLEntity = "allUsers"
AllAuthenticatedUsers ACLEntity = "allAuthenticatedUsers"
)
// ACLRule represents a grant for a role to an entity (user, group or team) for a
// Google Cloud Storage object or bucket.
type ACLRule struct {
Entity ACLEntity
EntityID string
Role ACLRole
Domain string
Email string
ProjectTeam *ProjectTeam
}
// ProjectTeam is the project team associated with the entity, if any.
type ProjectTeam struct {
ProjectNumber string
Team string
}
// ACLHandle provides operations on an access control list for a Google Cloud Storage bucket or object.
type ACLHandle struct {
c *Client
bucket string
object string
isDefault bool
userProject string // for requester-pays buckets
}
// Delete permanently deletes the ACL entry for the given entity.
func (a *ACLHandle) Delete(ctx context.Context, entity ACLEntity) (err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Delete")
defer func() { trace.EndSpan(ctx, err) }()
if a.object != "" {
return a.objectDelete(ctx, entity)
}
if a.isDefault {
return a.bucketDefaultDelete(ctx, entity)
}
return a.bucketDelete(ctx, entity)
}
// Set sets the role for the given entity.
func (a *ACLHandle) Set(ctx context.Context, entity ACLEntity, role ACLRole) (err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Set")
defer func() { trace.EndSpan(ctx, err) }()
if a.object != "" {
return a.objectSet(ctx, entity, role, false)
}
if a.isDefault {
return a.objectSet(ctx, entity, role, true)
}
return a.bucketSet(ctx, entity, role)
}
// List retrieves ACL entries.
func (a *ACLHandle) List(ctx context.Context) (rules []ACLRule, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.List")
defer func() { trace.EndSpan(ctx, err) }()
if a.object != "" {
return a.objectList(ctx)
}
if a.isDefault {
return a.bucketDefaultList(ctx)
}
return a.bucketList(ctx)
}
func (a *ACLHandle) bucketDefaultList(ctx context.Context) ([]ACLRule, error) {
var acls *raw.ObjectAccessControls
var err error
err = runWithRetry(ctx, func() error {
req := a.c.raw.DefaultObjectAccessControls.List(a.bucket)
a.configureCall(ctx, req)
acls, err = req.Do()
return err
})
if err != nil {
return nil, err
}
return toObjectACLRules(acls.Items), nil
}
func (a *ACLHandle) bucketDefaultDelete(ctx context.Context, entity ACLEntity) error {
return runWithRetry(ctx, func() error {
req := a.c.raw.DefaultObjectAccessControls.Delete(a.bucket, string(entity))
a.configureCall(ctx, req)
return req.Do()
})
}
func (a *ACLHandle) bucketList(ctx context.Context) ([]ACLRule, error) {
var acls *raw.BucketAccessControls
var err error
err = runWithRetry(ctx, func() error {
req := a.c.raw.BucketAccessControls.List(a.bucket)
a.configureCall(ctx, req)
acls, err = req.Do()
return err
})
if err != nil {
return nil, err
}
return toBucketACLRules(acls.Items), nil
}
func (a *ACLHandle) bucketSet(ctx context.Context, entity ACLEntity, role ACLRole) error {
acl := &raw.BucketAccessControl{
Bucket: a.bucket,
Entity: string(entity),
Role: string(role),
}
err := runWithRetry(ctx, func() error {
req := a.c.raw.BucketAccessControls.Update(a.bucket, string(entity), acl)
a.configureCall(ctx, req)
_, err := req.Do()
return err
})
if err != nil {
return err
}
return nil
}
func (a *ACLHandle) bucketDelete(ctx context.Context, entity ACLEntity) error {
return runWithRetry(ctx, func() error {
req := a.c.raw.BucketAccessControls.Delete(a.bucket, string(entity))
a.configureCall(ctx, req)
return req.Do()
})
}
func (a *ACLHandle) objectList(ctx context.Context) ([]ACLRule, error) {
var acls *raw.ObjectAccessControls
var err error
err = runWithRetry(ctx, func() error {
req := a.c.raw.ObjectAccessControls.List(a.bucket, a.object)
a.configureCall(ctx, req)
acls, err = req.Do()
return err
})
if err != nil {
return nil, err
}
return toObjectACLRules(acls.Items), nil
}
func (a *ACLHandle) objectSet(ctx context.Context, entity ACLEntity, role ACLRole, isBucketDefault bool) error {
type setRequest interface {
Do(opts ...googleapi.CallOption) (*raw.ObjectAccessControl, error)
Header() http.Header
}
acl := &raw.ObjectAccessControl{
Bucket: a.bucket,
Entity: string(entity),
Role: string(role),
}
var req setRequest
if isBucketDefault {
req = a.c.raw.DefaultObjectAccessControls.Update(a.bucket, string(entity), acl)
} else {
req = a.c.raw.ObjectAccessControls.Update(a.bucket, a.object, string(entity), acl)
}
a.configureCall(ctx, req)
return runWithRetry(ctx, func() error {
_, err := req.Do()
return err
})
}
func (a *ACLHandle) objectDelete(ctx context.Context, entity ACLEntity) error {
return runWithRetry(ctx, func() error {
req := a.c.raw.ObjectAccessControls.Delete(a.bucket, a.object, string(entity))
a.configureCall(ctx, req)
return req.Do()
})
}
func (a *ACLHandle) configureCall(ctx context.Context, call interface{ Header() http.Header }) {
vc := reflect.ValueOf(call)
vc.MethodByName("Context").Call([]reflect.Value{reflect.ValueOf(ctx)})
if a.userProject != "" {
vc.MethodByName("UserProject").Call([]reflect.Value{reflect.ValueOf(a.userProject)})
}
setClientHeader(call.Header())
}
func toObjectACLRules(items []*raw.ObjectAccessControl) []ACLRule {
var rs []ACLRule
for _, item := range items {
rs = append(rs, toObjectACLRule(item))
}
return rs
}
func toBucketACLRules(items []*raw.BucketAccessControl) []ACLRule {
var rs []ACLRule
for _, item := range items {
rs = append(rs, toBucketACLRule(item))
}
return rs
}
func toObjectACLRule(a *raw.ObjectAccessControl) ACLRule {
return ACLRule{
Entity: ACLEntity(a.Entity),
EntityID: a.EntityId,
Role: ACLRole(a.Role),
Domain: a.Domain,
Email: a.Email,
ProjectTeam: toObjectProjectTeam(a.ProjectTeam),
}
}
func toBucketACLRule(a *raw.BucketAccessControl) ACLRule {
return ACLRule{
Entity: ACLEntity(a.Entity),
EntityID: a.EntityId,
Role: ACLRole(a.Role),
Domain: a.Domain,
Email: a.Email,
ProjectTeam: toBucketProjectTeam(a.ProjectTeam),
}
}
func toRawObjectACL(rules []ACLRule) []*raw.ObjectAccessControl {
if len(rules) == 0 {
return nil
}
r := make([]*raw.ObjectAccessControl, 0, len(rules))
for _, rule := range rules {
r = append(r, rule.toRawObjectAccessControl("")) // bucket name unnecessary
}
return r
}
func toRawBucketACL(rules []ACLRule) []*raw.BucketAccessControl {
if len(rules) == 0 {
return nil
}
r := make([]*raw.BucketAccessControl, 0, len(rules))
for _, rule := range rules {
r = append(r, rule.toRawBucketAccessControl("")) // bucket name unnecessary
}
return r
}
func (r ACLRule) toRawBucketAccessControl(bucket string) *raw.BucketAccessControl {
return &raw.BucketAccessControl{
Bucket: bucket,
Entity: string(r.Entity),
Role: string(r.Role),
// The other fields are not settable.
}
}
func (r ACLRule) toRawObjectAccessControl(bucket string) *raw.ObjectAccessControl {
return &raw.ObjectAccessControl{
Bucket: bucket,
Entity: string(r.Entity),
Role: string(r.Role),
// The other fields are not settable.
}
}
func toBucketProjectTeam(p *raw.BucketAccessControlProjectTeam) *ProjectTeam {
if p == nil {
return nil
}
return &ProjectTeam{
ProjectNumber: p.ProjectNumber,
Team: p.Team,
}
}
func toObjectProjectTeam(p *raw.ObjectAccessControlProjectTeam) *ProjectTeam {
if p == nil {
return nil
}
return &ProjectTeam{
ProjectNumber: p.ProjectNumber,
Team: p.Team,
}
}

1181
vendor/cloud.google.com/go/storage/bucket.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

228
vendor/cloud.google.com/go/storage/copy.go generated vendored Normal file
View File

@ -0,0 +1,228 @@
// Copyright 2016 Google LLC
//
// 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 storage
import (
"context"
"errors"
"fmt"
"cloud.google.com/go/internal/trace"
raw "google.golang.org/api/storage/v1"
)
// CopierFrom creates a Copier that can copy src to dst.
// You can immediately call Run on the returned Copier, or
// you can configure it first.
//
// For Requester Pays buckets, the user project of dst is billed, unless it is empty,
// in which case the user project of src is billed.
func (dst *ObjectHandle) CopierFrom(src *ObjectHandle) *Copier {
return &Copier{dst: dst, src: src}
}
// A Copier copies a source object to a destination.
type Copier struct {
// ObjectAttrs are optional attributes to set on the destination object.
// Any attributes must be initialized before any calls on the Copier. Nil
// or zero-valued attributes are ignored.
ObjectAttrs
// RewriteToken can be set before calling Run to resume a copy
// operation. After Run returns a non-nil error, RewriteToken will
// have been updated to contain the value needed to resume the copy.
RewriteToken string
// ProgressFunc can be used to monitor the progress of a multi-RPC copy
// operation. If ProgressFunc is not nil and copying requires multiple
// calls to the underlying service (see
// https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite), then
// ProgressFunc will be invoked after each call with the number of bytes of
// content copied so far and the total size in bytes of the source object.
//
// ProgressFunc is intended to make upload progress available to the
// application. For example, the implementation of ProgressFunc may update
// a progress bar in the application's UI, or log the result of
// float64(copiedBytes)/float64(totalBytes).
//
// ProgressFunc should return quickly without blocking.
ProgressFunc func(copiedBytes, totalBytes uint64)
// The Cloud KMS key, in the form projects/P/locations/L/keyRings/R/cryptoKeys/K,
// that will be used to encrypt the object. Overrides the object's KMSKeyName, if
// any.
//
// Providing both a DestinationKMSKeyName and a customer-supplied encryption key
// (via ObjectHandle.Key) on the destination object will result in an error when
// Run is called.
DestinationKMSKeyName string
dst, src *ObjectHandle
}
// Run performs the copy.
func (c *Copier) Run(ctx context.Context) (attrs *ObjectAttrs, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Copier.Run")
defer func() { trace.EndSpan(ctx, err) }()
if err := c.src.validate(); err != nil {
return nil, err
}
if err := c.dst.validate(); err != nil {
return nil, err
}
if c.DestinationKMSKeyName != "" && c.dst.encryptionKey != nil {
return nil, errors.New("storage: cannot use DestinationKMSKeyName with a customer-supplied encryption key")
}
// Convert destination attributes to raw form, omitting the bucket.
// If the bucket is included but name or content-type aren't, the service
// returns a 400 with "Required" as the only message. Omitting the bucket
// does not cause any problems.
rawObject := c.ObjectAttrs.toRawObject("")
for {
res, err := c.callRewrite(ctx, rawObject)
if err != nil {
return nil, err
}
if c.ProgressFunc != nil {
c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize))
}
if res.Done { // Finished successfully.
return newObject(res.Resource), nil
}
}
}
func (c *Copier) callRewrite(ctx context.Context, rawObj *raw.Object) (*raw.RewriteResponse, error) {
call := c.dst.c.raw.Objects.Rewrite(c.src.bucket, c.src.object, c.dst.bucket, c.dst.object, rawObj)
call.Context(ctx).Projection("full")
if c.RewriteToken != "" {
call.RewriteToken(c.RewriteToken)
}
if c.DestinationKMSKeyName != "" {
call.DestinationKmsKeyName(c.DestinationKMSKeyName)
}
if c.PredefinedACL != "" {
call.DestinationPredefinedAcl(c.PredefinedACL)
}
if err := applyConds("Copy destination", c.dst.gen, c.dst.conds, call); err != nil {
return nil, err
}
if c.dst.userProject != "" {
call.UserProject(c.dst.userProject)
} else if c.src.userProject != "" {
call.UserProject(c.src.userProject)
}
if err := applySourceConds(c.src.gen, c.src.conds, call); err != nil {
return nil, err
}
if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
return nil, err
}
if err := setEncryptionHeaders(call.Header(), c.src.encryptionKey, true); err != nil {
return nil, err
}
var res *raw.RewriteResponse
var err error
setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { res, err = call.Do(); return err })
if err != nil {
return nil, err
}
c.RewriteToken = res.RewriteToken
return res, nil
}
// ComposerFrom creates a Composer that can compose srcs into dst.
// You can immediately call Run on the returned Composer, or you can
// configure it first.
//
// The encryption key for the destination object will be used to decrypt all
// source objects and encrypt the destination object. It is an error
// to specify an encryption key for any of the source objects.
func (dst *ObjectHandle) ComposerFrom(srcs ...*ObjectHandle) *Composer {
return &Composer{dst: dst, srcs: srcs}
}
// A Composer composes source objects into a destination object.
//
// For Requester Pays buckets, the user project of dst is billed.
type Composer struct {
// ObjectAttrs are optional attributes to set on the destination object.
// Any attributes must be initialized before any calls on the Composer. Nil
// or zero-valued attributes are ignored.
ObjectAttrs
dst *ObjectHandle
srcs []*ObjectHandle
}
// Run performs the compose operation.
func (c *Composer) Run(ctx context.Context) (attrs *ObjectAttrs, err error) {
ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Composer.Run")
defer func() { trace.EndSpan(ctx, err) }()
if err := c.dst.validate(); err != nil {
return nil, err
}
if len(c.srcs) == 0 {
return nil, errors.New("storage: at least one source object must be specified")
}
req := &raw.ComposeRequest{}
// Compose requires a non-empty Destination, so we always set it,
// even if the caller-provided ObjectAttrs is the zero value.
req.Destination = c.ObjectAttrs.toRawObject(c.dst.bucket)
for _, src := range c.srcs {
if err := src.validate(); err != nil {
return nil, err
}
if src.bucket != c.dst.bucket {
return nil, fmt.Errorf("storage: all source objects must be in bucket %q, found %q", c.dst.bucket, src.bucket)
}
if src.encryptionKey != nil {
return nil, fmt.Errorf("storage: compose source %s.%s must not have encryption key", src.bucket, src.object)
}
srcObj := &raw.ComposeRequestSourceObjects{
Name: src.object,
}
if err := applyConds("ComposeFrom source", src.gen, src.conds, composeSourceObj{srcObj}); err != nil {
return nil, err
}
req.SourceObjects = append(req.SourceObjects, srcObj)
}
call := c.dst.c.raw.Objects.Compose(c.dst.bucket, c.dst.object, req).Context(ctx)
if err := applyConds("ComposeFrom destination", c.dst.gen, c.dst.conds, call); err != nil {
return nil, err
}
if c.dst.userProject != "" {
call.UserProject(c.dst.userProject)
}
if c.PredefinedACL != "" {
call.DestinationPredefinedAcl(c.PredefinedACL)
}
if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
return nil, err
}
var obj *raw.Object
setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
if err != nil {
return nil, err
}
return newObject(obj), nil
}

Some files were not shown because too many files have changed in this diff Show More