Merge branch 'master' of https://github.com/hashicorp/packer into packer-provisioner-inspec

This commit is contained in:
xinau 2019-02-12 05:32:05 +00:00
commit c207451f7e
987 changed files with 67231 additions and 52958 deletions

View File

@ -129,8 +129,13 @@ to use `git push ...`.
we want to understand your thought process.
4. If we have requested changes, you can either make those changes or, if you
disagree with the suggested changes, we can have a conversation about our reasoning and agree on a path forward. This may be a multi-step process. Our view is that pull requests are a chance to collaborate, and we welcome
conversations about how to do things better.
disagree with the suggested changes, we can have a conversation about our
reasoning and agree on a path forward. This may be a multi-step process. Our
view is that pull requests are a chance to collaborate, and we welcome
conversations about how to do things better. It is the contributor's
responsibility to address any changes requested. While reviewers are happy to
give guidance, it is unsustainable for us to perform the coding work necessary
to get a PR into a mergeable state.
5. Once all outstanding comments and checklist items have been addressed, your
contribution will be merged! Merged PRs will be included in the next

View File

@ -1,5 +1,11 @@
**DELETE THIS TEMPLATE BEFORE SUBMITTING**
In order to have a good experience with our community, we recommend that you
read the contributing guidelines for making a PR, and understand the lifecycle
of a Packer PR:
https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.md#opening-an-pull-request
Describe the change you are making here!
Please include tests. Check out these examples:

View File

@ -1,4 +1,6 @@
## 1.3.4 (upcoming)
## 1.3.5 (upcoming)
## 1.3.4 (January 30, 2019)
### IMPROVEMENTS:
* builder/alicloud: delete copied image and snapshots if corresponding options
are specified [GH-7050]
@ -8,21 +10,69 @@
per builder [GH-7080]
* builder/amazon: don't Cleanup Temp Keys when there is no communicator to
avoid a panic [GH-7100] [GH-7095]
* builder/amazon: Don't try to guess region from metadata if not set & update
aws-sdk-go [GH-7230]
* builder/amazon: Import error messages should now contain reason for failure
[GH-7207]
* builder/azure: add certificate authentication [GH-7189]
* builder/azure: allow to configure disk caching [GH-7061]
* builder/azure: use deallocate instead of just power-off [GH-7203]
* builder/hyperv: Add support for legacy network adapters on Hyper-V. [GH-7128]
* builder/hyperv: Allow user to set `version` option in the New-VM command.
[GH-7136]
* builder/openstack: Add `volume_size` option [GH-7130]
* builder/openstack: Don't require network v2 [GH-6933]
* builder/openstack: Support for tagging new images [GH-7037]
* builder/qemu: Add configuration options to specify cpu count and memory size
[GH-7156]
* builder/qemu: Add support for whpx accelerator to qemu builder [GH-7151]
* builder/vmware: Escape query as suggested in issue #7200 [GH-7223]
* core/shell: Add env vars "PACKER_HTTP_IP" and "PACKER_HTTP_PORT" to shell
provisioners [GH-7075]
* core: allow to use `-except` on post-processors [GH-7183]
* core: Clean up internal handling and creation of temporary directories
[GH-7102]
* core: Deprecate mitchellh/go-homedir package in favor of os/user [GH-7062]
* core: Download checksum match failures will now log the received checksum.
[GH-7210]
* core: Explicitly set ProxyFromEnvironment in httpclients when creating an aws
session [GH-7226]
* core: make packer inspect not print sensitive variables [GH-7084]
* post-processor/google: Add new `guest-os-features` option. [GH-7218]
* postprocessor/docker-import: Added `change` support [GH-7127]
* provisioner/ansible-remote: add `-o IdentitiesOnly=yes`as a default flag
[GH-7115]
* provisioner/chef-client: Elevated support for chef-client provisioner
[GH-7078]
* provisioner/puppet: Elevated support for puppet-* provisioner [GH-7078]
* provisioner/windows-restart: wait for already-scheduled reboot [GH-7056] and
ignore reboot specific errors [GH-7071]
### BUG FIXES:
* builder/azure: Ensure the Windows Guest Agent is fully functional before
Sysprep is executed. [GH-7176]
* builder/azure: Fix snapshot regression [GH-7111]
* builder/docker: Ensure that entrypoint and arguments get passed to docker,
not the image. [GH-7091]
* builder/hcloud: fix go mod dependency [GH-7099]
* builder/hcloud: prevent panic when ssh key was not passed [GH-7118]
* builder/hyperv: Fix the Hyper-V gen 1 guest boot order. [GH-7147]
* builder/hyperv: hyper-v builder no longer ignores `ssh_host` option.
[GH-7154]
* builder/oracle-oci: Fix crash that occurs when image is nil [GH-7126]
* builder/parallels: Fix attaching prl tools [GH-7158]
* builder/virtualbox: Fix handling of portcount argument for version 6 beta
[GH-7174] [GH-7094]
* builder/vmware: Fix bug caused by 'nil' dir field in artifact struct when
building locally [GH-7116]
* communicator/docker: Fix docker file provisioner on Windows [GH-7163]
* core: prioritize AppData over default user directory ( UserProfile )
[GH-7166]
* core: removed a flaky race condition in tests [GH-7119]
* postprocessor/vsphere: Stop setting HDDOrder, since it was breaking uploads
[GH-7108]
## 1.3.3 (December 5, 2018)
### IMPROVEMENTS:

View File

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

View File

@ -9,9 +9,11 @@ import (
"log"
"runtime"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@ -190,7 +192,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
if err != nil {
return nil, err
}
ec2conn := ec2.New(session)
ec2conn := ec2.New(session, &aws.Config{
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
wrappedCommand := func(command string) (string, error) {
ctx := b.config.ctx
@ -201,6 +205,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("access_config", &b.config.AccessConfig)
state.Put("ami_config", &b.config.AMIConfig)
state.Put("ec2", ec2conn)
state.Put("awsSession", session)
state.Put("hook", hook)

View File

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
@ -14,6 +13,7 @@ import (
"syscall"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
)
// Communicator is a special communicator that works by executing
@ -67,7 +67,7 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error {
dst = filepath.Join(c.Chroot, dst)
log.Printf("Uploading to chroot dir: %s", dst)
tf, err := ioutil.TempFile("", "packer-amazon-chroot")
tf, err := tmp.File("packer-amazon-chroot")
if err != nil {
return fmt.Errorf("Error preparing shell script: %s", err)
}

View File

@ -6,19 +6,31 @@ import (
"log"
"net/http"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/hashicorp/go-cleanhttp"
cleanhttp "github.com/hashicorp/go-cleanhttp"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/template/interpolate"
vaultapi "github.com/hashicorp/vault/api"
)
type VaultAWSEngineOptions struct {
Name string `mapstructure:"name"`
RoleARN string `mapstructure:"role_arn"`
TTL string `mapstructure:"ttl"`
EngineName string `mapstructure:"engine_name"`
}
func (v *VaultAWSEngineOptions) Empty() bool {
return len(v.Name) == 0 && len(v.RoleARN) == 0 &&
len(v.EngineName) == 0 && len(v.TTL) == 0
}
// AccessConfig is for common configuration related to AWS access
type AccessConfig struct {
AccessKey string `mapstructure:"access_key"`
@ -33,6 +45,7 @@ type AccessConfig struct {
SkipMetadataApiCheck bool `mapstructure:"skip_metadata_api_check"`
Token string `mapstructure:"token"`
session *session.Session
VaultAWSEngine VaultAWSEngineOptions `mapstructure:"vault_aws_engine"`
getEC2Connection func() ec2iface.EC2API
}
@ -45,21 +58,15 @@ func (c *AccessConfig) Session() (*session.Session, error) {
}
config := aws.NewConfig().WithCredentialsChainVerboseErrors(true)
staticCreds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
if _, err := staticCreds.Get(); err != credentials.ErrStaticCredentialsEmpty {
config.WithCredentials(staticCreds)
}
// default is 3, and when it was causing failures for users being throttled
// retries are exponentially backed off.
config = config.WithMaxRetries(8)
region, err := c.region()
if err != nil {
return nil, fmt.Errorf("Could not get region, "+
"probably because it's not set or we're not running on AWS. %s", err)
if c.RawRegion != "" {
config = config.WithRegion(c.RawRegion)
}
config = config.WithRegion(region)
if c.CustomEndpointEc2 != "" {
config = config.WithEndpoint(c.CustomEndpointEc2)
@ -88,24 +95,24 @@ func (c *AccessConfig) Session() (*session.Session, error) {
}
}
if sess, err := session.NewSessionWithOptions(opts); err != nil {
sess, err := session.NewSessionWithOptions(opts)
if err != nil {
return nil, err
} else {
log.Printf("Found region %s", *sess.Config.Region)
c.session = sess
cp, err := c.session.Config.Credentials.Get()
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
return nil, fmt.Errorf("No valid credential sources found for AWS Builder. " +
"Please see https://www.packer.io/docs/builders/amazon.html#specifying-amazon-credentials " +
"for more information on providing credentials for the AWS Builder.")
} else {
return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
}
}
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
}
log.Printf("Found region %s", *sess.Config.Region)
c.session = sess
cp, err := c.session.Config.Credentials.Get()
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
return nil, fmt.Errorf("No valid credential sources found for AWS Builder. " +
"Please see https://www.packer.io/docs/builders/amazon.html#specifying-amazon-credentials " +
"for more information on providing credentials for the AWS Builder.")
} else {
return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
}
}
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
if c.DecodeAuthZMessages {
DecodeAuthZMessages(c.session)
@ -130,24 +137,37 @@ func (c *AccessConfig) IsChinaCloud() bool {
return strings.HasPrefix(c.SessionRegion(), "cn-")
}
// metadataRegion returns the region from the metadata service
func (c *AccessConfig) metadataRegion() (string, error) {
client := cleanhttp.DefaultClient()
// Keep the default timeout (100ms) low as we don't want to wait in non-EC2 environments
client.Timeout = 100 * time.Millisecond
ec2meta := ec2metadata.New(session.New(), &aws.Config{
HTTPClient: client,
})
return ec2meta.Region()
}
func (c *AccessConfig) region() (string, error) {
if c.RawRegion != "" {
return c.RawRegion, nil
func (c *AccessConfig) GetCredsFromVault() error {
// const EnvVaultAddress = "VAULT_ADDR"
// const EnvVaultToken = "VAULT_TOKEN"
vaultConfig := vaultapi.DefaultConfig()
cli, err := vaultapi.NewClient(vaultConfig)
if err != nil {
return fmt.Errorf("Error getting Vault client: %s", err)
}
return c.metadataRegion()
if c.VaultAWSEngine.EngineName == "" {
c.VaultAWSEngine.EngineName = "aws"
}
path := fmt.Sprintf("/%s/creds/%s", c.VaultAWSEngine.EngineName,
c.VaultAWSEngine.Name)
secret, err := cli.Logical().Read(path)
if err != nil {
return fmt.Errorf("Error reading vault secret: %s", err)
}
if secret == nil {
return fmt.Errorf("Vault Secret does not exist at the given path.")
}
c.AccessKey = secret.Data["access_key"].(string)
c.SecretKey = secret.Data["secret_key"].(string)
token := secret.Data["security_token"]
if token != nil {
c.Token = token.(string)
} else {
c.Token = ""
}
return nil
}
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
@ -156,8 +176,23 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
if c.SkipMetadataApiCheck {
log.Println("(WARN) skip_metadata_api_check ignored.")
}
// Either both access and secret key must be set or neither of them should
// be.
// Make sure it's obvious from the config how we're getting credentials:
// Vault, Packer config, or environemnt.
if !c.VaultAWSEngine.Empty() {
if len(c.AccessKey) > 0 {
errs = append(errs,
fmt.Errorf("If you have set vault_aws_engine, you must not set"+
" the access_key or secret_key."))
}
// Go ahead and grab those credentials from Vault now, so we can set
// the keys and token now.
err := c.GetCredsFromVault()
if err != nil {
errs = append(errs, err)
}
}
if (len(c.AccessKey) > 0) != (len(c.SecretKey) > 0) {
errs = append(errs,
fmt.Errorf("`access_key` and `secret_key` must both be either set or not set."))
@ -174,5 +209,10 @@ func (c *AccessConfig) NewEC2Connection() (ec2iface.EC2API, error) {
if err != nil {
return nil, err
}
return ec2.New(sess), nil
ec2conn := ec2.New(sess, &aws.Config{
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
return ec2conn, nil
}

View File

@ -9,6 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
)
@ -70,7 +71,8 @@ func (a *Artifact) Destroy() error {
log.Printf("Deregistering image ID (%s) from region (%s)", imageId, region)
regionConn := ec2.New(a.Session, &aws.Config{
Region: aws.String(region),
Region: aws.String(region),
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
// Get image metadata

View File

@ -34,6 +34,9 @@ func (c *AccessConfig) ValidateRegion(regions ...string) error {
var invalidRegions []string
for _, region := range regions {
if region == "" {
continue
}
found := false
for _, validRegion := range validRegions {
if region == validRegion {

View File

@ -7,7 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -100,8 +100,9 @@ func amiRegionCopy(ctx context.Context, state multistep.StateBag, config *Access
isEncrypted = true
}
regionconn := ec2.New(session.Copy(&aws.Config{
Region: aws.String(target)},
))
Region: aws.String(target),
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
}))
resp, err := regionconn.CopyImage(&ec2.CopyImageInput{
SourceRegion: &source,

View File

@ -9,6 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
retry "github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -35,7 +36,8 @@ func (s *StepCreateTags) Run(_ context.Context, state multistep.StateBag) multis
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami))
regionConn := ec2.New(session, &aws.Config{
Region: aws.String(region),
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
Region: aws.String(region),
})
// Retrieve image list for given AMI

View File

@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -37,8 +38,9 @@ func (s *StepDeregisterAMI) Run(_ context.Context, state multistep.StateBag) mul
}
regionconn := ec2.New(session.Copy(&aws.Config{
Region: aws.String(region)},
))
Region: aws.String(region),
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
}))
resp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
Owners: aws.StringSlice([]string{"self"}),

View File

@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -145,7 +146,8 @@ func (s *StepModifyAMIAttributes) Run(_ context.Context, state multistep.StateBa
for region, ami := range amis {
ui.Say(fmt.Sprintf("Modifying attributes on AMI (%s)...", ami))
regionConn := ec2.New(session, &aws.Config{
Region: aws.String(region),
Region: aws.String(region),
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
for name, input := range options {
ui.Message(fmt.Sprintf("Modifying: %s", name))
@ -165,7 +167,8 @@ func (s *StepModifyAMIAttributes) Run(_ context.Context, state multistep.StateBa
for _, snapshot := range region_snapshots {
ui.Say(fmt.Sprintf("Modifying attributes on snapshot (%s)...", snapshot))
regionConn := ec2.New(session, &aws.Config{
Region: aws.String(region),
Region: aws.String(region),
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
for name, input := range snapshotOptions {
ui.Message(fmt.Sprintf("Modifying: %s", name))

View File

@ -3,9 +3,12 @@ package common
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
retry "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -20,6 +23,51 @@ type StepPreValidate struct {
func (s *StepPreValidate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if accessConfig, ok := state.GetOk("access_config"); ok {
accessconf := accessConfig.(*AccessConfig)
if !accessconf.VaultAWSEngine.Empty() {
// loop over the authentication a few times to give vault-created creds
// time to become eventually-consistent
ui.Say("You're using Vault-generated AWS credentials. It may take a " +
"few moments for them to become available on AWS. Waiting...")
err := retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
ec2conn, err := accessconf.NewEC2Connection()
if err != nil {
return true, err
}
_, err = listEC2Regions(ec2conn)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "AuthFailure" {
log.Printf("Waiting for Vault-generated AWS credentials" +
" to pass authentication... trying again.")
return false, nil
}
return true, err
}
return true, nil
})
if err != nil {
state.Put("error", fmt.Errorf("Was unable to Authenticate to AWS using Vault-"+
"Generated Credentials within the retry timeout."))
return multistep.ActionHalt
}
}
if amiConfig, ok := state.GetOk("ami_config"); ok {
amiconf := amiConfig.(*AMIConfig)
if !amiconf.AMISkipRegionValidation {
regionsToValidate := append(amiconf.AMIRegions, accessconf.RawRegion)
err := accessconf.ValidateRegion(regionsToValidate...)
if err != nil {
state.Put("error", fmt.Errorf("error validating regions: %v", err))
return multistep.ActionHalt
}
}
}
}
if s.ForceDeregister {
ui.Say("Force Deregister flag found, skipping prevalidating AMI Name")
return multistep.ActionContinue

View File

@ -9,9 +9,11 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
@ -90,18 +92,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
if err != nil {
return nil, err
}
if !b.config.AMISkipRegionValidation {
regionsToValidate := append(b.config.AMIRegions, b.config.RawRegion)
err := b.config.AccessConfig.ValidateRegion(regionsToValidate...)
if err != nil {
return nil, fmt.Errorf("error validating regions: %v", err)
}
}
ec2conn := ec2.New(session)
ec2conn := ec2.New(session, &aws.Config{
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("access_config", &b.config.AccessConfig)
state.Put("ami_config", &b.config.AMIConfig)
state.Put("ec2", ec2conn)
state.Put("awsSession", session)
state.Put("hook", hook)

View File

@ -7,9 +7,11 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
@ -105,11 +107,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
if err != nil {
return nil, err
}
ec2conn := ec2.New(session)
ec2conn := ec2.New(session, &aws.Config{
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("access_config", &b.config.AccessConfig)
state.Put("ami_config", &b.config.AMIConfig)
state.Put("ec2", ec2conn)
state.Put("awsSession", session)
state.Put("hook", hook)

View File

@ -6,9 +6,11 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
@ -94,11 +96,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
if err != nil {
return nil, err
}
ec2conn := ec2.New(session)
ec2conn := ec2.New(session, &aws.Config{
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("access_config", &b.config.AccessConfig)
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)

View File

@ -9,9 +9,11 @@ import (
"os"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
@ -175,11 +177,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
if err != nil {
return nil, err
}
ec2conn := ec2.New(session)
ec2conn := ec2.New(session, &aws.Config{
HTTPClient: commonhelper.HttpClientWithEnvironmentProxy(),
})
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("access_config", &b.config.AccessConfig)
state.Put("ami_config", &b.config.AMIConfig)
state.Put("ec2", ec2conn)
state.Put("awsSession", session)
state.Put("hook", hook)

View File

@ -2,44 +2,9 @@ package arm
import (
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
)
type Authenticate struct {
env azure.Environment
clientID string
clientSecret string
tenantID string
}
func NewAuthenticate(env azure.Environment, clientID, clientSecret, tenantID string) *Authenticate {
return &Authenticate{
env: env,
clientID: clientID,
clientSecret: clientSecret,
tenantID: tenantID,
}
}
func (a *Authenticate) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
return a.getServicePrincipalTokenWithResource(a.env.ResourceManagerEndpoint)
}
func (a *Authenticate) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := adal.NewOAuthConfig(a.env.ActiveDirectoryEndpoint, a.tenantID)
if err != nil {
return nil, err
}
if a.clientID == "" && a.clientSecret == "" {
return adal.NewServicePrincipalTokenFromMSI("http://169.254.169.254/metadata/identity/oauth2/token", resource)
}
spt, err := adal.NewServicePrincipalToken(
*oauthConfig,
a.clientID,
a.clientSecret,
resource)
return spt, err
type oAuthTokenProvider interface {
getServicePrincipalToken() (*adal.ServicePrincipalToken, error)
getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error)
}

View File

@ -0,0 +1,155 @@
package arm
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/Azure/go-autorest/autorest/azure"
jwt "github.com/dgrijalva/jwt-go"
)
func NewCertOAuthTokenProvider(env azure.Environment, clientID, clientCertPath, tenantID string) (oAuthTokenProvider, error) {
cert, key, err := readCert(clientCertPath)
if err != nil {
return nil, fmt.Errorf("Error reading certificate: %v", err)
}
audience := fmt.Sprintf("%s%s/oauth2/token", env.ActiveDirectoryEndpoint, tenantID)
jwt, err := makeJWT(clientID, audience, cert, key, time.Hour, true)
if err != nil {
return nil, fmt.Errorf("Error generating JWT: %v", err)
}
return NewJWTOAuthTokenProvider(env, clientID, jwt, tenantID), nil
}
// Creates a new JSON Web Token to be used as bearer JWT to authenticate
// to the Azure AD token endpoint to retrieve an access token for `audience`.
// If the full certificate is included in the token, then issuer/subject name
// could be used to authenticate if configured by the identity provider (AAD).
func makeJWT(clientID string, audience string,
cert *x509.Certificate, privatekey interface{},
validFor time.Duration, includeFullCertificate bool) (string, error) {
// The jti (JWT ID) claim provides a unique identifier for the JWT.
// See https://tools.ietf.org/html/rfc7519#section-4.1.7
jti := make([]byte, 20)
_, err := rand.Read(jti)
if err != nil {
return "", err
}
var token *jwt.Token
if cert.PublicKeyAlgorithm == x509.RSA {
token = jwt.New(jwt.SigningMethodRS256)
} else if cert.PublicKeyAlgorithm == x509.ECDSA {
token = jwt.New(jwt.SigningMethodES256)
} else {
return "", fmt.Errorf("Don't know how to handle this type of key algorithm: %v", cert.PublicKeyAlgorithm)
}
hasher := sha1.New()
if _, err := hasher.Write(cert.Raw); err != nil {
return "", err
}
thumbprint := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
// X.509 thumbprint, see https://tools.ietf.org/html/rfc7515#section-4.1.7
token.Header["x5t"] = thumbprint
if includeFullCertificate {
// X.509 certificate (chain), see https://tools.ietf.org/html/rfc7515#section-4.1.6
token.Header["x5c"] = []string{base64.StdEncoding.EncodeToString(cert.Raw)}
}
token.Claims = jwt.MapClaims{
// See https://tools.ietf.org/html/rfc7519#section-4.1
"aud": audience,
"iss": clientID,
"sub": clientID,
"jti": base64.URLEncoding.EncodeToString(jti),
"nbf": time.Now().Unix(),
"exp": time.Now().Add(validFor).Unix(),
}
return token.SignedString(privatekey)
}
func readCert(file string) (cert *x509.Certificate, key interface{}, err error) {
f, err := os.Open(file)
if err != nil {
return nil, nil, err
}
defer f.Close()
d, err := ioutil.ReadAll(f)
if err != nil {
return nil, nil, err
}
blocks := []*pem.Block{}
for len(d) > 0 {
var b *pem.Block
b, d = pem.Decode(d)
if b == nil {
break
}
blocks = append(blocks, b)
}
certs := []*x509.Certificate{}
for _, block := range blocks {
if block.Type == "CERTIFICATE" {
c, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(
"Failed to read certificate block: %v", err)
}
certs = append(certs, c)
} else if block.Type == "PRIVATE KEY" {
key, err = x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(
"Failed to read private key block: %v", err)
}
}
// Don't care about other types of blocks, ignore
}
if key == nil {
return nil, nil, fmt.Errorf("Did not find private key in pem file")
}
// find the certificate that belongs to the private key by comparing the public keys
switch key := key.(type) {
case *rsa.PrivateKey:
for _, c := range certs {
if cp, ok := c.PublicKey.(*rsa.PublicKey); ok &&
(cp.N.Cmp(key.PublicKey.N) == 0) {
cert = c
}
}
case *ecdsa.PrivateKey:
for _, c := range certs {
if cp, ok := c.PublicKey.(*ecdsa.PublicKey); ok &&
(cp.X.Cmp(key.PublicKey.X) == 0) &&
(cp.Y.Cmp(key.PublicKey.Y) == 0) {
cert = c
}
}
}
if cert == nil {
return nil, nil, fmt.Errorf("Did not find certificate belonging to private key in pem file")
}
return cert, key, nil
}

View File

@ -0,0 +1,40 @@
package arm
import (
"fmt"
"strings"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
packerAzureCommon "github.com/hashicorp/packer/builder/azure/common"
)
func NewDeviceFlowOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider {
return &deviceflowOauthTokenProvider{
env: env,
say: say,
tenantID: tenantID,
}
}
type deviceflowOauthTokenProvider struct {
env azure.Environment
say func(string)
tenantID string
}
func (tp *deviceflowOauthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint)
}
func (tp *deviceflowOauthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) {
if resource == tp.env.ServiceManagementEndpoint {
tp.say("Getting auth token for Service management endpoint")
} else if resource == strings.TrimRight(tp.env.KeyVaultEndpoint, "/") {
tp.say("Getting token for Vault resource")
} else {
tp.say(fmt.Sprintf("Getting token for %s", resource))
}
return packerAzureCommon.Authenticate(tp.env, tp.tenantID, tp.say, resource)
}

View File

@ -0,0 +1,43 @@
package arm
import (
"net/url"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
)
// for clientID/bearer JWT auth
type jwtOAuthTokenProvider struct {
env azure.Environment
clientID, clientJWT, tenantID string
}
func NewJWTOAuthTokenProvider(env azure.Environment, clientID, clientJWT, tenantID string) oAuthTokenProvider {
return &jwtOAuthTokenProvider{env, clientID, clientJWT, tenantID}
}
func (tp *jwtOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint)
}
func (tp *jwtOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := adal.NewOAuthConfig(tp.env.ActiveDirectoryEndpoint, tp.tenantID)
if err != nil {
return nil, err
}
return adal.NewServicePrincipalTokenWithSecret(
*oauthConfig,
tp.clientID,
resource,
tp)
}
// implements github.com/Azure/go-autorest/autorest/adal.ServicePrincipalSecret
func (tp *jwtOAuthTokenProvider) SetAuthenticationValues(
t *adal.ServicePrincipalToken, v *url.Values) error {
v.Set("client_assertion", tp.clientJWT)
v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
return nil
}

View File

@ -0,0 +1,23 @@
package arm
import (
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
)
// for managed identity auth
type msiOAuthTokenProvider struct {
env azure.Environment
}
func NewMSIOAuthTokenProvider(env azure.Environment) oAuthTokenProvider {
return &msiOAuthTokenProvider{env}
}
func (tp *msiOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint)
}
func (tp *msiOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) {
return adal.NewServicePrincipalTokenFromMSI("http://169.254.169.254/metadata/identity/oauth2/token", resource)
}

View File

@ -0,0 +1,35 @@
package arm
import (
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
)
// for clientID/secret auth
type secretOAuthTokenProvider struct {
env azure.Environment
clientID, clientSecret, tenantID string
}
func NewSecretOAuthTokenProvider(env azure.Environment, clientID, clientSecret, tenantID string) oAuthTokenProvider {
return &secretOAuthTokenProvider{env, clientID, clientSecret, tenantID}
}
func (tp *secretOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint)
}
func (tp *secretOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := adal.NewOAuthConfig(tp.env.ActiveDirectoryEndpoint, tp.tenantID)
if err != nil {
return nil, err
}
spt, err := adal.NewServicePrincipalToken(
*oauthConfig,
tp.clientID,
tp.clientSecret,
resource)
return spt, err
}

View File

@ -9,8 +9,8 @@ import (
// Behavior is the most important thing to assert for ServicePrincipalToken, but
// that cannot be done in a unit test because it involves network access. Instead,
// I assert the expected inertness of this class.
func TestNewAuthenticate(t *testing.T) {
testSubject := NewAuthenticate(azure.PublicCloud, "clientID", "clientString", "tenantID")
func TestNewSecretOAuthTokenProvider(t *testing.T) {
testSubject := NewSecretOAuthTokenProvider(azure.PublicCloud, "clientID", "clientString", "tenantID")
spn, err := testSubject.getServicePrincipalToken()
if err != nil {
t.Fatalf(err.Error())

View File

@ -385,52 +385,7 @@ func (b *Builder) setImageParameters(stateBag multistep.StateBag) {
}
func (b *Builder) getServicePrincipalTokens(say func(string)) (*adal.ServicePrincipalToken, *adal.ServicePrincipalToken, error) {
var servicePrincipalToken *adal.ServicePrincipalToken
var servicePrincipalTokenVault *adal.ServicePrincipalToken
var err error
if b.config.useDeviceLogin {
say("Getting auth token for Service management endpoint")
servicePrincipalToken, err = packerAzureCommon.Authenticate(*b.config.cloudEnvironment, b.config.TenantID, say, b.config.cloudEnvironment.ServiceManagementEndpoint)
if err != nil {
return nil, nil, err
}
say("Getting token for Vault resource")
servicePrincipalTokenVault, err = packerAzureCommon.Authenticate(*b.config.cloudEnvironment, b.config.TenantID, say, strings.TrimRight(b.config.cloudEnvironment.KeyVaultEndpoint, "/"))
if err != nil {
return nil, nil, err
}
} else {
auth := NewAuthenticate(*b.config.cloudEnvironment, b.config.ClientID, b.config.ClientSecret, b.config.TenantID)
servicePrincipalToken, err = auth.getServicePrincipalToken()
if err != nil {
return nil, nil, err
}
servicePrincipalTokenVault, err = auth.getServicePrincipalTokenWithResource(
strings.TrimRight(b.config.cloudEnvironment.KeyVaultEndpoint, "/"))
if err != nil {
return nil, nil, err
}
}
err = servicePrincipalToken.EnsureFresh()
if err != nil {
return nil, nil, err
}
err = servicePrincipalTokenVault.EnsureFresh()
if err != nil {
return nil, nil, err
}
return servicePrincipalToken, servicePrincipalTokenVault, nil
return b.config.ClientConfig.getServicePrincipalTokens(say)
}
func getObjectIdFromToken(ui packer.Ui, token *adal.ServicePrincipalToken) string {

View File

@ -0,0 +1,222 @@
package arm
import (
"fmt"
"os"
"strings"
"time"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
jwt "github.com/dgrijalva/jwt-go"
"github.com/hashicorp/packer/packer"
)
// ClientConfig allows for various ways to authenticate Azure clients
type ClientConfig struct {
// Describes where API's are
CloudEnvironmentName string `mapstructure:"cloud_environment_name"`
cloudEnvironment *azure.Environment
// Authentication fields
// Client ID
ClientID string `mapstructure:"client_id"`
// Client secret/password
ClientSecret string `mapstructure:"client_secret"`
// Certificate path for client auth
ClientCertPath string `mapstructure:"client_cert_path"`
// JWT bearer token for client auth (RFC 7523, Sec. 2.2)
ClientJWT string `mapstructure:"client_jwt"`
ObjectID string `mapstructure:"object_id"`
TenantID string `mapstructure:"tenant_id"`
SubscriptionID string `mapstructure:"subscription_id"`
}
const DefaultCloudEnvironmentName = "Public"
func (c *ClientConfig) provideDefaultValues() {
if c.CloudEnvironmentName == "" {
c.CloudEnvironmentName = DefaultCloudEnvironmentName
}
}
func (c *ClientConfig) setCloudEnvironment() error {
lookup := map[string]string{
"CHINA": "AzureChinaCloud",
"CHINACLOUD": "AzureChinaCloud",
"AZURECHINACLOUD": "AzureChinaCloud",
"GERMAN": "AzureGermanCloud",
"GERMANCLOUD": "AzureGermanCloud",
"AZUREGERMANCLOUD": "AzureGermanCloud",
"GERMANY": "AzureGermanCloud",
"GERMANYCLOUD": "AzureGermanCloud",
"AZUREGERMANYCLOUD": "AzureGermanCloud",
"PUBLIC": "AzurePublicCloud",
"PUBLICCLOUD": "AzurePublicCloud",
"AZUREPUBLICCLOUD": "AzurePublicCloud",
"USGOVERNMENT": "AzureUSGovernmentCloud",
"USGOVERNMENTCLOUD": "AzureUSGovernmentCloud",
"AZUREUSGOVERNMENTCLOUD": "AzureUSGovernmentCloud",
}
name := strings.ToUpper(c.CloudEnvironmentName)
envName, ok := lookup[name]
if !ok {
return fmt.Errorf("There is no cloud environment matching the name '%s'!", c.CloudEnvironmentName)
}
env, err := azure.EnvironmentFromName(envName)
c.cloudEnvironment = &env
return err
}
func (c ClientConfig) assertRequiredParametersSet(errs *packer.MultiError) {
/////////////////////////////////////////////
// Authentication via OAUTH
// Check if device login is being asked for, and is allowed.
//
// Device login is enabled if the user only defines SubscriptionID and not
// ClientID, ClientSecret, and TenantID.
//
// Device login is not enabled for Windows because the WinRM certificate is
// readable by the ObjectID of the App. There may be another way to handle
// this case, but I am not currently aware of it - send feedback.
if c.useMSI() {
return
}
if c.useDeviceLogin() {
return
}
if c.SubscriptionID != "" && c.ClientID != "" &&
c.ClientSecret != "" &&
c.ClientCertPath == "" &&
c.ClientJWT == "" {
// Service principal using secret
return
}
if c.SubscriptionID != "" && c.ClientID != "" &&
c.ClientSecret == "" &&
c.ClientCertPath != "" &&
c.ClientJWT == "" {
// Service principal using certificate
if _, err := os.Stat(c.ClientCertPath); err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("client_cert_path is not an accessible file: %v", err))
}
return
}
if c.SubscriptionID != "" && c.ClientID != "" &&
c.ClientSecret == "" &&
c.ClientCertPath == "" &&
c.ClientJWT != "" {
// Service principal using JWT
// Check that JWT is valid for at least 5 more minutes
p := jwt.Parser{}
claims := jwt.StandardClaims{}
token, _, err := p.ParseUnverified(c.ClientJWT, &claims)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("client_jwt is not a JWT: %v", err))
} else {
if claims.ExpiresAt < time.Now().Add(5*time.Minute).Unix() {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("client_jwt will expire within 5 minutes, please use a JWT that is valid for at least 5 minutes"))
}
if t, ok := token.Header["x5t"]; !ok || t == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("client_jwt is missing the x5t header value, which is required for bearer JWT client authentication to Azure"))
}
}
return
}
errs = packer.MultiErrorAppend(errs, fmt.Errorf("No valid set of authentication values specified:\n"+
" to use the Managed Identity of the current machine, do not specify any of the fields below\n"+
" to use interactive user authentication, specify only subscription_id\n"+
" to use an Azure Active Directory service principal, specify either:\n"+
" - subscription_id, client_id and client_secret\n"+
" - subscription_id, client_id and client_cert_path\n"+
" - subscription_id, client_id and client_jwt."))
}
func (c ClientConfig) useDeviceLogin() bool {
return c.SubscriptionID != "" &&
c.ClientID == "" &&
c.ClientSecret == "" &&
c.ClientJWT == "" &&
c.ClientCertPath == ""
}
func (c ClientConfig) useMSI() bool {
return c.SubscriptionID == "" &&
c.ClientID == "" &&
c.ClientSecret == "" &&
c.ClientJWT == "" &&
c.ClientCertPath == "" &&
c.TenantID == ""
}
func (c ClientConfig) getServicePrincipalTokens(
say func(string)) (
servicePrincipalToken *adal.ServicePrincipalToken,
servicePrincipalTokenVault *adal.ServicePrincipalToken,
err error) {
tenantID := c.TenantID
var auth oAuthTokenProvider
if c.useDeviceLogin() {
say("Getting tokens using device flow")
auth = NewDeviceFlowOAuthTokenProvider(*c.cloudEnvironment, say, tenantID)
} else if c.useMSI() {
say("Getting tokens using Managed Identity for Azure")
auth = NewMSIOAuthTokenProvider(*c.cloudEnvironment)
} else if c.ClientSecret != "" {
say("Getting tokens using client secret")
auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, tenantID)
} else if c.ClientCertPath != "" {
say("Getting tokens using client certificate")
auth, err = NewCertOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientCertPath, tenantID)
if err != nil {
return nil, nil, err
}
} else {
say("Getting tokens using client bearer JWT")
auth = NewJWTOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientJWT, tenantID)
}
servicePrincipalToken, err = auth.getServicePrincipalToken()
if err != nil {
return nil, nil, err
}
err = servicePrincipalToken.EnsureFresh()
if err != nil {
return nil, nil, err
}
servicePrincipalTokenVault, err = auth.getServicePrincipalTokenWithResource(
strings.TrimRight(c.cloudEnvironment.KeyVaultEndpoint, "/"))
if err != nil {
return nil, nil, err
}
err = servicePrincipalTokenVault.EnsureFresh()
if err != nil {
return nil, nil, err
}
return servicePrincipalToken, servicePrincipalTokenVault, nil
}

View File

@ -0,0 +1,404 @@
package arm
import (
crand "crypto/rand"
"crypto/rsa"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
mrand "math/rand"
"os"
"testing"
"time"
"github.com/Azure/go-autorest/autorest/azure"
jwt "github.com/dgrijalva/jwt-go"
"github.com/hashicorp/packer/packer"
)
func Test_ClientConfig_RequiredParametersSet(t *testing.T) {
tests := []struct {
name string
config ClientConfig
wantErr bool
}{
{
name: "no client_id, client_secret or subscription_id should enable MSI auth",
config: ClientConfig{},
wantErr: false,
},
{
name: "subscription_id is set will trigger device flow",
config: ClientConfig{
SubscriptionID: "error",
},
wantErr: false,
},
{
name: "client_id without client_secret, client_cert_path or client_jwt should error",
config: ClientConfig{
ClientID: "error",
},
wantErr: true,
},
{
name: "client_secret without client_id should error",
config: ClientConfig{
ClientSecret: "error",
},
wantErr: true,
},
{
name: "client_cert_path without client_id should error",
config: ClientConfig{
ClientCertPath: "/dev/null",
},
wantErr: true,
},
{
name: "client_jwt without client_id should error",
config: ClientConfig{
ClientJWT: "error",
},
wantErr: true,
},
{
name: "missing subscription_id when using secret",
config: ClientConfig{
ClientID: "ok",
ClientSecret: "ok",
},
wantErr: true,
},
{
name: "missing subscription_id when using certificate",
config: ClientConfig{
ClientID: "ok",
ClientCertPath: "ok",
},
wantErr: true,
},
{
name: "missing subscription_id when using JWT",
config: ClientConfig{
ClientID: "ok",
ClientJWT: "ok",
},
wantErr: true,
},
{
name: "too many client_* values",
config: ClientConfig{
SubscriptionID: "ok",
ClientID: "ok",
ClientSecret: "ok",
ClientCertPath: "error",
},
wantErr: true,
},
{
name: "too many client_* values (2)",
config: ClientConfig{
SubscriptionID: "ok",
ClientID: "ok",
ClientSecret: "ok",
ClientJWT: "error",
},
wantErr: true,
},
{
name: "tenant_id alone should fail",
config: ClientConfig{
TenantID: "ok",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
errs := &packer.MultiError{}
tt.config.assertRequiredParametersSet(errs)
if (len(errs.Errors) != 0) != tt.wantErr {
t.Errorf("newConfig() error = %v, wantErr %v", errs, tt.wantErr)
return
}
})
}
}
func Test_ClientConfig_DeviceLogin(t *testing.T) {
getEnvOrSkip(t, "AZURE_DEVICE_LOGIN")
cfg := ClientConfig{
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
cloudEnvironment: getCloud(),
}
assertValid(t, cfg)
spt, sptkv, err := cfg.getServicePrincipalTokens(
func(s string) { fmt.Printf("SAY: %s\n", s) })
if err != nil {
t.Fatalf("Expected nil err, but got: %v", err)
}
token := spt.Token()
if token.AccessToken == "" {
t.Fatal("Expected management token to have non-nil access token")
}
if token.RefreshToken == "" {
t.Fatal("Expected management token to have non-nil refresh token")
}
kvtoken := sptkv.Token()
if kvtoken.AccessToken == "" {
t.Fatal("Expected keyvault token to have non-nil access token")
}
if kvtoken.RefreshToken == "" {
t.Fatal("Expected keyvault token to have non-nil refresh token")
}
}
func Test_ClientConfig_ClientPassword(t *testing.T) {
cfg := ClientConfig{
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
ClientSecret: getEnvOrSkip(t, "AZURE_CLIENTSECRET"),
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
cloudEnvironment: getCloud(),
}
assertValid(t, cfg)
spt, sptkv, err := cfg.getServicePrincipalTokens(func(s string) { fmt.Printf("SAY: %s\n", s) })
if err != nil {
t.Fatalf("Expected nil err, but got: %v", err)
}
token := spt.Token()
if token.AccessToken == "" {
t.Fatal("Expected management token to have non-nil access token")
}
if token.RefreshToken != "" {
t.Fatal("Expected management token to have no refresh token")
}
kvtoken := sptkv.Token()
if kvtoken.AccessToken == "" {
t.Fatal("Expected keyvault token to have non-nil access token")
}
if kvtoken.RefreshToken != "" {
t.Fatal("Expected keyvault token to have no refresh token")
}
}
func Test_ClientConfig_ClientCert(t *testing.T) {
cfg := ClientConfig{
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
ClientCertPath: getEnvOrSkip(t, "AZURE_CLIENTCERT"),
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
cloudEnvironment: getCloud(),
}
assertValid(t, cfg)
spt, sptkv, err := cfg.getServicePrincipalTokens(func(s string) { fmt.Printf("SAY: %s\n", s) })
if err != nil {
t.Fatalf("Expected nil err, but got: %v", err)
}
token := spt.Token()
if token.AccessToken == "" {
t.Fatal("Expected management token to have non-nil access token")
}
if token.RefreshToken != "" {
t.Fatal("Expected management token to have no refresh token")
}
kvtoken := sptkv.Token()
if kvtoken.AccessToken == "" {
t.Fatal("Expected keyvault token to have non-nil access token")
}
if kvtoken.RefreshToken != "" {
t.Fatal("Expected keyvault token to have no refresh token")
}
}
func Test_ClientConfig_ClientJWT(t *testing.T) {
cfg := ClientConfig{
SubscriptionID: getEnvOrSkip(t, "AZURE_SUBSCRIPTION"),
ClientID: getEnvOrSkip(t, "AZURE_CLIENTID"),
ClientJWT: getEnvOrSkip(t, "AZURE_CLIENTJWT"),
TenantID: getEnvOrSkip(t, "AZURE_TENANTID"),
cloudEnvironment: getCloud(),
}
assertValid(t, cfg)
spt, sptkv, err := cfg.getServicePrincipalTokens(func(s string) { fmt.Printf("SAY: %s\n", s) })
if err != nil {
t.Fatalf("Expected nil err, but got: %v", err)
}
token := spt.Token()
if token.AccessToken == "" {
t.Fatal("Expected management token to have non-nil access token")
}
if token.RefreshToken != "" {
t.Fatal("Expected management token to have no refresh token")
}
kvtoken := sptkv.Token()
if kvtoken.AccessToken == "" {
t.Fatal("Expected keyvault token to have non-nil access token")
}
if kvtoken.RefreshToken != "" {
t.Fatal("Expected keyvault token to have no refresh token")
}
}
func getEnvOrSkip(t *testing.T, envVar string) string {
v := os.Getenv(envVar)
if v == "" {
t.Skipf("%s is empty, skipping", envVar)
}
return v
}
func getCloud() *azure.Environment {
cloudName := os.Getenv("AZURE_CLOUD")
if cloudName == "" {
cloudName = "AZUREPUBLICCLOUD"
}
c, _ := azure.EnvironmentFromName(cloudName)
return &c
}
// tests for assertRequiredParametersSet
func Test_ClientConfig_CanUseDeviceCode(t *testing.T) {
// TenantID is optional, but Builder will look up tenant ID before requesting
t.Run("without TenantID", func(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
assertValid(t, cfg)
})
t.Run("with TenantID", func(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.TenantID = "12345"
assertValid(t, cfg)
})
}
func assertValid(t *testing.T, cfg ClientConfig) {
errs := &packer.MultiError{}
cfg.assertRequiredParametersSet(errs)
if len(errs.Errors) != 0 {
t.Fatal("Expected errs to be empty: ", errs)
}
}
func assertInvalid(t *testing.T, cfg ClientConfig) {
errs := &packer.MultiError{}
cfg.assertRequiredParametersSet(errs)
if len(errs.Errors) == 0 {
t.Fatal("Expected errs to be non-empty")
}
}
func Test_ClientConfig_CanUseClientSecret(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.ClientID = "12345"
cfg.ClientSecret = "12345"
assertValid(t, cfg)
}
func Test_ClientConfig_CanUseClientSecretWithTenantID(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.ClientID = "12345"
cfg.ClientSecret = "12345"
cfg.TenantID = "12345"
assertValid(t, cfg)
}
func Test_ClientConfig_CanUseClientJWT(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.ClientID = "12345"
cfg.ClientJWT = getJWT(10*time.Minute, true)
assertValid(t, cfg)
}
func Test_ClientConfig_CanUseClientJWTWithTenantID(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.ClientID = "12345"
cfg.ClientJWT = getJWT(10*time.Minute, true)
cfg.TenantID = "12345"
assertValid(t, cfg)
}
func Test_ClientConfig_CannotUseBothClientJWTAndSecret(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.ClientID = "12345"
cfg.ClientSecret = "12345"
cfg.ClientJWT = getJWT(10*time.Minute, true)
assertInvalid(t, cfg)
}
func Test_ClientConfig_ClientJWTShouldBeValidForAtLeast5Minutes(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.ClientID = "12345"
cfg.ClientJWT = getJWT(time.Minute, true)
assertInvalid(t, cfg)
}
func Test_ClientConfig_ClientJWTShouldHaveThumbprint(t *testing.T) {
cfg := emptyClientConfig()
cfg.SubscriptionID = "12345"
cfg.ClientID = "12345"
cfg.ClientJWT = getJWT(10*time.Minute, false)
assertInvalid(t, cfg)
}
func emptyClientConfig() ClientConfig {
cfg := ClientConfig{}
_ = cfg.setCloudEnvironment()
return cfg
}
func Test_getJWT(t *testing.T) {
if getJWT(time.Minute, true) == "" {
t.Fatalf("getJWT is broken")
}
}
func newRandReader() io.Reader {
var seed int64
binary.Read(crand.Reader, binary.LittleEndian, &seed)
return mrand.New(mrand.NewSource(seed))
}
func getJWT(validFor time.Duration, withX5tHeader bool) string {
token := jwt.New(jwt.SigningMethodRS256)
key, _ := rsa.GenerateKey(newRandReader(), 2048)
token.Claims = jwt.MapClaims{
"aud": "https://login.microsoftonline.com/tenant.onmicrosoft.com/oauth2/token?api-version=1.0",
"iss": "355dff10-cd78-11e8-89fe-000d3afd16e3",
"sub": "355dff10-cd78-11e8-89fe-000d3afd16e3",
"jti": base64.URLEncoding.EncodeToString([]byte{0}),
"nbf": time.Now().Unix(),
"exp": time.Now().Add(validFor).Unix(),
}
if withX5tHeader {
token.Header["x5t"] = base64.URLEncoding.EncodeToString([]byte("thumbprint"))
}
jwt, _ := token.SignedString(key)
return jwt
}

View File

@ -15,7 +15,6 @@ import (
"time"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
"github.com/masterzen/winrm"
@ -32,7 +31,6 @@ import (
)
const (
DefaultCloudEnvironmentName = "Public"
DefaultImageVersion = "latest"
DefaultUserName = "packer"
DefaultPrivateVirtualNetworkWithPublicIp = false
@ -79,11 +77,7 @@ type Config struct {
common.PackerConfig `mapstructure:",squash"`
// Authentication via OAUTH
ClientID string `mapstructure:"client_id"`
ClientSecret string `mapstructure:"client_secret"`
ObjectID string `mapstructure:"object_id"`
TenantID string `mapstructure:"tenant_id"`
SubscriptionID string `mapstructure:"subscription_id"`
ClientConfig `mapstructure:",squash"`
// Capture
CaptureNamePrefix string `mapstructure:"capture_name_prefix"`
@ -122,8 +116,6 @@ type Config struct {
TempResourceGroupName string `mapstructure:"temp_resource_group_name"`
BuildResourceGroupName string `mapstructure:"build_resource_group_name"`
storageAccountBlobEndpoint string
CloudEnvironmentName string `mapstructure:"cloud_environment_name"`
cloudEnvironment *azure.Environment
PrivateVirtualNetworkWithPublicIp bool `mapstructure:"private_virtual_network_with_public_ip"`
VirtualNetworkName string `mapstructure:"virtual_network_name"`
VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"`
@ -157,8 +149,6 @@ type Config struct {
tmpVirtualNetworkName string
tmpWinRMCertificateUrl string
useDeviceLogin bool
// Authentication with the VM via SSH
sshAuthorizedKey string
@ -287,7 +277,7 @@ func newConfig(raws ...interface{}) (*Config, []string, error) {
provideDefaultValues(&c)
setRuntimeValues(&c)
setUserNamePassword(&c)
err = setCloudEnvironment(&c)
err = c.ClientConfig.setCloudEnvironment()
if err != nil {
return nil, nil, err
}
@ -416,40 +406,6 @@ func setUserNamePassword(c *Config) {
}
}
func setCloudEnvironment(c *Config) error {
lookup := map[string]string{
"CHINA": "AzureChinaCloud",
"CHINACLOUD": "AzureChinaCloud",
"AZURECHINACLOUD": "AzureChinaCloud",
"GERMAN": "AzureGermanCloud",
"GERMANCLOUD": "AzureGermanCloud",
"AZUREGERMANCLOUD": "AzureGermanCloud",
"GERMANY": "AzureGermanCloud",
"GERMANYCLOUD": "AzureGermanCloud",
"AZUREGERMANYCLOUD": "AzureGermanCloud",
"PUBLIC": "AzurePublicCloud",
"PUBLICCLOUD": "AzurePublicCloud",
"AZUREPUBLICCLOUD": "AzurePublicCloud",
"USGOVERNMENT": "AzureUSGovernmentCloud",
"USGOVERNMENTCLOUD": "AzureUSGovernmentCloud",
"AZUREUSGOVERNMENTCLOUD": "AzureUSGovernmentCloud",
}
name := strings.ToUpper(c.CloudEnvironmentName)
envName, ok := lookup[name]
if !ok {
return fmt.Errorf("There is no cloud environment matching the name '%s'!", c.CloudEnvironmentName)
}
env, err := azure.EnvironmentFromName(envName)
c.cloudEnvironment = &env
return err
}
func setCustomData(c *Config) error {
if c.CustomDataFile == "" {
return nil
@ -481,9 +437,7 @@ func provideDefaultValues(c *Config) {
c.ImageVersion = DefaultImageVersion
}
if c.CloudEnvironmentName == "" {
c.CloudEnvironmentName = DefaultCloudEnvironmentName
}
c.provideDefaultValues()
}
func assertTagProperties(c *Config, errs *packer.MultiError) {
@ -502,37 +456,7 @@ func assertTagProperties(c *Config, errs *packer.MultiError) {
}
func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
/////////////////////////////////////////////
// Authentication via OAUTH
// Check if device login is being asked for, and is allowed.
//
// Device login is enabled if the user only defines SubscriptionID and not
// ClientID, ClientSecret, and TenantID.
//
// Device login is not enabled for Windows because the WinRM certificate is
// readable by the ObjectID of the App. There may be another way to handle
// this case, but I am not currently aware of it - send feedback.
isUseDeviceLogin := func(c *Config) bool {
return c.SubscriptionID != "" &&
c.ClientID == "" &&
c.ClientSecret == "" &&
c.TenantID == ""
}
if isUseDeviceLogin(c) {
c.useDeviceLogin = true
} else {
if c.ClientID == "" && c.ClientSecret != "" || c.ClientID != "" && c.ClientSecret == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A client_id and client_secret must be specified together or not specified at all"))
}
if c.ClientID != "" && c.SubscriptionID == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A subscription_id must be specified when client_id & client_secret are"))
}
}
c.ClientConfig.assertRequiredParametersSet(errs)
/////////////////////////////////////////////
// Capture

View File

@ -133,87 +133,6 @@ func TestConfigShouldNotDefaultImageVersionIfCustomImage(t *testing.T) {
}
}
func Test_newConfig_MSI(t *testing.T) {
baseConfig := map[string]string{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"location": "ignore",
"image_url": "ignore",
"storage_account": "ignore",
"resource_group_name": "ignore",
"os_type": constants.Target_Linux,
}
tests := []struct {
name string
args []interface{}
wantErr bool
}{
{
name: "no client_id and no client_secret should enable MSI auth",
args: []interface{}{
baseConfig,
getPackerConfiguration(),
},
wantErr: false,
},
{
name: "subscription_id is will be taken from MSI",
args: []interface{}{
baseConfig,
map[string]string{
"subscription_id": "error",
},
getPackerConfiguration(),
},
wantErr: false,
},
{
name: "client_id without client_secret should error",
args: []interface{}{
baseConfig,
map[string]string{
"client_id": "error",
},
getPackerConfiguration(),
},
wantErr: true,
},
{
name: "client_secret without client_id should error",
args: []interface{}{
baseConfig,
map[string]string{
"client_secret": "error",
},
getPackerConfiguration(),
},
wantErr: true,
},
{
name: "missing subscription_id",
args: []interface{}{
baseConfig,
map[string]string{
"client_id": "ok",
"client_secret": "ok",
},
getPackerConfiguration(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, _, err := newConfig(tt.args...)
if (err != nil) != tt.wantErr {
t.Errorf("newConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}
func TestConfigShouldNormalizeOSTypeCase(t *testing.T) {
config := map[string]string{
"capture_name_prefix": "ignore",

View File

@ -28,7 +28,7 @@ func NewStepPowerOffCompute(client *AzureClient, ui packer.Ui) *StepPowerOffComp
}
func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, resourceGroupName string, computeName string) error {
f, err := s.client.VirtualMachinesClient.PowerOff(ctx, resourceGroupName, computeName)
f, err := s.client.VirtualMachinesClient.Deallocate(ctx, resourceGroupName, computeName)
if err == nil {
err = f.WaitForCompletion(ctx, s.client.VirtualMachinesClient.Client)
}

View File

@ -3,8 +3,8 @@ package docker
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@ -16,18 +16,33 @@ type StepTempDir struct {
tempDir string
}
// ConfigTmpDir returns the configuration tmp directory for Docker
func ConfigTmpDir() (string, error) {
if tmpdir := os.Getenv("PACKER_TMP_DIR"); tmpdir != "" {
return filepath.Abs(tmpdir)
}
configdir, err := packer.ConfigDir()
if err != nil {
return "", err
}
td := filepath.Join(configdir, "tmp")
_, err = os.Stat(td)
if os.IsNotExist(err) {
if err = os.MkdirAll(td, 0755); err != nil {
return "", err
}
} else if err != nil {
return "", err
}
return td, nil
}
func (s *StepTempDir) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating a temporary directory for sharing data...")
var err error
var tempdir string
configTmpDir, err := packer.ConfigTmpDir()
if err == nil {
tempdir, err = ioutil.TempDir(configTmpDir, "packer-docker")
}
tempdir, err := ConfigTmpDir()
if err != nil {
err := fmt.Errorf("Error making temp dir: %s", err)
state.Put("error", err)

View File

@ -3,11 +3,9 @@ package docker
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
func TestStepTempDir_impl(t *testing.T) {
@ -52,46 +50,3 @@ func testStepTempDir_impl(t *testing.T) string {
func TestStepTempDir(t *testing.T) {
testStepTempDir_impl(t)
}
func TestStepTempDir_notmpdir(t *testing.T) {
tempenv := "PACKER_TMP_DIR"
oldenv := os.Getenv(tempenv)
defer os.Setenv(tempenv, oldenv)
os.Setenv(tempenv, "")
dir1 := testStepTempDir_impl(t)
cd, err := packer.ConfigDir()
if err != nil {
t.Fatalf("bad ConfigDir")
}
td := filepath.Join(cd, "tmp")
os.Setenv(tempenv, td)
dir2 := testStepTempDir_impl(t)
if filepath.Dir(dir1) != filepath.Dir(dir2) {
t.Fatalf("temp base directories do not match: %s %s", filepath.Dir(dir1), filepath.Dir(dir2))
}
}
func TestStepTempDir_packertmpdir(t *testing.T) {
tempenv := "PACKER_TMP_DIR"
oldenv := os.Getenv(tempenv)
defer os.Setenv(tempenv, oldenv)
os.Setenv(tempenv, ".")
dir1 := testStepTempDir_impl(t)
abspath, err := filepath.Abs(".")
if err != nil {
t.Fatalf("bad absolute path")
}
dir2 := filepath.Join(abspath, "tmp")
if filepath.Dir(dir1) != filepath.Dir(dir2) {
t.Fatalf("temp base directories do not match: %s %s", filepath.Dir(dir1), filepath.Dir(dir2))
}
}

View File

@ -34,7 +34,7 @@ type driverGCE struct {
var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}
func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) {
func NewClientGCE(a *AccountFile) (*http.Client, error) {
var err error
var client *http.Client
@ -78,6 +78,15 @@ func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) {
return nil, err
}
return client, nil
}
func NewDriverGCE(ui packer.Ui, p string, a *AccountFile) (Driver, error) {
client, err := NewClientGCE(a)
if err != nil {
return nil, err
}
log.Printf("[INFO] Instantiating GCE client...")
service, err := compute.New(client)
if err != nil {

View File

@ -15,7 +15,7 @@ const StartupScriptStatusNotDone string = "notdone"
var StartupScriptLinux string = fmt.Sprintf(`#!/usr/bin/env bash
echo "Packer startup script starting."
RETVAL=0
BASEMETADATAURL=http://metadata/computeMetadata/v1/instance/
BASEMETADATAURL=http://metadata.google.internal/computeMetadata/v1/instance/
GetMetadata () {
echo "$(curl -f -H "Metadata-Flavor: Google" ${BASEMETADATAURL}/${1} 2> /dev/null)"

View File

@ -73,7 +73,7 @@ type Driver interface {
DeleteVirtualSwitch(string) error
CreateVirtualMachine(string, string, string, int64, int64, int64, string, uint, bool, bool) error
CreateVirtualMachine(string, string, string, int64, int64, int64, string, uint, bool, bool, string) error
AddVirtualMachineHardDrive(string, string, string, int64, int64, string) error

View File

@ -136,6 +136,7 @@ type DriverMock struct {
CreateVirtualMachine_Generation uint
CreateVirtualMachine_DifferentialDisk bool
CreateVirtualMachine_FixedVHD bool
CreateVirtualMachine_Version string
CreateVirtualMachine_Err error
CloneVirtualMachine_Called bool
@ -422,7 +423,7 @@ func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, v
func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string,
ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint,
diffDisks bool, fixedVHD bool) error {
diffDisks bool, fixedVHD bool, version string) error {
d.CreateVirtualMachine_Called = true
d.CreateVirtualMachine_VmName = vmName
d.CreateVirtualMachine_Path = path
@ -433,6 +434,7 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP
d.CreateVirtualMachine_SwitchName = switchName
d.CreateVirtualMachine_Generation = generation
d.CreateVirtualMachine_DifferentialDisk = diffDisks
d.CreateVirtualMachine_Version = version
return d.CreateVirtualMachine_Err
}

View File

@ -188,9 +188,9 @@ func (d *HypervPS4Driver) AddVirtualMachineHardDrive(vmName string, vhdFile stri
func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64,
diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool,
fixedVHD bool) error {
fixedVHD bool, version string) error {
return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, ram, diskSize, diskBlockSize, switchName,
generation, diffDisks, fixedVHD)
generation, diffDisks, fixedVHD, version)
}
func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string,

View File

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
)
type StepCreateBuildDir struct {
@ -29,12 +30,13 @@ func (s *StepCreateBuildDir) Run(_ context.Context, state multistep.StateBag) mu
ui.Say("Creating build directory...")
var err error
if s.TempPath == "" {
s.TempPath = os.TempDir()
s.buildDir, err = tmp.Dir("hyperv")
} else {
s.buildDir, err = ioutil.TempDir(s.TempPath, "hyperv")
}
var err error
s.buildDir, err = ioutil.TempDir(s.TempPath, "packerhv")
if err != nil {
err = fmt.Errorf("Error creating build directory: %s", err)
state.Put("error", err)

View File

@ -38,7 +38,7 @@ func TestStepCreateBuildDir_Defaults(t *testing.T) {
// This prevents the regexp interpreting backslashes as escape sequences
stateBuildDir := filepath.ToSlash(v.(string))
expectedBuildDirRe := regexp.MustCompile(
filepath.ToSlash(filepath.Join(os.TempDir(), "packerhv") + `[[:digit:]]{9}$`))
filepath.ToSlash(filepath.Join(os.TempDir(), "hyperv") + `[[:digit:]]{9}$`))
match := expectedBuildDirRe.MatchString(stateBuildDir)
if !match {
t.Fatalf("Got path that doesn't match expected format in 'build_dir': %s", stateBuildDir)
@ -79,7 +79,7 @@ func TestStepCreateBuildDir_UserDefinedTempPath(t *testing.T) {
// This prevents the regexp interpreting backslashes as escape sequences
stateBuildDir := filepath.ToSlash(v.(string))
expectedBuildDirRe := regexp.MustCompile(
filepath.ToSlash(filepath.Join(step.TempPath, "packerhv") + `[[:digit:]]{9}$`))
filepath.ToSlash(filepath.Join(step.TempPath, "hyperv") + `[[:digit:]]{9}$`))
match := expectedBuildDirRe.MatchString(stateBuildDir)
if !match {
t.Fatalf("Got path that doesn't match expected format in 'build_dir': %s", stateBuildDir)

View File

@ -34,6 +34,7 @@ type StepCreateVM struct {
DifferencingDisk bool
MacAddress string
FixedVHD bool
Version string
}
func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
@ -60,12 +61,12 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste
}
// convert the MB to bytes
ramSize := int64(s.RamSize * 1024 * 1024)
diskSize := int64(s.DiskSize * 1024 * 1024)
diskBlockSize := int64(s.DiskBlockSize * 1024 * 1024)
ramSize := int64(s.RamSize) * 1024 * 1024
diskSize := int64(s.DiskSize) * 1024 * 1024
diskBlockSize := int64(s.DiskBlockSize) * 1024 * 1024
err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, ramSize, diskSize, diskBlockSize,
s.SwitchName, s.Generation, s.DifferencingDisk, s.FixedVHD)
s.SwitchName, s.Generation, s.DifferencingDisk, s.FixedVHD, s.Version)
if err != nil {
err := fmt.Errorf("Error creating virtual machine: %s", err)
state.Put("error", err)

View File

@ -4,13 +4,13 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
)
const (
@ -93,7 +93,7 @@ func (s *StepMountFloppydrive) Cleanup(state multistep.StateBag) {
}
func (s *StepMountFloppydrive) copyFloppy(path string) (string, error) {
tempdir, err := ioutil.TempDir("", "packer")
tempdir, err := tmp.Dir("hyperv")
if err != nil {
return "", err
}

View File

@ -1,146 +0,0 @@
package common
import (
"context"
"os"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
func TestStepOutputDir_imp(t *testing.T) {
var _ multistep.Step = new(StepOutputDir)
}
func TestStepOutputDir_Default(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
step.Path = genTestDirPath("packerHypervOutput")
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("Should NOT have error")
}
// The directory should have been created
if _, err := os.Stat(step.Path); err != nil {
t.Fatal("Should have created output directory")
}
// Remove the directory created due to test
err := os.RemoveAll(step.Path)
if err != nil {
t.Fatalf("Error encountered removing directory created by test: %s", err)
}
}
func TestStepOutputDir_DirectoryAlreadyExistsNoForce(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
step.Path = genTestDirPath("packerHypervOutput")
// Create the directory so that we can test
err := os.Mkdir(step.Path, 0755)
if err != nil {
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
}
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
step.Force = false // Default
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("Should halt when directory exists and 'Force' is false. Bad action: %v", action)
}
if _, ok := state.GetOk("error"); !ok {
t.Fatal("Should error when directory exists and 'Force' is false")
}
}
func TestStepOutputDir_DirectoryAlreadyExistsForce(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
step.Path = genTestDirPath("packerHypervOutput")
// Create the directory so that we can test
err := os.Mkdir(step.Path, 0755)
if err != nil {
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
}
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
step.Force = true // User specified that existing directory and contents should be discarded
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("Should complete when directory exists and 'Force' is true. Bad action: %v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatalf("Should NOT error when directory exists and 'Force' is true: %s", err)
}
}
func TestStepOutputDir_CleanupBuildCancelled(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
step.Path = genTestDirPath("packerHypervOutput")
// Create the directory so that we can test the cleanup
err := os.Mkdir(step.Path, 0755)
if err != nil {
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
}
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
// 'Cancel' the build
state.Put(multistep.StateCancelled, true)
// Ensure the directory isn't removed if the cleanup flag is false
step.cleanup = false
step.Cleanup(state)
if _, err := os.Stat(step.Path); err != nil {
t.Fatal("Output dir should NOT be removed if on 'Cancel' if cleanup flag is unset/false")
}
// Ensure the directory is removed if the cleanup flag is true
step.cleanup = true
step.Cleanup(state)
if _, err := os.Stat(step.Path); err == nil {
t.Fatalf("Output directory should NOT exist after 'Cancel' and Cleanup: %s", step.Path)
}
}
func TestStepOutputDir_CleanupBuildHalted(t *testing.T) {
state := testState(t)
step := new(StepOutputDir)
step.Path = genTestDirPath("packerHypervOutput")
// Create the directory so that we can test the cleanup
err := os.Mkdir(step.Path, 0755)
if err != nil {
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
}
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
// 'Halt' the build and test the directory is removed
state.Put(multistep.StateHalted, true)
// Ensure the directory isn't removed if the cleanup flag is false
step.cleanup = false
step.Cleanup(state)
if _, err := os.Stat(step.Path); err != nil {
t.Fatal("Output dir should NOT be removed if on 'Halt' if cleanup flag is unset/false")
}
// Ensure the directory is removed if the cleanup flag is true
step.cleanup = true
step.Cleanup(state)
if _, err := os.Stat(step.Path); err == nil {
t.Fatalf("Output directory should NOT exist after 'Halt' and Cleanup: %s", step.Path)
}
}

View File

@ -96,6 +96,7 @@ type Config struct {
SecureBootTemplate string `mapstructure:"secure_boot_template"`
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"`
TempPath string `mapstructure:"temp_path"`
Version string `mapstructure:"configuration_version"`
Communicator string `mapstructure:"communicator"`
@ -375,7 +376,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&hypervcommon.StepCreateBuildDir{
TempPath: b.config.TempPath,
},
&hypervcommon.StepOutputDir{
&common.StepOutputDir{
Force: b.config.PackerForce,
Path: b.config.OutputDir,
},
@ -418,6 +419,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
DifferencingDisk: b.config.DifferencingDisk,
MacAddress: b.config.MacAddress,
FixedVHD: b.config.FixedVHD,
Version: b.config.Version,
},
&hypervcommon.StepEnableIntegrationService{},

View File

@ -5,12 +5,11 @@ package iso
import (
"context"
"fmt"
"os"
"reflect"
"strconv"
"testing"
"os"
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"

View File

@ -93,6 +93,7 @@ type Config struct {
SecureBootTemplate string `mapstructure:"secure_boot_template"`
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"`
TempPath string `mapstructure:"temp_path"`
Version string `mapstructure:"configuration_version"`
Communicator string `mapstructure:"communicator"`
@ -391,7 +392,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&hypervcommon.StepCreateBuildDir{
TempPath: b.config.TempPath,
},
&hypervcommon.StepOutputDir{
&common.StepOutputDir{
Force: b.config.PackerForce,
Path: b.config.OutputDir,
},

View File

@ -12,6 +12,7 @@ import (
"syscall"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
)
type LxcAttachCommunicator struct {
@ -60,7 +61,7 @@ func (c *LxcAttachCommunicator) Start(cmd *packer.RemoteCmd) error {
func (c *LxcAttachCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error {
log.Printf("Uploading to rootfs: %s", dst)
tf, err := ioutil.TempFile("", "packer-lxc-attach")
tf, err := tmp.File("packer-lxc-attach")
if err != nil {
return fmt.Errorf("Error uploading file to rootfs: %s", err)
}

View File

@ -5,7 +5,7 @@ import (
"log"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
)
// Artifact is an artifact implementation that contains built images.

View File

@ -71,6 +71,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, fmt.Errorf("Error initializing compute client: %s", err)
}
imageClient, err := b.config.imageV2Client()
if err != nil {
return nil, fmt.Errorf("Error initializing image client: %s", err)
}
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
@ -142,6 +147,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&stepCreateImage{
UseBlockStorageVolume: b.config.UseBlockStorageVolume,
},
&stepUpdateImageTags{},
&stepUpdateImageVisibility{},
&stepAddImageMembers{},
}
@ -164,7 +170,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
artifact := &Artifact{
ImageId: state.Get("image").(string),
BuilderIdValue: BuilderId,
Client: computeClient,
Client: imageClient,
}
return artifact, nil

View File

@ -15,6 +15,7 @@ type ImageConfig struct {
ImageVisibility imageservice.ImageVisibility `mapstructure:"image_visibility"`
ImageMembers []string `mapstructure:"image_members"`
ImageDiskFormat string `mapstructure:"image_disk_format"`
ImageTags []string `mapstructure:"image_tags"`
}
func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {

View File

@ -9,8 +9,8 @@ import (
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -25,13 +25,21 @@ func (s *stepCreateImage) Run(_ context.Context, state multistep.StateBag) multi
ui := state.Get("ui").(packer.Ui)
// We need the v2 compute client
client, err := config.computeV2Client()
computeClient, err := config.computeV2Client()
if err != nil {
err = fmt.Errorf("Error initializing compute client: %s", err)
state.Put("error", err)
return multistep.ActionHalt
}
// We need the v2 image client
imageClient, err := config.imageV2Client()
if err != nil {
err = fmt.Errorf("Error initializing image service client: %s", err)
state.Put("error", err)
return multistep.ActionHalt
}
// Create the image.
// Image source depends on the type of the Compute instance. It can be
// Block Storage service volume or regular Compute service local volume.
@ -58,7 +66,7 @@ func (s *stepCreateImage) Run(_ context.Context, state multistep.StateBag) multi
}
imageId = image.ImageID
} else {
imageId, err = servers.CreateImage(client, server.ID, servers.CreateImageOpts{
imageId, err = servers.CreateImage(computeClient, server.ID, servers.CreateImageOpts{
Name: config.ImageName,
Metadata: config.ImageMetadata,
}).ExtractImageID()
@ -76,7 +84,7 @@ func (s *stepCreateImage) Run(_ context.Context, state multistep.StateBag) multi
// Wait for the image to become ready
ui.Say(fmt.Sprintf("Waiting for image %s (image id: %s) to become ready...", config.ImageName, imageId))
if err := WaitForImage(client, imageId); err != nil {
if err := WaitForImage(imageClient, imageId); err != nil {
err := fmt.Errorf("Error waiting for image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
@ -113,11 +121,11 @@ func WaitForImage(client *gophercloud.ServiceClient, imageId string) error {
return err
}
if image.Status == "ACTIVE" {
if image.Status == "active" {
return nil
}
log.Printf("Waiting for image creation status: %s (%d%%)", image.Status, image.Progress)
log.Printf("Waiting for image creation status: %s", image.Status)
time.Sleep(2 * time.Second)
}
}

View File

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
"golang.org/x/crypto/ssh"
)
@ -137,13 +138,13 @@ func berToDer(ber []byte, ui packer.Ui) []byte {
return ber
}
berKey, err := ioutil.TempFile("", "packer-ber-privatekey-")
berKey, err := tmp.File("packer-ber-privatekey-")
defer os.Remove(berKey.Name())
if err != nil {
return ber
}
ioutil.WriteFile(berKey.Name(), ber, os.ModeAppend)
derKey, err := ioutil.TempFile("", "packer-der-privatekey-")
derKey, err := tmp.File("packer-der-privatekey-")
defer os.Remove(derKey.Name())
if err != nil {
return ber

View File

@ -0,0 +1,52 @@
package openstack
import (
"context"
"fmt"
"strings"
imageservice "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type stepUpdateImageTags struct{}
func (s *stepUpdateImageTags) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
imageId := state.Get("image").(string)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(*Config)
if len(config.ImageTags) == 0 {
return multistep.ActionContinue
}
imageClient, err := config.imageV2Client()
if err != nil {
err = fmt.Errorf("Error initializing image service client: %s", err)
state.Put("error", err)
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Updating image tags to %s", strings.Join(config.ImageTags, ", ")))
r := imageservice.Update(
imageClient,
imageId,
imageservice.UpdateOpts{
imageservice.ReplaceImageTags{
NewTags: config.ImageTags,
},
},
)
if _, err = r.Extract(); err != nil {
err = fmt.Errorf("Error updating image tags: %s", err)
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepUpdateImageTags) Cleanup(multistep.StateBag) {
// No cleanup...
}

View File

@ -15,6 +15,7 @@ import (
"github.com/ChrisTrenkamp/goxpath"
"github.com/ChrisTrenkamp/goxpath/tree/xmltree"
"github.com/hashicorp/packer/packer/tmp"
)
// Parallels9Driver is a base type for Parallels builders.
@ -153,6 +154,7 @@ func (d *Parallels9Driver) DeviceAddCDROM(name string, image string) (string, er
"set", name,
"--device-add", "cdrom",
"--image", image,
"--enable", "--connect",
}
out, err := exec.Command(d.PrlctlPath, command...).Output()
@ -288,7 +290,7 @@ func (d *Parallels9Driver) SendKeyScanCodes(vmName string, codes ...string) erro
return nil
}
f, err := ioutil.TempFile("", "prltype")
f, err := tmp.File("prltype")
if err != nil {
return err
}

View File

@ -97,6 +97,7 @@ type Config struct {
ISOSkipCache bool `mapstructure:"iso_skip_cache"`
Accelerator string `mapstructure:"accelerator"`
CpuCount int `mapstructure:"cpus"`
DiskInterface string `mapstructure:"disk_interface"`
DiskSize uint `mapstructure:"disk_size"`
DiskCache string `mapstructure:"disk_cache"`
@ -109,6 +110,7 @@ type Config struct {
DiskImage bool `mapstructure:"disk_image"`
UseBackingFile bool `mapstructure:"use_backing_file"`
MachineType string `mapstructure:"machine_type"`
MemorySize int `mapstructure:"memory"`
NetDevice string `mapstructure:"net_device"`
OutputDir string `mapstructure:"output_directory"`
QemuArgs [][]string `mapstructure:"qemuargs"`
@ -201,6 +203,16 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.QemuBinary = "qemu-system-x86_64"
}
if b.config.MemorySize < 10 {
log.Printf("MemorySize %d is too small, using default: 512", b.config.MemorySize)
b.config.MemorySize = 512
}
if b.config.CpuCount < 1 {
log.Printf("CpuCount %d too small, using default: 1", b.config.CpuCount)
b.config.CpuCount = 1
}
if b.config.SSHHostPortMin == 0 {
b.config.SSHHostPortMin = 2222
}

View File

@ -150,7 +150,10 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
defaultArgs["-cdrom"] = isoPath
}
defaultArgs["-boot"] = bootDrive
defaultArgs["-m"] = "512M"
defaultArgs["-m"] = fmt.Sprintf("%dM", config.MemorySize)
if config.CpuCount > 1 {
defaultArgs["-smp"] = fmt.Sprintf("cpus=%d,sockets=%d", config.CpuCount, config.CpuCount)
}
defaultArgs["-vnc"] = vnc
// Append the accelerator to the machine type if it is specified

View File

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
@ -28,11 +29,7 @@ func (s *stepSetISO) Run(_ context.Context, state multistep.StateBag) multistep.
req.Header.Set("User-Agent", "Packer")
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}
httpClient := commonhelper.HttpClientWithEnvironmentProxy()
res, err := httpClient.Do(req)
if err == nil && (res.StatusCode >= 200 && res.StatusCode < 300) {

View File

@ -3,6 +3,7 @@ package common
import (
"bytes"
"fmt"
versionUtil "github.com/hashicorp/go-version"
"log"
"os/exec"
"regexp"
@ -24,9 +25,19 @@ func (d *VBox42Driver) CreateSATAController(vmName string, name string, portcoun
return err
}
portCountArg := "--sataportcount"
if strings.HasPrefix(version, "4.3") || strings.HasPrefix(version, "5.") || strings.HasPrefix(version, "6.") {
portCountArg = "--portcount"
portCountArg := "--portcount"
currentVersion, err := versionUtil.NewVersion(version)
if err != nil {
return err
}
firstVersionUsingPortCount, err := versionUtil.NewVersion("4.3")
if err != nil {
return err
}
if currentVersion.LessThan(firstVersionUsingPortCount) {
portCountArg = "--sataportcount"
}
command := []string{

View File

@ -4,13 +4,13 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
)
// This step attaches the ISO to the virtual machine.
@ -106,7 +106,7 @@ func (s *StepAttachFloppy) Cleanup(state multistep.StateBag) {
}
func (s *StepAttachFloppy) copyFloppy(path string) (string, error) {
tempdir, err := ioutil.TempDir("", "packer")
tempdir, err := tmp.Dir("virtualbox")
if err != nil {
return "", err
}

View File

@ -21,8 +21,9 @@ import (
//
// Produces:
type StepAttachGuestAdditions struct {
attachedPath string
GuestAdditionsMode string
attachedPath string
GuestAdditionsMode string
GuestAdditionsInterface string
}
func (s *StepAttachGuestAdditions) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
@ -40,12 +41,22 @@ func (s *StepAttachGuestAdditions) Run(_ context.Context, state multistep.StateB
guestAdditionsPath := state.Get("guest_additions_path").(string)
// Attach the guest additions to the computer
controllerName := "IDE Controller"
port := "1"
device := "0"
if s.GuestAdditionsInterface == "sata" {
controllerName = "SATA Controller"
port = "2"
device = "0"
}
log.Println("Attaching guest additions ISO onto IDE controller...")
command := []string{
"storageattach", vmName,
"--storagectl", "IDE Controller",
"--port", "1",
"--device", "0",
"--storagectl", controllerName,
"--port", port,
"--device", device,
"--type", "dvddrive",
"--medium", guestAdditionsPath,
}
@ -71,11 +82,20 @@ func (s *StepAttachGuestAdditions) Cleanup(state multistep.StateBag) {
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)
controllerName := "IDE Controller"
port := "1"
device := "0"
if s.GuestAdditionsInterface == "sata" {
controllerName = "SATA Controller"
port = "2"
device = "0"
}
command := []string{
"storageattach", vmName,
"--storagectl", "IDE Controller",
"--port", "1",
"--device", "0",
"--storagectl", controllerName,
"--port", port,
"--device", device,
"--medium", "none",
}

View File

@ -5,7 +5,6 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
@ -13,6 +12,7 @@ import (
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
"github.com/hashicorp/packer/template/interpolate"
)
@ -153,7 +153,7 @@ func (s *StepDownloadGuestAdditions) downloadAdditionsSHA256(ctx context.Context
"https://download.virtualbox.org/virtualbox/%s/SHA256SUMS",
additionsVersion)
checksumsFile, err := ioutil.TempFile("", "packer")
checksumsFile, err := tmp.File("packer")
if err != nil {
state.Put("error", fmt.Errorf(
"Failed creating temporary file to store guest addition checksums: %s",

View File

@ -1,86 +0,0 @@
package common
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"time"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// StepOutputDir sets up the output directory by creating it if it does
// not exist, deleting it if it does exist and we're forcing, and cleaning
// it up when we're done with it.
type StepOutputDir struct {
Force bool
Path string
cleanup bool
}
func (s *StepOutputDir) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if _, err := os.Stat(s.Path); err == nil {
if !s.Force {
err := fmt.Errorf(
"Output directory exists: %s\n\n"+
"Use the force flag to delete it prior to building.",
s.Path)
state.Put("error", err)
return multistep.ActionHalt
}
ui.Say("Deleting previous output directory...")
os.RemoveAll(s.Path)
}
// Enable cleanup
s.cleanup = true
// Create the directory
if err := os.MkdirAll(s.Path, 0755); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Make sure we can write in the directory
f, err := os.Create(filepath.Join(s.Path, "_packer_perm_check"))
if err != nil {
err = fmt.Errorf("Couldn't write to output directory: %s", err)
state.Put("error", err)
return multistep.ActionHalt
}
f.Close()
os.Remove(f.Name())
return multistep.ActionContinue
}
func (s *StepOutputDir) Cleanup(state multistep.StateBag) {
if !s.cleanup {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting output directory...")
for i := 0; i < 5; i++ {
err := os.RemoveAll(s.Path)
if err == nil {
break
}
log.Printf("Error removing output dir: %s", err)
time.Sleep(2 * time.Second)
}
}
}

View File

@ -20,7 +20,8 @@ import (
//
// Produces:
type StepRemoveDevices struct {
Bundling VBoxBundleConfig
Bundling VBoxBundleConfig
GuestAdditionsInterface string
}
func (s *StepRemoveDevices) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
@ -100,11 +101,19 @@ func (s *StepRemoveDevices) Run(_ context.Context, state multistep.StateBag) mul
if _, ok := state.GetOk("guest_additions_attached"); ok {
ui.Message("Removing guest additions drive...")
controllerName := "IDE Controller"
port := "1"
device := "0"
if s.GuestAdditionsInterface == "sata" {
controllerName = "SATA Controller"
port = "2"
device = "0"
}
command := []string{
"storageattach", vmName,
"--storagectl", "IDE Controller",
"--port", "1",
"--device", "0",
"--storagectl", controllerName,
"--port", port,
"--device", device,
"--medium", "none",
}
if err := driver.VBoxManage(command...); err != nil {

View File

@ -41,20 +41,21 @@ type Config struct {
vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
vboxcommon.VBoxBundleConfig `mapstructure:",squash"`
DiskSize uint `mapstructure:"disk_size"`
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
GuestOSType string `mapstructure:"guest_os_type"`
HardDriveDiscard bool `mapstructure:"hard_drive_discard"`
HardDriveInterface string `mapstructure:"hard_drive_interface"`
SATAPortCount int `mapstructure:"sata_port_count"`
HardDriveNonrotational bool `mapstructure:"hard_drive_nonrotational"`
ISOInterface string `mapstructure:"iso_interface"`
KeepRegistered bool `mapstructure:"keep_registered"`
SkipExport bool `mapstructure:"skip_export"`
VMName string `mapstructure:"vm_name"`
DiskSize uint `mapstructure:"disk_size"`
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
GuestAdditionsInterface string `mapstructure:"guest_additions_interface"`
GuestOSType string `mapstructure:"guest_os_type"`
HardDriveDiscard bool `mapstructure:"hard_drive_discard"`
HardDriveInterface string `mapstructure:"hard_drive_interface"`
SATAPortCount int `mapstructure:"sata_port_count"`
HardDriveNonrotational bool `mapstructure:"hard_drive_nonrotational"`
ISOInterface string `mapstructure:"iso_interface"`
KeepRegistered bool `mapstructure:"keep_registered"`
SkipExport bool `mapstructure:"skip_export"`
VMName string `mapstructure:"vm_name"`
ctx interpolate.Context
}
@ -125,6 +126,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.ISOInterface = "ide"
}
if b.config.GuestAdditionsInterface == "" {
b.config.GuestAdditionsInterface = b.config.ISOInterface
}
if b.config.VMName == "" {
b.config.VMName = fmt.Sprintf(
"packer-%s-%d", b.config.PackerBuildName, interpolate.InitTime.Unix())
@ -209,7 +214,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
TargetPath: b.config.TargetPath,
Url: b.config.ISOUrls,
},
&vboxcommon.StepOutputDir{
&common.StepOutputDir{
Force: b.config.PackerForce,
Path: b.config.OutputDir,
},
@ -227,7 +232,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
new(stepCreateDisk),
new(stepAttachISO),
&vboxcommon.StepAttachGuestAdditions{
GuestAdditionsMode: b.config.GuestAdditionsMode,
GuestAdditionsMode: b.config.GuestAdditionsMode,
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
},
&vboxcommon.StepConfigureVRDP{
VRDPBindAddress: b.config.VRDPBindAddress,
@ -280,7 +286,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Delay: b.config.PostShutdownDelay,
},
&vboxcommon.StepRemoveDevices{
Bundling: b.config.VBoxBundleConfig,
Bundling: b.config.VBoxBundleConfig,
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
},
&vboxcommon.StepVBoxManage{
Commands: b.config.VBoxManagePost,

View File

@ -50,7 +50,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Build the steps.
steps := []multistep.Step{
&vboxcommon.StepOutputDir{
&common.StepOutputDir{
Force: b.config.PackerForce,
Path: b.config.OutputDir,
},
@ -84,7 +84,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ImportFlags: b.config.ImportFlags,
},
&vboxcommon.StepAttachGuestAdditions{
GuestAdditionsMode: b.config.GuestAdditionsMode,
GuestAdditionsMode: b.config.GuestAdditionsMode,
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
},
&vboxcommon.StepConfigureVRDP{
VRDPBindAddress: b.config.VRDPBindAddress,
@ -136,7 +137,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Timeout: b.config.ShutdownTimeout,
Delay: b.config.PostShutdownDelay,
},
new(vboxcommon.StepRemoveDevices),
&vboxcommon.StepRemoveDevices{
GuestAdditionsInterface: b.config.GuestAdditionsInterface,
},
&vboxcommon.StepVBoxManage{
Commands: b.config.VBoxManagePost,
Ctx: b.config.ctx,

View File

@ -28,19 +28,20 @@ type Config struct {
vboxcommon.VBoxManagePostConfig `mapstructure:",squash"`
vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
Checksum string `mapstructure:"checksum"`
ChecksumType string `mapstructure:"checksum_type"`
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
ImportFlags []string `mapstructure:"import_flags"`
ImportOpts string `mapstructure:"import_opts"`
SourcePath string `mapstructure:"source_path"`
TargetPath string `mapstructure:"target_path"`
VMName string `mapstructure:"vm_name"`
KeepRegistered bool `mapstructure:"keep_registered"`
SkipExport bool `mapstructure:"skip_export"`
Checksum string `mapstructure:"checksum"`
ChecksumType string `mapstructure:"checksum_type"`
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
GuestAdditionsInterface string `mapstructure:"guest_additions_interface"`
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
ImportFlags []string `mapstructure:"import_flags"`
ImportOpts string `mapstructure:"import_opts"`
SourcePath string `mapstructure:"source_path"`
TargetPath string `mapstructure:"target_path"`
VMName string `mapstructure:"vm_name"`
KeepRegistered bool `mapstructure:"keep_registered"`
SkipExport bool `mapstructure:"skip_export"`
ctx interpolate.Context
}
@ -72,6 +73,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
if c.GuestAdditionsPath == "" {
c.GuestAdditionsPath = "VBoxGuestAdditions.iso"
}
if c.GuestAdditionsInterface == "" {
c.GuestAdditionsInterface = "ide"
}
if c.VMName == "" {
c.VMName = fmt.Sprintf(

View File

@ -69,9 +69,9 @@ func (c *DriverConfig) Validate(SkipExport bool) error {
// now, so that we don't fail for a simple mistake after a long
// build
ovftool := GetOVFTool()
ovfToolArgs := []string{"--verifyOnly", fmt.Sprintf("vi://" +
url.QueryEscape(c.RemoteUser) + ":" +
url.QueryEscape(c.RemotePassword) + "@" +
ovfToolArgs := []string{"--verifyOnly", fmt.Sprintf("vi://%s:%s@%s",
url.QueryEscape(c.RemoteUser),
url.QueryEscape(c.RemotePassword),
c.RemoteHost)}
var out bytes.Buffer

View File

@ -14,6 +14,7 @@ type HWConfig struct {
// cpu information
CpuCount int `mapstructure:"cpus"`
MemorySize int `mapstructure:"memory"`
CoreCount int `mapstructure:"cores"`
// network type and adapter
Network string `mapstructure:"network"`
@ -40,6 +41,11 @@ func (c *HWConfig) Prepare(ctx *interpolate.Context) []error {
errs = append(errs, fmt.Errorf("An invalid amount of memory was specified (memory < 0): %d", c.MemorySize))
}
// Hardware and cpu options
if c.CoreCount < 0 {
errs = append(errs, fmt.Errorf("An invalid number of cores was specified (cores < 0): %d", c.CoreCount))
}
// Peripherals
if !c.Sound {
c.Sound = false

View File

@ -8,6 +8,7 @@ import (
func testHWConfig() *HWConfig {
return &HWConfig{
CpuCount: 1,
CoreCount: 1,
MemorySize: 512,
Sound: true,
@ -26,6 +27,10 @@ func TestHWConfigPrepare(t *testing.T) {
t.Errorf("bad cpu count: %d", c.CpuCount)
}
if c.CoreCount < 0 {
t.Errorf("bad core count: %d", c.CoreCount)
}
if c.MemorySize < 0 {
t.Errorf("bad memory size: %d", c.MemorySize)
}

View File

@ -12,6 +12,7 @@ import (
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
"github.com/hashicorp/packer/template/interpolate"
)
@ -410,7 +411,7 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist
if config.RemoteType != "" {
// For remote builds, we just put the VMX in a temporary
// directory since it just gets uploaded anyways.
vmxDir, err = ioutil.TempDir("", "packer-vmx")
vmxDir, err = tmp.Dir("vmw-iso")
if err != nil {
err := fmt.Errorf("Error preparing VMX template: %s", err)
state.Put("error", err)
@ -422,12 +423,19 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist
s.tempDir = vmxDir
}
/// Now to handle options that will modify the template
/// Now to handle options that will modify the template without using "vmxTemplateData"
vmxData := vmwcommon.ParseVMX(vmxContents)
// If no cpus were specified, then remove the entry to use the default
if vmxData["numvcpus"] == "" {
delete(vmxData, "numvcpus")
}
// If some number of cores were specified, then update "cpuid.coresPerSocket" with the requested value
if config.HWConfig.CoreCount > 0 {
vmxData["cpuid.corespersocket"] = strconv.Itoa(config.HWConfig.CoreCount)
}
/// Write the vmxData to the vmxPath
vmxPath := filepath.Join(vmxDir, config.VMName+".vmx")
if err := vmwcommon.WriteVMX(vmxPath, vmxData); err != nil {

View File

@ -47,6 +47,7 @@ func tmpnam(prefix string) string {
dir := os.TempDir()
max := int(math.Pow(2, float64(length)))
// FIXME use ioutil.TempFile() or at least mimic implementation, this could loop forever
n, err := rand.Intn(max), nil
for path = filepath.Join(dir, prefix+strconv.Itoa(n)); err == nil; _, err = os.Stat(path) {
n = rand.Intn(max)

View File

@ -3,7 +3,6 @@ package vmx
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
@ -12,6 +11,7 @@ import (
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
)
// StepCloneVMX takes a VMX file and clones the VM into the output directory.
@ -50,7 +50,7 @@ func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multiste
// * The disk compaction step needs the paths to all attached disks
if remoteDriver, ok := driver.(vmwcommon.RemoteDriver); ok {
remoteVmxPath := vmxPath
tempDir, err := ioutil.TempDir("", "packer-vmx")
tempDir, err := tmp.Dir("packer-vmx")
if err != nil {
return halt(err)
}

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/packer/builder/file"
"github.com/hashicorp/packer/packer"
shell_local "github.com/hashicorp/packer/post-processor/shell-local"
)
func TestBuildOnlyFileCommaFlags(t *testing.T) {
@ -26,15 +27,20 @@ func TestBuildOnlyFileCommaFlags(t *testing.T) {
fatalCommand(t, c.Meta)
}
if !fileExists("chocolate.txt") {
t.Error("Expected to find chocolate.txt")
}
if !fileExists("vanilla.txt") {
t.Error("Expected to find vanilla.txt")
for _, f := range []string{"chocolate.txt", "vanilla.txt",
"apple.txt", "peach.txt", "pear.txt"} {
if !fileExists(f) {
t.Errorf("Expected to find %s", f)
}
}
if fileExists("cherry.txt") {
t.Error("Expected NOT to find cherry.txt")
}
if !fileExists("tomato.txt") {
t.Error("Expected to find tomato.txt")
}
}
func TestBuildStdin(t *testing.T) {
@ -56,14 +62,10 @@ func TestBuildStdin(t *testing.T) {
fatalCommand(t, c.Meta)
}
if !fileExists("chocolate.txt") {
t.Error("Expected to find chocolate.txt")
}
if !fileExists("vanilla.txt") {
t.Error("Expected to find vanilla.txt")
}
if !fileExists("cherry.txt") {
t.Error("Expected to find cherry.txt")
for _, f := range []string{"vanilla.txt", "cherry.txt", "chocolate.txt"} {
if !fileExists(f) {
t.Errorf("Expected to find %s", f)
}
}
}
@ -75,6 +77,9 @@ func TestBuildOnlyFileMultipleFlags(t *testing.T) {
args := []string{
"-only=chocolate",
"-only=cherry",
"-only=apple", // ignored
"-only=peach", // ignored
"-only=pear", // ignored
filepath.Join(testFixture("build-only"), "template.json"),
}
@ -84,14 +89,16 @@ func TestBuildOnlyFileMultipleFlags(t *testing.T) {
fatalCommand(t, c.Meta)
}
if !fileExists("chocolate.txt") {
t.Error("Expected to find chocolate.txt")
for _, f := range []string{"vanilla.txt"} {
if fileExists(f) {
t.Errorf("Expected NOT to find %s", f)
}
}
if fileExists("vanilla.txt") {
t.Error("Expected NOT to find vanilla.txt")
}
if !fileExists("cherry.txt") {
t.Error("Expected to find cherry.txt")
for _, f := range []string{"chocolate.txt", "cherry.txt",
"apple.txt", "peach.txt", "pear.txt"} {
if !fileExists(f) {
t.Errorf("Expected to find %s", f)
}
}
}
@ -101,7 +108,7 @@ func TestBuildExceptFileCommaFlags(t *testing.T) {
}
args := []string{
"-except=chocolate",
"-except=chocolate,vanilla",
filepath.Join(testFixture("build-only"), "template.json"),
}
@ -111,14 +118,15 @@ func TestBuildExceptFileCommaFlags(t *testing.T) {
fatalCommand(t, c.Meta)
}
if fileExists("chocolate.txt") {
t.Error("Expected NOT to find chocolate.txt")
for _, f := range []string{"chocolate.txt", "vanilla.txt", "tomato.txt"} {
if fileExists(f) {
t.Errorf("Expected NOT to find %s", f)
}
}
if !fileExists("vanilla.txt") {
t.Error("Expected to find vanilla.txt")
}
if !fileExists("cherry.txt") {
t.Error("Expected to find cherry.txt")
for _, f := range []string{"apple.txt", "cherry.txt", "pear.txt", "peach.txt"} {
if !fileExists(f) {
t.Errorf("Expected to find %s", f)
}
}
}
@ -137,6 +145,9 @@ func testCoreConfigBuilder(t *testing.T) *packer.CoreConfig {
Builder: func(n string) (packer.Builder, error) {
return &file.Builder{}, nil
},
PostProcessor: func(n string) (packer.PostProcessor, error) {
return &shell_local.PostProcessor{}, nil
},
}
return &packer.CoreConfig{
Components: components,
@ -159,4 +170,8 @@ func cleanup() {
os.RemoveAll("chocolate.txt")
os.RemoveAll("vanilla.txt")
os.RemoveAll("cherry.txt")
os.RemoveAll("apple.txt")
os.RemoveAll("peach.txt")
os.RemoveAll("pear.txt")
os.RemoveAll("tomato.txt")
}

View File

@ -6,8 +6,8 @@ import (
"fmt"
"io"
"github.com/hashicorp/packer/helper/flag-kv"
"github.com/hashicorp/packer/helper/flag-slice"
kvflag "github.com/hashicorp/packer/helper/flag-kv"
sliceflag "github.com/hashicorp/packer/helper/flag-slice"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template"
)
@ -31,9 +31,7 @@ type Meta struct {
Version string
// These are set by command-line flags
flagBuildExcept []string
flagBuildOnly []string
flagVars map[string]string
flagVars map[string]string
}
// Core returns the core for the given template given the configured
@ -59,7 +57,7 @@ func (m *Meta) BuildNames(c *packer.Core) []string {
// TODO: test
// Filter the "only"
if len(m.flagBuildOnly) > 0 {
if len(m.CoreConfig.Only) > 0 {
// Build a set of all the available names
nameSet := make(map[string]struct{})
for _, n := range c.BuildNames() {
@ -67,8 +65,8 @@ func (m *Meta) BuildNames(c *packer.Core) []string {
}
// Build our result set which we pre-allocate some sane number
result := make([]string, 0, len(m.flagBuildOnly))
for _, n := range m.flagBuildOnly {
result := make([]string, 0, len(m.CoreConfig.Only))
for _, n := range m.CoreConfig.Only {
if _, ok := nameSet[n]; ok {
result = append(result, n)
}
@ -78,10 +76,10 @@ func (m *Meta) BuildNames(c *packer.Core) []string {
}
// Filter the "except"
if len(m.flagBuildExcept) > 0 {
if len(m.CoreConfig.Except) > 0 {
// Build a set of the things we don't want
nameSet := make(map[string]struct{})
for _, n := range m.flagBuildExcept {
for _, n := range m.CoreConfig.Except {
nameSet[n] = struct{}{}
}
@ -111,8 +109,8 @@ func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
// FlagSetBuildFilter tells us to enable the settings for selecting
// builds we care about.
if fs&FlagSetBuildFilter != 0 {
f.Var((*sliceflag.StringFlag)(&m.flagBuildExcept), "except", "")
f.Var((*sliceflag.StringFlag)(&m.flagBuildOnly), "only", "")
f.Var((*sliceflag.StringFlag)(&m.CoreConfig.Except), "except", "")
f.Var((*sliceflag.StringFlag)(&m.CoreConfig.Only), "only", "")
}
// FlagSetVars tells us what variables to use

View File

@ -41,7 +41,7 @@ import (
profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks"
qemubuilder "github.com/hashicorp/packer/builder/qemu"
scalewaybuilder "github.com/hashicorp/packer/builder/scaleway"
tencentcloudbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm"
tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm"
tritonbuilder "github.com/hashicorp/packer/builder/triton"
virtualboxisobuilder "github.com/hashicorp/packer/builder/virtualbox/iso"
virtualboxovfbuilder "github.com/hashicorp/packer/builder/virtualbox/ovf"
@ -115,7 +115,7 @@ var Builders = map[string]packer.Builder{
"profitbricks": new(profitbricksbuilder.Builder),
"qemu": new(qemubuilder.Builder),
"scaleway": new(scalewaybuilder.Builder),
"tencentcloud-cvm": new(tencentcloudbuilder.Builder),
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
"triton": new(tritonbuilder.Builder),
"virtualbox-iso": new(virtualboxisobuilder.Builder),
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),

View File

@ -1,22 +1,53 @@
{
"builders": [
{
"name":"chocolate",
"type":"file",
"content":"chocolate",
"target":"chocolate.txt"
"name": "chocolate",
"type": "file",
"content": "chocolate",
"target": "chocolate.txt"
},
{
"name":"vanilla",
"type":"file",
"content":"vanilla",
"target":"vanilla.txt"
"name": "vanilla",
"type": "file",
"content": "vanilla",
"target": "vanilla.txt"
},
{
"name":"cherry",
"type":"file",
"content":"cherry",
"target":"cherry.txt"
"name": "cherry",
"type": "file",
"content": "cherry",
"target": "cherry.txt"
}
],
"post-processors": [
[
{
"name": "apple",
"type": "shell-local",
"inline": [ "touch apple.txt" ]
},
{
"name": "peach",
"type": "shell-local",
"inline": [ "touch peach.txt" ]
}
],
[
{
"name": "pear",
"type": "shell-local",
"inline": [ "touch pear.txt" ]
}
],
[
{
"only": [
"vanilla"
],
"name": "tomato",
"type": "shell-local",
"inline": [ "touch tomato.txt" ]
}
]
]
}

View File

@ -1190,7 +1190,6 @@ func newParser(filename string, b []byte, opts ...Option) *parser {
Stats: &stats,
// start rule is rule [0] unless an alternate entrypoint is specified
entrypoint: g.rules[0].name,
emptyState: make(storeDict),
}
p.setOptions(opts)
@ -1279,9 +1278,6 @@ type parser struct {
choiceNoMatch string
// recovery expression stack, keeps track of the currently available recovery expression, these are traversed in reverse
recoveryStack []map[string]interface{}
// emptyState contains an empty storeDict, which is used to optimize cloneState if global "state" store is not used.
emptyState storeDict
}
// push a variable set on the vstack.
@ -1455,13 +1451,6 @@ func (p *parser) cloneState() storeDict {
defer p.out(p.in("cloneState"))
}
if len(p.cur.state) == 0 {
if len(p.emptyState) > 0 {
p.emptyState = make(storeDict)
}
return p.emptyState
}
state := make(storeDict, len(p.cur.state))
for k, v := range p.cur.state {
if c, ok := v.(Cloner); ok {

View File

@ -20,6 +20,7 @@ import (
"runtime"
"strings"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
)
@ -191,8 +192,9 @@ func (d *DownloadClient) Get() (string, error) {
}
err = fmt.Errorf(
"checksums didn't match expected: %s",
hex.EncodeToString(d.config.Checksum))
"checksums didn't match. expected %s and got %s",
hex.EncodeToString(d.config.Checksum),
hex.EncodeToString(d.config.Hash.Sum(nil)))
}
}
@ -256,11 +258,7 @@ func (d *HTTPDownloader) Download(dst *os.File, src *url.URL) error {
req.Header.Set("User-Agent", d.userAgent)
}
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}
httpClient := commonhelper.HttpClientWithEnvironmentProxy()
resp, err := httpClient.Do(req)
if err != nil || resp == nil {

View File

@ -13,6 +13,7 @@ import (
"strings"
"testing"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
)
@ -258,11 +259,7 @@ func TestDownloadClient_usesDefaultUserAgent(t *testing.T) {
t.Fatal(err)
}
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}
httpClient := commonhelper.HttpClientWithEnvironmentProxy()
_, err = httpClient.Do(req)
if err != nil {
@ -411,7 +408,7 @@ func TestDownloadFileUrl(t *testing.T) {
// Verify that we fail to match the checksum
_, err = client.Get()
if err.Error() != "checksums didn't match expected: 6e6f7065" {
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)
}
@ -442,7 +439,7 @@ func SimulateFileUriDownload(t *testing.T, uri string) (string, error) {
path, err := client.Get()
// 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" {
if !strings.HasPrefix(path, "\\\\") && err.Error() != "checksums didn't match. expected 6e6f7065 and got 606f1945f81a022d0ed0bd99edfd4f99081c1cb1f97fae087291ee14e945e608" {
t.Fatalf("Unexpected failure; expected checksum not to match")
}

View File

@ -1,15 +1,34 @@
package hyperv
import (
"bytes"
"context"
"errors"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"text/template"
"github.com/hashicorp/packer/common/powershell"
)
type scriptOptions struct {
Version string
VMName string
VHDX string
Path string
HardDrivePath string
MemoryStartupBytes int64
NewVHDSizeBytes int64
VHDBlockSizeBytes int64
SwitchName string
Generation uint
DiffDisks bool
FixedVHD bool
}
func GetHostAdapterIpAddressForSwitch(switchName string) (string, error) {
var script = `
param([string]$switchName, [int]$addressIndex)
@ -202,77 +221,101 @@ Hyper-V\Set-VMFloppyDiskDrive -VMName $vmName -Path $null
return err
}
// This was created as a proof of concept for moving logic out of the powershell
// scripting so that we can test the pathways activated by our variables.
// Rather than creating a powershell script with several conditionals, this will
// generate a conditional-free script which already sets all of the necessary
// variables inline.
//
// For examples of what this template will generate, you can look at the
// test cases in ./hyperv_test.go
//
func getCreateVMScript(opts *scriptOptions) (string, error) {
if opts.FixedVHD && opts.Generation == 2 {
return "", fmt.Errorf("Generation 2 VMs don't support fixed disks.")
}
opts.VHDX = opts.VMName + ".vhdx"
if opts.FixedVHD {
opts.VHDX = opts.VMName + ".vhd"
}
var tpl = template.Must(template.New("createVM").Parse(`
$vhdPath = Join-Path -Path "{{ .Path }}" -ChildPath "{{ .VHDX }}"
{{ if ne .HardDrivePath "" -}}
{{- if .DiffDisks -}}
Hyper-V\New-VHD -Path $vhdPath -ParentPath "{{ .HardDrivePath }}" -Differencing -BlockSizeBytes {{ .VHDBlockSizeBytes }}
{{- else -}}
Copy-Item -Path "{{ .HardDrivePath }}" -Destination $vhdPath
{{- end -}}
{{- else -}}
{{- if .FixedVHD -}}
Hyper-V\New-VHD -Path $vhdPath -Fixed -SizeBytes {{ .NewVHDSizeBytes }}
{{- else -}}
Hyper-V\New-VHD -Path $vhdPath -SizeBytes {{ .NewVHDSizeBytes }} -BlockSizeBytes {{ .VHDBlockSizeBytes }}
{{- end -}}
{{- end }}
Hyper-V\New-VM -Name "{{ .VMName }}" -Path "{{ .Path }}" -MemoryStartupBytes {{ .MemoryStartupBytes }} -VHDPath $vhdPath -SwitchName "{{ .SwitchName }}"
{{- if eq .Generation 2}} -Generation {{ .Generation }} {{- end -}}
{{- if ne .Version ""}} -Version {{ .Version }} {{- end -}}
`))
var b bytes.Buffer
err := tpl.Execute(&b, opts)
if err != nil {
return "", err
}
// Tidy away the excess newlines left over by the template
regex, err := regexp.Compile("^\n")
if err != nil {
return "", err
}
final := regex.ReplaceAllString(b.String(), "")
regex, err = regexp.Compile("\n\n")
final = regex.ReplaceAllString(final, "\n")
return final, nil
}
func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64,
diskSize int64, diskBlockSize int64, switchName string, generation uint,
diffDisks bool, fixedVHD bool) error {
diffDisks bool, fixedVHD bool, version string) error {
if generation == 2 {
var script = `
param([string]$vmName, [string]$path, [string]$harddrivePath, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks)
$vhdx = $vmName + '.vhdx'
$vhdPath = Join-Path -Path $path -ChildPath $vhdx
if ($harddrivePath){
if($diffDisks -eq "true"){
New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes
} else {
Copy-Item -Path $harddrivePath -Destination $vhdPath
opts := scriptOptions{
Version: version,
VMName: vmName,
Path: path,
HardDrivePath: harddrivePath,
MemoryStartupBytes: ram,
NewVHDSizeBytes: diskSize,
VHDBlockSizeBytes: diskBlockSize,
SwitchName: switchName,
Generation: generation,
DiffDisks: diffDisks,
FixedVHD: fixedVHD,
}
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation
} else {
Hyper-V\New-VHD -Path $vhdPath -SizeBytes $newVHDSizeBytes -BlockSizeBytes $vhdBlockSizeBytes
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation
}
`
var ps powershell.PowerShellCmd
if err := ps.Run(script, vmName, path, harddrivePath, strconv.FormatInt(ram, 10),
strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10),
switchName, strconv.FormatInt(int64(generation), 10),
strconv.FormatBool(diffDisks)); err != nil {
return err
}
return DisableAutomaticCheckpoints(vmName)
} else {
var script = `
param([string]$vmName, [string]$path, [string]$harddrivePath, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [string]$diffDisks, [string]$fixedVHD)
if($fixedVHD -eq "true"){
$vhdx = $vmName + '.vhd'
}
else{
$vhdx = $vmName + '.vhdx'
}
$vhdPath = Join-Path -Path $path -ChildPath $vhdx
if ($harddrivePath){
if($diffDisks -eq "true"){
New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes
script, err := getCreateVMScript(&opts)
if err != nil {
return err
}
else{
Copy-Item -Path $harddrivePath -Destination $vhdPath
}
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName
} else {
if($fixedVHD -eq "true"){
Hyper-V\New-VHD -Path $vhdPath -Fixed -SizeBytes $newVHDSizeBytes
}
else {
Hyper-V\New-VHD -Path $vhdPath -SizeBytes $newVHDSizeBytes -BlockSizeBytes $vhdBlockSizeBytes
}
Hyper-V\New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName
}
`
var ps powershell.PowerShellCmd
if err := ps.Run(script, vmName, path, harddrivePath, strconv.FormatInt(ram, 10),
strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10),
switchName, strconv.FormatBool(diffDisks), strconv.FormatBool(fixedVHD)); err != nil {
return err
}
if err := DisableAutomaticCheckpoints(vmName); err != nil {
return err
}
var ps powershell.PowerShellCmd
if err = ps.Run(script); err != nil {
return err
}
if err := DisableAutomaticCheckpoints(vmName); err != nil {
return err
}
if generation != 2 {
return DeleteAllDvdDrives(vmName)
}
return nil
}
func DisableAutomaticCheckpoints(vmName string) error {

View File

@ -0,0 +1,131 @@
package hyperv
import (
"strings"
"testing"
)
func Test_getCreateVMScript(t *testing.T) {
opts := scriptOptions{
Version: "5.0",
VMName: "myvm",
Path: "C://mypath",
HardDrivePath: "C://harddrivepath",
MemoryStartupBytes: int64(1024),
NewVHDSizeBytes: int64(8192),
VHDBlockSizeBytes: int64(10),
SwitchName: "hyperv-vmx-switch",
Generation: uint(1),
DiffDisks: true,
FixedVHD: true,
}
// Check Fixed VHD conditional set
scriptString, err := getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected := `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhd"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch" -Version 5.0`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
// We should never get here thanks to good template validation, but it's
// good to fail rather than trying to run the ps script and erroring.
opts.Generation = uint(2)
scriptString, err = getCreateVMScript(&opts)
if err == nil {
t.Fatalf("Should have Error: %s", err.Error())
}
// Check VHDX conditional set
opts.FixedVHD = false
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch" -Generation 2 -Version 5.0`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
// Check generation 1 no fixed VHD
opts.FixedVHD = false
opts.Generation = uint(1)
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch" -Version 5.0`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
// Check that we use generation one template even if generation is unset
opts.Generation = uint(0)
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
// same "expected" as above
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.Version = ""
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -ParentPath "C://harddrivepath" -Differencing -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.DiffDisks = false
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Copy-Item -Path "C://harddrivepath" -Destination $vhdPath
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.HardDrivePath = ""
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhdx"
Hyper-V\New-VHD -Path $vhdPath -SizeBytes 8192 -BlockSizeBytes 10
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
opts.FixedVHD = true
scriptString, err = getCreateVMScript(&opts)
if err != nil {
t.Fatalf("Error: %s", err.Error())
}
expected = `$vhdPath = Join-Path -Path "C://mypath" -ChildPath "myvm.vhd"
Hyper-V\New-VHD -Path $vhdPath -Fixed -SizeBytes 8192
Hyper-V\New-VM -Name "myvm" -Path "C://mypath" -MemoryStartupBytes 1024 -VHDPath $vhdPath -SwitchName "hyperv-vmx-switch"`
if ok := strings.Compare(scriptString, expected); ok != 0 {
t.Fatalf("EXPECTED: \n%s\n\n RECEIVED: \n%s\n\n", expected, scriptString)
}
}

View File

@ -4,12 +4,13 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"strconv"
"strings"
"github.com/hashicorp/packer/packer/tmp"
)
const (
@ -121,7 +122,7 @@ func (ps *PowerShellCmd) getPowerShellPath() (string, error) {
}
func saveScript(fileContents string) (string, error) {
file, err := ioutil.TempFile(os.TempDir(), "ps")
file, err := tmp.File("powershell")
if err != nil {
return "", err
}

View File

@ -202,8 +202,7 @@ func Validate(config *Config) error {
"the Script or Scripts options instead"))
}
}
// This is currently undocumented and not a feature users are expected to
// interact with.
if config.EnvVarFormat == "" {
if (runtime.GOOS == "windows") && !config.UseLinuxPathing {
config.EnvVarFormat = "set %s=%s && "

View File

@ -3,7 +3,6 @@ package shell_local
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"runtime"
@ -13,6 +12,7 @@ import (
"github.com/hashicorp/packer/common"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
"github.com/hashicorp/packer/template/interpolate"
)
@ -107,7 +107,7 @@ func Run(ui packer.Ui, config *Config) (bool, error) {
}
func createInlineScriptFile(config *Config) (string, error) {
tf, err := ioutil.TempFile("", "packer-shell")
tf, err := tmp.File("packer-shell")
if err != nil {
return "", fmt.Errorf("Error preparing shell script: %s", err)
}

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
@ -13,6 +12,7 @@ import (
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
"github.com/mitchellh/go-fs"
"github.com/mitchellh/go-fs/fat"
)
@ -39,7 +39,7 @@ func (s *StepCreateFloppy) Run(_ context.Context, state multistep.StateBag) mult
ui.Say("Creating floppy disk...")
// Create a temporary file to be our floppy drive
floppyF, err := ioutil.TempFile("", "packer")
floppyF, err := tmp.File("packer")
if err != nil {
state.Put("error",
fmt.Errorf("Error creating temporary file for floppy: %s", err))

View File

@ -1,14 +1,25 @@
package common
import (
"bytes"
"context"
"io/ioutil"
"os"
"testing"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
func testState(t *testing.T) multistep.StateBag {
state := new(multistep.BasicStateBag)
state.Put("ui", &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
})
return state
}
func testStepOutputDir(t *testing.T) *StepOutputDir {
td, err := ioutil.TempDir("", "packer")
if err != nil {

View File

@ -16,6 +16,7 @@ import (
"time"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/tmp"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
@ -793,7 +794,7 @@ func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader, fi *
} else {
// Create a temporary file where we can copy the contents of the src
// so that we can determine the length, since SCP is length-prefixed.
tf, err := ioutil.TempFile("", "packer-upload")
tf, err := tmp.File("packer-upload")
if err != nil {
return fmt.Errorf("Error creating temporary file for upload: %s", err)
}

View File

@ -14,7 +14,7 @@ _packer () {
'-force[Force a build to continue if artifacts exist, deletes existing artifacts.]'
'-machine-readable[Produce machine-readable output.]'
'-color=[(false) Disable color output. (Default: color)]'
'-except=[(foo,bar,baz) Build all builds other than these.]'
'-except=[(foo,bar,baz) Run all builds and post-procesors other than these.]'
'-on-error=[(cleanup,abort,ask) If the build fails do: clean up (default), abort, or ask.]'
'-only=[(foo,bar,baz) Only build the given builds by name.]'
'-parallel=[(false) Disable parallelization. (Default: parallel)]'

View File

@ -30,10 +30,15 @@
}],
"provisioners": [{
"type": "powershell",
"inline": [
"if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
"while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
"inline": [
" # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.",
" while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
" while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }",
" while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
"if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
"while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
]
}]
}

View File

@ -35,10 +35,15 @@
],
"provisioners": [{
"type": "powershell",
"inline": [
"if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
"while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
"inline": [
" # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.",
" while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
" while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }",
" while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
"if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
"while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
]
}]
}

View File

@ -26,10 +26,15 @@
}],
"provisioners": [{
"type": "powershell",
"inline": [
"if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
"while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
"inline": [
" # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.",
" while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
" while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }",
" while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
"if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit",
"while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10 } else { break } }"
]
}]
}

6
go.mod
View File

@ -23,7 +23,7 @@ require (
github.com/armon/go-metrics v0.0.0-20180713145231-3c58d8115a78 // indirect
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 // indirect
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf // indirect
github.com/aws/aws-sdk-go v1.15.54
github.com/aws/aws-sdk-go v1.16.24
github.com/bgentry/speakeasy v0.0.0-20150902231413-36e9cfdd6909 // indirect
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
@ -139,7 +139,7 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4 v0.0.0-20160112163551-383c0d87b5dd
github.com/pierrec/xxHash v0.0.0-20160112165351-5a004441f897 // indirect
github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e // indirect
github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e
github.com/pkg/sftp v0.0.0-20160118190721-e84cc8c755ca
github.com/posener/complete v0.0.0-20170908125245-88e59760adad
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
@ -160,7 +160,7 @@ require (
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
github.com/ulikunitz/xz v0.0.0-20180703112113-636d36a76670
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311
github.com/xanzy/go-cloudstack v2.1.4+incompatible
github.com/xanzy/go-cloudstack v2.4.1+incompatible
golang.org/x/crypto v0.0.0-20180322175230-88942b9c40a4
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be

29
go.sum
View File

@ -1,3 +1,4 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
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=
@ -43,8 +44,8 @@ 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.54 h1:yNFryYgHYlEuNnMM74EZgAaOM2BsJvu77/ev9I9GHlU=
github.com/aws/aws-sdk-go v1.15.54/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
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/speakeasy v0.0.0-20150902231413-36e9cfdd6909 h1:mUVWHQ4tjVv86uJhxSbYqwdz4o+Imcl6HoZtoaqC3zM=
@ -80,7 +81,7 @@ github.com/digitalocean/godo v0.0.0-20170407151542-4c04abe183f4 h1:34XBbvedApvUM
github.com/digitalocean/godo v0.0.0-20170407151542-4c04abe183f4/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
github.com/dnaeon/go-vcr v1.0.0 h1:1QZ+ahihvRvppcJnFvuoHAdnZTf1PqKjO4Ftr1cfQTo=
github.com/dnaeon/go-vcr v1.0.0/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/docker v0.0.0-20170406124027-fa3e2d5ab9b5 h1:V/WghsgVPZl+92drOvPEwuZdHskJmrnD83V1msaV/Jc=
github.com/docker/docker v0.0.0-20170406124027-fa3e2d5ab9b5 h1:DwY2bFs8p+xf2WaQewx2hnGdjYR5K4UAaxcNPyKkTek=
github.com/docker/docker v0.0.0-20170406124027-fa3e2d5ab9b5/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
@ -140,7 +141,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1
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:1yOKgt0XYKUg1HOKunGOSt2ocU4bxLCjmIHt0vRtVHM=
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/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=
@ -169,7 +170,7 @@ github.com/hashicorp/go-plugin v0.0.0-20181030172320-54b6ff97d818 h1:wA1XRGBHMdp
github.com/hashicorp/go-plugin v0.0.0-20181030172320-54b6ff97d818/go.mod h1:Ft7ju2vWzhO0ETMKUVo12XmXmII6eSUS4rsPTkY/siA=
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 h1:qCv4319q2q7XKn0MQbi8p37hsJ+9Xo8e6yojA73JVxk=
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:9HVkPxOpo+yO93Ah4yrO67d/qh0fbLLWbKqhYjyHq9A=
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-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
@ -191,16 +192,14 @@ github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20181106190520-2236f141171e
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20181106190520-2236f141171e/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hetznercloud/hcloud-go v0.0.0-20181016094819-eaf050e4f370 h1:I1Qmx2XRn+A6wNYJErMKlvC1NQxVlu4D21Dq6F7A1x8=
github.com/hetznercloud/hcloud-go v0.0.0-20181016094819-eaf050e4f370/go.mod h1:g5pff0YNAZywQaivY/CmhUYFVp7oP0nu3MiODC2W4Hw=
github.com/hetznercloud/hcloud-go v1.12.0 h1:ugZO8a8ADekqSWi7xWlcs6pxr4QE0tw5VnyjXcL5n28=
github.com/hetznercloud/hcloud-go v1.12.0/go.mod h1:g5pff0YNAZywQaivY/CmhUYFVp7oP0nu3MiODC2W4Hw=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
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/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
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=
@ -357,23 +356,29 @@ github.com/ulikunitz/xz v0.0.0-20180703112113-636d36a76670 h1:HQWT4ta3wW5GZ790Ga
github.com/ulikunitz/xz v0.0.0-20180703112113-636d36a76670/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.1.4+incompatible h1:5c72sRFakVv8wH/HsQFg+xr37CmNQU2UbJfaBjW5f0c=
github.com/xanzy/go-cloudstack v2.1.4+incompatible/go.mod h1:s3eL3z5pNXF5FVybcT+LIVdId8pYn709yv6v5mrkrQE=
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=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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/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/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/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/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@ -408,7 +413,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38VS1jM=
gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
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:y0IMTfclpMdsdIbr6uwmJn5/WZ7vFuObxDMdrylFM3A=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -0,0 +1,14 @@
package common
import (
"net/http"
)
func HttpClientWithEnvironmentProxy() *http.Client {
httpClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}
return httpClient
}

View File

@ -1,12 +1,13 @@
package communicator
import (
"io/ioutil"
"testing"
"github.com/hashicorp/packer/packer/tmp"
)
func TestPEM(t *testing.T) string {
tf, err := ioutil.TempFile("", "packer")
tf, err := tmp.File("packer")
if err != nil {
t.Fatalf("err: %s", err)
}

View File

@ -21,6 +21,7 @@ import (
"github.com/hashicorp/packer/command"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/plugin"
"github.com/hashicorp/packer/packer/tmp"
"github.com/hashicorp/packer/version"
"github.com/mitchellh/cli"
"github.com/mitchellh/panicwrap"
@ -69,7 +70,7 @@ func realMain() int {
// We always send logs to a temporary file that we use in case
// there is a panic. Otherwise, we delete it.
logTempFile, err := ioutil.TempFile("", "packer-log")
logTempFile, err := tmp.File("packer-log")
if err != nil {
fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
return 1

View File

@ -5,6 +5,8 @@ import (
"os"
"strings"
"testing"
"github.com/hashicorp/packer/packer/tmp"
)
type TestCache struct{}
@ -30,7 +32,7 @@ func TestFileCache_Implements(t *testing.T) {
}
func TestFileCache(t *testing.T) {
cacheDir, err := ioutil.TempDir("", "packer")
cacheDir, err := tmp.Dir("packer")
if err != nil {
t.Fatalf("error creating temporary dir: %s", err)
}

View File

@ -21,29 +21,7 @@ func ConfigDir() (string, error) {
return configDir()
}
// ConfigTmpDir returns the configuration tmp directory for Packer
func ConfigTmpDir() (string, error) {
if tmpdir := os.Getenv("PACKER_TMP_DIR"); tmpdir != "" {
return filepath.Abs(tmpdir)
}
configdir, err := configDir()
if err != nil {
return "", err
}
td := filepath.Join(configdir, "tmp")
_, err = os.Stat(td)
if os.IsNotExist(err) {
if err = os.MkdirAll(td, 0755); err != nil {
return "", err
}
} else if err != nil {
return "", err
}
return td, nil
}
func homeDir() (string, error) {
// Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470
if home := os.Getenv("HOME"); home != "" {
log.Printf("Detected home directory from env var: %s", home)

View File

@ -4,8 +4,8 @@ import (
"fmt"
"sort"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-version"
multierror "github.com/hashicorp/go-multierror"
version "github.com/hashicorp/go-version"
"github.com/hashicorp/packer/template"
"github.com/hashicorp/packer/template/interpolate"
)
@ -20,6 +20,9 @@ type Core struct {
builds map[string]*template.Builder
version string
secrets []string
except []string
only []string
}
// CoreConfig is the structure for initializing a new Core. Once a CoreConfig
@ -30,6 +33,10 @@ type CoreConfig struct {
Variables map[string]string
SensitiveVariables []string
Version string
// These are set by command-line flags
Except []string
Only []string
}
// The function type used to lookup Builder implementations.
@ -61,6 +68,8 @@ func NewCore(c *CoreConfig) (*Core, error) {
components: c.Components,
variables: c.Variables,
version: c.Version,
only: c.Only,
except: c.Except,
}
if err := result.validate(); err != nil {
@ -126,7 +135,7 @@ func (c *Core) Build(n string) (Build, error) {
provisioners := make([]coreBuildProvisioner, 0, len(c.Template.Provisioners))
for _, rawP := range c.Template.Provisioners {
// If we're skipping this, then ignore it
if rawP.Skip(rawName) {
if rawP.OnlyExcept.Skip(rawName) {
continue
}
@ -171,10 +180,19 @@ func (c *Core) Build(n string) (Build, error) {
for _, rawPs := range c.Template.PostProcessors {
current := make([]coreBuildPostProcessor, 0, len(rawPs))
for _, rawP := range rawPs {
// If we skip, ignore
if rawP.Skip(rawName) {
continue
}
// -except skips post-processor & build
foundExcept := false
for _, except := range c.except {
if except == rawP.Name {
foundExcept = true
}
}
if foundExcept {
continue
}
// Get the post-processor
postProcessor, err := c.components.PostProcessor(rawP.Type)

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