Merge branch 'master' of https://github.com/hashicorp/packer into packer-provisioner-inspec
This commit is contained in:
commit
c207451f7e
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
52
CHANGELOG.md
52
CHANGELOG.md
|
@ -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:
|
||||
|
|
|
@ -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' }`
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"}),
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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())
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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{},
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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...
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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" ]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 && "
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)]'
|
||||
|
|
|
@ -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 } }"
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -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 } }"
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -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
6
go.mod
|
@ -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
29
go.sum
|
@ -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=
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func HttpClientWithEnvironmentProxy() *http.Client {
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
},
|
||||
}
|
||||
return httpClient
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
3
main.go
3
main.go
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue