Merge branch 'master' of https://github.com/hashicorp/packer into vsphere-tpl
This commit is contained in:
commit
138f54d9d3
|
@ -21,3 +21,4 @@ packer-test*.log
|
|||
|
||||
.idea/
|
||||
*.iml
|
||||
Thumbs.db
|
||||
|
|
|
@ -8,6 +8,7 @@ language: go
|
|||
go:
|
||||
- 1.7.4
|
||||
- 1.8.3
|
||||
- 1.9
|
||||
|
||||
install:
|
||||
- make deps
|
||||
|
@ -21,5 +22,3 @@ branches:
|
|||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: 1.4.3
|
||||
|
|
102
CHANGELOG.md
102
CHANGELOG.md
|
@ -1,20 +1,98 @@
|
|||
## (UNRELEASED)
|
||||
## UNRELEASED
|
||||
|
||||
### IMRPOVEMENTS:
|
||||
* core: Experimental Android ARM support. [GH-5111]
|
||||
* postprocessor/atlas: Disallow pushing vagrant.box artifacts now that Vagrant cloud is live. [GH-4780]
|
||||
* builder/cloudstack: Add support for using a HTTP server. [GH-5017]
|
||||
* builder/cloudstack: Make expunge optional and improve logging output. [GH-5099]
|
||||
* builder/googlecompute: Allow using URL's for network and subnetwork. [GH-5035]
|
||||
### IMPROVEMENTS:
|
||||
|
||||
* provisioner/salt-masterless: Also use sudo to clean up if we used sudo to install. [GH-5240]
|
||||
* builder/profitbricks: added support for Cloud API v4. [GH-5233]
|
||||
* builder/vmware: Set artifact ID to `VMName`. [GH-5187]
|
||||
|
||||
### BACKWARDS INCOMPATIBILITIES:
|
||||
|
||||
* communicator/ssh: Renamed ssh_disable_agent to ssh_disable_agent_forwarding. Need to run fixer on packer configs that use ssh_disable_agent. [GH-5024]
|
||||
* provisioner/shell: Set default for ExpectDisconnect to false. [GH-5283]
|
||||
* communicator: Preserve left-sided white-space in remote command output. Make sure any scripts that parse this output can handle the new whitespace before upgrading. [GH-5167]
|
||||
|
||||
### BUG FIXES:
|
||||
* builder/hyperv: Verify membership in the group Hyper-V Administrators by SID not name. [GH-5022]
|
||||
* builder/docker: Fix windows filepath in docker-toolbox call. [GH-4887]
|
||||
* builder/amazon: Fix panic that happens if ami_block_device_mappings is empty. [GH-5059]
|
||||
|
||||
* builder/vmware: Fix timestamp in default VMName. [GH-5274]
|
||||
* provisioner/windows-restart: The first powershell provisioner after a restart now works. [GH-5272]
|
||||
* builder/amazon: force_deregister works in all regions, not just original region. [GH-5250]
|
||||
* builder/docker: Fix file uploads. [GH-5251]
|
||||
|
||||
|
||||
## 1.0.4 (August 11, 2017)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
* builder/alicloud: Increase polling timeout. [GH-5148]
|
||||
* builder/azure: Add `private_virtual_network_with_public_ip` option to
|
||||
optionally obtain a public IP. [GH-5222]
|
||||
* builder/googlecompute: use a more portable method of obtaining zone.
|
||||
[GH-5192]
|
||||
* builder/hyperv: Properly interpolate user variables in template. [GH-5184]
|
||||
* builder/parallels: Remove soon to be removed --vmtype flag in createvm.
|
||||
[GH-5172]
|
||||
* contrib: add json files to zsh completion. [GH-5195]
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
* builder/amazon: Don't delete snapshots we didn't create. [GH-5211]
|
||||
* builder/amazon: fix builds when using the null communicator. [GH-5217]
|
||||
* builder/docker: Correctly handle case when uploading an empty directory.
|
||||
[GH-5234]
|
||||
* command/push: Don't push variables if they are unspecified. Reverts to
|
||||
behavior in 1.0.1. [GH-5235]
|
||||
* command/push: fix handling of symlinks. [GH-5226]
|
||||
* core: Strip query parameters from ISO URLs when checking against a checksum
|
||||
file. [GH-5181]
|
||||
* provisioner/ansible-remote: Fix issue where packer could hang communicating
|
||||
with ansible-remote. [GH-5146]
|
||||
|
||||
## 1.0.3 (July 17, 2017)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
* builder/azure: Update to latest Azure SDK, enabling support for managed
|
||||
disks. [GH-4511]
|
||||
* builder/cloudstack: Add default cidr_list [ 0.0.0.0/0 ]. [GH-5125]
|
||||
* builder/cloudstack: Add support for ssh_agent_auth. [GH-5130]
|
||||
* builder/cloudstack: Add support for using a HTTP server. [GH-5017]
|
||||
* builder/cloudstack: Allow reading api_url, api_key, and secret_key from env
|
||||
vars. [GH-5124]
|
||||
* builder/cloudstack: Make expunge optional and improve logging output.
|
||||
[GH-5099]
|
||||
* builder/googlecompute: Allow using URL's for network and subnetwork.
|
||||
[GH-5035]
|
||||
* builder/hyperv: Add support for floppy_dirs with hyperv-iso builder.
|
||||
* builder/hyperv: Add support for override of system %temp% path.
|
||||
* core: Experimental Android ARM support. [GH-5111]
|
||||
* post-processor/atlas: Disallow packer push of vagrant.box artifacts to atlas.
|
||||
[GH-4780]
|
||||
* postprocessor/atlas: Disallow pushing vagrant.box artifacts now that Vagrant
|
||||
cloud is live. [GH-4780]
|
||||
|
||||
### BUG FIXES:
|
||||
* builder/amazon: Fix panic that happens if ami_block_device_mappings is empty.
|
||||
[GH-5059]
|
||||
* builder/azure: Write private SSH to file in debug mode. [GH-5070] [GH-5074]
|
||||
* builder/cloudstack: Properly report back errors. [GH-5103]
|
||||
* builder/parallels: Skip missing paths when looking for unnecessary files. [GH-5058]
|
||||
* builder/cloudstack: Properly report back errors. [GH-5103] [GH-5123]
|
||||
* builder/docker: Fix windows filepath in docker-toolbox call [GH-4887]
|
||||
* builder/docker: Fix windows filepath in docker-toolbox call. [GH-4887]
|
||||
* builder/hyperv: Use SID to verify membersip in Admin group, fixing for non-
|
||||
english users. [GH-5022]
|
||||
* builder/hyperv: Verify membership in the group Hyper-V Administrators by SID
|
||||
not name. [GH-5022]
|
||||
* builder/openstack: Update gophercloud version, fixing builds > 1 hr long.
|
||||
[GH-5046]
|
||||
* builder/parallels: Skip missing paths when looking for unnecessary files.
|
||||
[GH-5058]
|
||||
* builder/vmware-esxi: Fix VNC port discovery default timeout. [GH-5051]
|
||||
* communicator/ssh: Add ProvisionerTypes to communicator tests, resolving panic
|
||||
[GH-5116]
|
||||
* communicator/ssh: Resolve race condition that sometimes truncates ssh
|
||||
provisioner stdout [GH-4719]
|
||||
* post-processor/checksum: Fix interpolation of "output". [GH-5112]
|
||||
* push: Push vars in packer config, not just those set from command line and in
|
||||
var-file. [GH-5101]
|
||||
|
||||
## 1.0.2 (June 21, 2017)
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ If you have never worked with Go before, you will have to complete the
|
|||
following steps in order to be able to compile and test Packer. These instructions target POSIX-like environments (Mac OS X, Linux, Cygwin, etc.) so you may need to adjust them for Windows or other shells.
|
||||
|
||||
1. [Download](https://golang.org/dl) and install Go. The instructions below
|
||||
are for go 1.6. Earlier versions of Go are no longer supported.
|
||||
are for go 1.7. Earlier versions of Go are no longer supported.
|
||||
|
||||
2. Set and export the `GOPATH` environment variable and update your `PATH`. For
|
||||
example, you can add to your `.bash_profile`.
|
||||
|
|
1
Makefile
1
Makefile
|
@ -7,6 +7,7 @@ GITBRANCH:=$(shell git symbolic-ref --short HEAD 2>/dev/null)
|
|||
GOFMT_FILES?=$$(find . -not -path "./vendor/*" -name "*.go")
|
||||
GOOS=$(shell go env GOOS)
|
||||
GOARCH=$(shell go env GOARCH)
|
||||
GOPATH=$(shell go env GOPATH)
|
||||
|
||||
# Get the git commit
|
||||
GIT_DIRTY=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)
|
||||
|
|
|
@ -5,6 +5,7 @@ package ecs
|
|||
import (
|
||||
"log"
|
||||
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
|
@ -98,8 +99,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
|
||||
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
||||
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
||||
//DebugKeyPath: b.config.Com
|
||||
RegionId: b.config.AlicloudRegion,
|
||||
DebugKeyPath: fmt.Sprintf("ecs_%s.pem", b.config.PackerBuildName),
|
||||
RegionId: b.config.AlicloudRegion,
|
||||
},
|
||||
}
|
||||
if b.chooseNetworkType() == VpcNet {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/mitchellh/multistep"
|
||||
"time"
|
||||
)
|
||||
|
||||
type stepAttachKeyPar struct {
|
||||
|
@ -21,7 +22,7 @@ func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
|
|||
client := state.Get("client").(*ecs.Client)
|
||||
config := state.Get("config").(Config)
|
||||
instance := state.Get("instance").(*ecs.InstanceAttributesType)
|
||||
retry_times := 3
|
||||
timeoutPoint := time.Now().Add(120 * time.Second)
|
||||
for {
|
||||
err := client.AttachKeyPair(&ecs.AttachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
|
||||
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
|
||||
|
@ -29,8 +30,8 @@ func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
|
|||
e, _ := err.(*common.Error)
|
||||
if (!(e.Code == "MissingParameter" || e.Code == "DependencyViolation.WindowsInstance" ||
|
||||
e.Code == "InvalidKeyPairName.NotFound" || e.Code == "InvalidRegionId.NotFound")) &&
|
||||
retry_times > 0 {
|
||||
retry_times = retry_times - 1
|
||||
time.Now().Before(timeoutPoint) {
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
err := fmt.Errorf("Error attaching keypair %s to instance %s : %s",
|
||||
|
|
|
@ -121,12 +121,12 @@ func (s *stepConfigAlicloudSecurityGroup) Cleanup(state multistep.StateBag) {
|
|||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
message(state, "security group")
|
||||
start := time.Now().Add(10 * time.Second)
|
||||
timeoutPoint := time.Now().Add(120 * time.Second)
|
||||
for {
|
||||
if err := client.DeleteSecurityGroup(common.Region(s.RegionId), s.SecurityGroupId); err != nil {
|
||||
e, _ := err.(*common.Error)
|
||||
if e.Code == "DependencyViolation" && time.Now().Before(start) {
|
||||
time.Sleep(1 * time.Second)
|
||||
if e.Code == "DependencyViolation" && time.Now().Before(timeoutPoint) {
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
ui.Error(fmt.Sprintf("Failed to delete security group, it may still be around: %s", err))
|
||||
|
|
|
@ -78,13 +78,13 @@ func (s *stepConfigAlicloudVPC) Cleanup(state multistep.StateBag) {
|
|||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
message(state, "VPC")
|
||||
start := time.Now().Add(10 * time.Second)
|
||||
timeoutPoint := time.Now().Add(60 * time.Second)
|
||||
for {
|
||||
if err := client.DeleteVpc(s.VpcId); err != nil {
|
||||
e, _ := err.(*common.Error)
|
||||
if (e.Code == "DependencyViolation.Instance" || e.Code == "DependencyViolation.RouteEntry" ||
|
||||
e.Code == "DependencyViolation.VSwitch" ||
|
||||
e.Code == "DependencyViolation.SecurityGroup") && time.Now().Before(start) {
|
||||
e.Code == "DependencyViolation.SecurityGroup") && time.Now().Before(timeoutPoint) {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ func (s *stepConfigAlicloudVSwitch) Run(state multistep.StateBag) multistep.Step
|
|||
}
|
||||
if err := client.WaitForVSwitchAvailable(vpcId, s.VSwitchId, ALICLOUD_DEFAULT_TIMEOUT); err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(fmt.Sprintf("Timeout waiting for vswitch to become avaiable: %v", err))
|
||||
ui.Error(fmt.Sprintf("Timeout waiting for vswitch to become available: %v", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("vswitchid", vswitchId)
|
||||
|
@ -130,13 +130,13 @@ func (s *stepConfigAlicloudVSwitch) Cleanup(state multistep.StateBag) {
|
|||
client := state.Get("client").(*ecs.Client)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
message(state, "vSwitch")
|
||||
start := time.Now().Add(10 * time.Second)
|
||||
timeoutPoint := time.Now().Add(10 * time.Second)
|
||||
for {
|
||||
if err := client.DeleteVSwitch(s.VSwitchId); err != nil {
|
||||
e, _ := err.(*common.Error)
|
||||
if (e.Code == "IncorrectVSwitchStatus" || e.Code == "DependencyViolation" ||
|
||||
e.Code == "DependencyViolation.HaVip" ||
|
||||
e.Code == "IncorretRouteEntryStatus") && time.Now().Before(start) {
|
||||
e.Code == "IncorretRouteEntryStatus") && time.Now().Before(timeoutPoint) {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -213,9 +213,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
if !b.config.FromScratch {
|
||||
steps = append(steps,
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
},
|
||||
&StepCheckRootDevice{},
|
||||
)
|
||||
|
@ -245,17 +246,22 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&StepEarlyCleanup{},
|
||||
&StepSnapshot{},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||
AMIName: b.config.AMIName,
|
||||
Regions: b.config.AMIRegions,
|
||||
},
|
||||
&StepRegisterAMI{
|
||||
RootVolumeSize: b.config.RootVolumeSize,
|
||||
RootVolumeSize: b.config.RootVolumeSize,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&awscommon.StepCreateEncryptedAMICopy{
|
||||
KeyID: b.config.AMIKmsKeyId,
|
||||
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
||||
Name: b.config.AMIName,
|
||||
AMIMappings: b.config.AMIBlockDevices.AMIMappings,
|
||||
},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
|
|
@ -4,7 +4,8 @@ package chroot
|
|||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// See: http://linux.die.net/include/sys/file.h
|
||||
|
@ -13,7 +14,7 @@ const LOCK_NB = 4
|
|||
const LOCK_UN = 8
|
||||
|
||||
func lockFile(f *os.File) error {
|
||||
err := syscall.Flock(int(f.Fd()), LOCK_EX)
|
||||
err := unix.Flock(int(f.Fd()), LOCK_EX)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -22,5 +23,5 @@ func lockFile(f *os.File) error {
|
|||
}
|
||||
|
||||
func unlockFile(f *os.File) error {
|
||||
return syscall.Flock(int(f.Fd()), LOCK_UN)
|
||||
return unix.Flock(int(f.Fd()), LOCK_UN)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,9 @@ import (
|
|||
|
||||
// StepRegisterAMI creates the AMI.
|
||||
type StepRegisterAMI struct {
|
||||
RootVolumeSize int64
|
||||
RootVolumeSize int64
|
||||
EnableAMIENASupport bool
|
||||
EnableAMISriovNetSupport bool
|
||||
}
|
||||
|
||||
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
@ -75,11 +77,12 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
|||
registerOpts = buildRegisterOpts(config, image, newMappings)
|
||||
}
|
||||
|
||||
if config.AMIEnhancedNetworking {
|
||||
if s.EnableAMISriovNetSupport {
|
||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||
registerOpts.SriovNetSupport = aws.String("simple")
|
||||
|
||||
}
|
||||
if s.EnableAMIENASupport {
|
||||
// Set EnaSupport to true
|
||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||
registerOpts.EnaSupport = aws.Bool(true)
|
||||
|
|
|
@ -17,7 +17,8 @@ type AMIConfig struct {
|
|||
AMIRegions []string `mapstructure:"ami_regions"`
|
||||
AMISkipRegionValidation bool `mapstructure:"skip_region_validation"`
|
||||
AMITags map[string]string `mapstructure:"tags"`
|
||||
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
|
||||
AMIENASupport bool `mapstructure:"ena_support"`
|
||||
AMISriovNetSupport bool `mapstructure:"sriov_support"`
|
||||
AMIForceDeregister bool `mapstructure:"force_deregister"`
|
||||
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
|
||||
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
|
||||
|
|
|
@ -51,7 +51,7 @@ func (a *Artifact) String() string {
|
|||
}
|
||||
|
||||
sort.Strings(amiStrings)
|
||||
return fmt.Sprintf("AMIs were created:\n\n%s", strings.Join(amiStrings, "\n"))
|
||||
return fmt.Sprintf("AMIs were created:\n%s\n", strings.Join(amiStrings, "\n"))
|
||||
}
|
||||
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
|
|
|
@ -48,9 +48,9 @@ func TestArtifactState_atlasMetadata(t *testing.T) {
|
|||
|
||||
func TestArtifactString(t *testing.T) {
|
||||
expected := `AMIs were created:
|
||||
|
||||
east: foo
|
||||
west: bar`
|
||||
west: bar
|
||||
`
|
||||
|
||||
amis := make(map[string]string)
|
||||
amis["east"] = "foo"
|
||||
|
|
|
@ -10,59 +10,76 @@ import (
|
|||
)
|
||||
|
||||
type StepDeregisterAMI struct {
|
||||
AccessConfig *AccessConfig
|
||||
ForceDeregister bool
|
||||
ForceDeleteSnapshot bool
|
||||
AMIName string
|
||||
Regions []string
|
||||
}
|
||||
|
||||
func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
regions := s.Regions
|
||||
if len(regions) == 0 {
|
||||
regions = append(regions, s.AccessConfig.RawRegion)
|
||||
}
|
||||
|
||||
// Check for force deregister
|
||||
if s.ForceDeregister {
|
||||
resp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
||||
Filters: []*ec2.Filter{{
|
||||
Name: aws.String("name"),
|
||||
Values: []*string{aws.String(s.AMIName)},
|
||||
}}})
|
||||
for _, region := range regions {
|
||||
// get new connection for each region in which we need to deregister vms
|
||||
session, err := s.AccessConfig.Session()
|
||||
if err != nil {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error describing AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
regionconn := ec2.New(session.Copy(&aws.Config{
|
||||
Region: aws.String(region)},
|
||||
))
|
||||
|
||||
// Deregister image(s) by name
|
||||
for _, i := range resp.Images {
|
||||
_, err := ec2conn.DeregisterImage(&ec2.DeregisterImageInput{
|
||||
ImageId: i.ImageId,
|
||||
})
|
||||
resp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
|
||||
Filters: []*ec2.Filter{{
|
||||
Name: aws.String("name"),
|
||||
Values: []*string{aws.String(s.AMIName)},
|
||||
}}})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deregistering existing AMI: %s", err)
|
||||
err := fmt.Errorf("Error describing AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Deregistered AMI %s, id: %s", s.AMIName, *i.ImageId))
|
||||
|
||||
// Delete snapshot(s) by image
|
||||
if s.ForceDeleteSnapshot {
|
||||
for _, b := range i.BlockDeviceMappings {
|
||||
if b.Ebs != nil && aws.StringValue(b.Ebs.SnapshotId) != "" {
|
||||
_, err := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{
|
||||
SnapshotId: b.Ebs.SnapshotId,
|
||||
})
|
||||
// Deregister image(s) by name
|
||||
for _, i := range resp.Images {
|
||||
_, err := regionconn.DeregisterImage(&ec2.DeregisterImageInput{
|
||||
ImageId: i.ImageId,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deleting existing snapshot: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deregistering existing AMI: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Deregistered AMI %s, id: %s", s.AMIName, *i.ImageId))
|
||||
|
||||
// Delete snapshot(s) by image
|
||||
if s.ForceDeleteSnapshot {
|
||||
for _, b := range i.BlockDeviceMappings {
|
||||
if b.Ebs != nil && aws.StringValue(b.Ebs.SnapshotId) != "" {
|
||||
_, err := regionconn.DeleteSnapshot(&ec2.DeleteSnapshotInput{
|
||||
SnapshotId: b.Ebs.SnapshotId,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deleting existing snapshot: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Deleted snapshot: %s", *b.Ebs.SnapshotId))
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Deleted snapshot: %s", *b.Ebs.SnapshotId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ type StepCreateEncryptedAMICopy struct {
|
|||
KeyID string
|
||||
EncryptBootVolume bool
|
||||
Name string
|
||||
AMIMappings []BlockDevice
|
||||
}
|
||||
|
||||
func (s *StepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
@ -116,9 +117,18 @@ func (s *StepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste
|
|||
ui.Say("Deleting unencrypted snapshots")
|
||||
snapshots := state.Get("snapshots").(map[string][]string)
|
||||
|
||||
OuterLoop:
|
||||
for _, blockDevice := range unencImage.BlockDeviceMappings {
|
||||
if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
|
||||
ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
|
||||
// If this packer run didn't create it, then don't delete it
|
||||
for _, origDevice := range s.AMIMappings {
|
||||
if origDevice.SnapshotId == *blockDevice.Ebs.SnapshotId {
|
||||
ui.Message(fmt.Sprintf("Keeping Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
|
||||
continue OuterLoop
|
||||
}
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Deleting Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
|
||||
deleteSnapOpts := &ec2.DeleteSnapshotInput{
|
||||
SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId),
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ import (
|
|||
)
|
||||
|
||||
type StepModifyEBSBackedInstance struct {
|
||||
EnableEnhancedNetworking bool
|
||||
EnableAMIENASupport bool
|
||||
EnableAMISriovNetSupport bool
|
||||
}
|
||||
|
||||
func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
@ -18,9 +19,9 @@ func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.St
|
|||
instance := state.Get("instance").(*ec2.Instance)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if s.EnableEnhancedNetworking {
|
||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||
if s.EnableAMISriovNetSupport {
|
||||
ui.Say("Enabling Enhanced Networking (SR-IOV)...")
|
||||
simple := "simple"
|
||||
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
||||
|
@ -33,11 +34,13 @@ func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.St
|
|||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Set EnaSupport to true.
|
||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||
// Set EnaSupport to true.
|
||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||
if s.EnableAMIENASupport {
|
||||
ui.Say("Enabling Enhanced Networking (ENA)...")
|
||||
_, err = ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
||||
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
||||
InstanceId: instance.InstanceId,
|
||||
EnaSupport: &ec2.AttributeBooleanValue{Value: aws.Bool(true)},
|
||||
})
|
||||
|
|
|
@ -136,7 +136,23 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||
|
||||
var instanceId string
|
||||
|
||||
ui.Say("Adding tags to source instance")
|
||||
if _, exists := s.Tags["Name"]; !exists {
|
||||
s.Tags["Name"] = "Packer Builder"
|
||||
}
|
||||
|
||||
createTagsAfterInstanceStarts := true
|
||||
ec2Tags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error tagging source instance: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ReportTags(ui, ec2Tags)
|
||||
|
||||
if spotPrice == "" || spotPrice == "0" {
|
||||
|
||||
runOpts := &ec2.RunInstancesInput{
|
||||
ImageId: &s.SourceAMI,
|
||||
InstanceType: &s.InstanceType,
|
||||
|
@ -149,6 +165,16 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||
EbsOptimized: &s.EbsOptimized,
|
||||
}
|
||||
|
||||
if len(ec2Tags) > 0 {
|
||||
runTags := &ec2.TagSpecification{
|
||||
ResourceType: aws.String("instance"),
|
||||
Tags: ec2Tags,
|
||||
}
|
||||
|
||||
runOpts.SetTagSpecifications([]*ec2.TagSpecification{runTags})
|
||||
createTagsAfterInstanceStarts = false
|
||||
}
|
||||
|
||||
if keyName != "" {
|
||||
runOpts.KeyName = &keyName
|
||||
}
|
||||
|
@ -255,6 +281,7 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
instanceId = *spotResp.SpotInstanceRequests[0].InstanceId
|
||||
|
||||
}
|
||||
|
||||
// Set the instance ID so that the cleanup works properly
|
||||
|
@ -278,43 +305,30 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
|
|||
|
||||
instance := latestInstance.(*ec2.Instance)
|
||||
|
||||
ui.Say("Adding tags to source instance")
|
||||
if _, exists := s.Tags["Name"]; !exists {
|
||||
s.Tags["Name"] = "Packer Builder"
|
||||
}
|
||||
|
||||
ec2Tags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error tagging source instance: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ReportTags(ui, ec2Tags)
|
||||
|
||||
// Retry creating tags for about 2.5 minutes
|
||||
err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
|
||||
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
|
||||
Tags: ec2Tags,
|
||||
Resources: []*string{instance.InstanceId},
|
||||
})
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if awsErr, ok := err.(awserr.Error); ok {
|
||||
if awsErr.Code() == "InvalidInstanceID.NotFound" {
|
||||
return false, nil
|
||||
if createTagsAfterInstanceStarts {
|
||||
// Retry creating tags for about 2.5 minutes
|
||||
err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
|
||||
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
|
||||
Tags: ec2Tags,
|
||||
Resources: []*string{instance.InstanceId},
|
||||
})
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return true, err
|
||||
})
|
||||
if awsErr, ok := err.(awserr.Error); ok {
|
||||
if awsErr.Code() == "InvalidInstanceID.NotFound" {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error tagging source instance: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error tagging source instance: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
if s.Debug {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/private/waiter"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/common/uuid"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
|
@ -45,7 +45,9 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
|
|||
|
||||
port := s.CommConfig.Port()
|
||||
if port == 0 {
|
||||
panic("port must be set to a non-zero value.")
|
||||
if s.CommConfig.Type != "none" {
|
||||
panic("port must be set to a non-zero value.")
|
||||
}
|
||||
}
|
||||
|
||||
// Create the group
|
||||
|
@ -151,36 +153,42 @@ func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
|
|||
}
|
||||
|
||||
func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error {
|
||||
waiterCfg := waiter.Config{
|
||||
Operation: "DescribeSecurityGroups",
|
||||
Delay: 15,
|
||||
ctx := aws.BackgroundContext()
|
||||
w := request.Waiter{
|
||||
Name: "DescribeSecurityGroups",
|
||||
MaxAttempts: 40,
|
||||
Acceptors: []waiter.WaitAcceptor{
|
||||
Acceptors: []request.WaiterAcceptor{
|
||||
{
|
||||
State: "success",
|
||||
Matcher: "path",
|
||||
State: request.SuccessWaiterState,
|
||||
Matcher: request.PathWaiterMatch,
|
||||
Argument: "length(SecurityGroups[]) > `0`",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
State: "retry",
|
||||
Matcher: "error",
|
||||
State: request.RetryWaiterState,
|
||||
Matcher: request.ErrorWaiterMatch,
|
||||
Argument: "",
|
||||
Expected: "InvalidGroup.NotFound",
|
||||
},
|
||||
{
|
||||
State: "retry",
|
||||
Matcher: "error",
|
||||
State: request.RetryWaiterState,
|
||||
Matcher: request.ErrorWaiterMatch,
|
||||
Argument: "",
|
||||
Expected: "InvalidSecurityGroupID.NotFound",
|
||||
},
|
||||
},
|
||||
Logger: c.Config.Logger,
|
||||
NewRequest: func(opts []request.Option) (*request.Request, error) {
|
||||
var inCpy *ec2.DescribeSecurityGroupsInput
|
||||
if input != nil {
|
||||
tmp := *input
|
||||
inCpy = &tmp
|
||||
}
|
||||
req, _ := c.DescribeSecurityGroupsRequest(inCpy)
|
||||
req.SetContext(ctx)
|
||||
req.ApplyOptions(opts...)
|
||||
return req, nil
|
||||
},
|
||||
}
|
||||
|
||||
w := waiter.Waiter{
|
||||
Client: c,
|
||||
Input: input,
|
||||
Config: waiterCfg,
|
||||
}
|
||||
return w.Wait()
|
||||
return w.WaitWithContext(ctx)
|
||||
}
|
||||
|
|
|
@ -17,9 +17,10 @@ import (
|
|||
// Produces:
|
||||
// source_image *ec2.Image - the source AMI info
|
||||
type StepSourceAMIInfo struct {
|
||||
SourceAmi string
|
||||
EnhancedNetworking bool
|
||||
AmiFilters AmiFilterOptions
|
||||
SourceAmi string
|
||||
EnableAMISriovNetSupport bool
|
||||
EnableAMIENASupport bool
|
||||
AmiFilters AmiFilterOptions
|
||||
}
|
||||
|
||||
// Build a slice of AMI filter options from the filters provided.
|
||||
|
@ -103,7 +104,7 @@ func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction {
|
|||
|
||||
// Enhanced Networking can only be enabled on HVM AMIs.
|
||||
// See http://goo.gl/icuXh5
|
||||
if s.EnhancedNetworking && *image.VirtualizationType != "hvm" {
|
||||
if (s.EnableAMIENASupport || s.EnableAMISriovNetSupport) && *image.VirtualizationType != "hvm" {
|
||||
err := fmt.Errorf("Cannot enable enhanced networking, source AMI '%s' is not HVM", s.SourceAmi)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
|
|
|
@ -115,9 +115,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
},
|
||||
&awscommon.StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
|
@ -179,18 +180,22 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
DisableStopInstance: b.config.DisableStopInstance,
|
||||
},
|
||||
&awscommon.StepModifyEBSBackedInstance{
|
||||
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||
AMIName: b.config.AMIName,
|
||||
Regions: b.config.AMIRegions,
|
||||
},
|
||||
&stepCreateAMI{},
|
||||
&awscommon.StepCreateEncryptedAMICopy{
|
||||
KeyID: b.config.AMIKmsKeyId,
|
||||
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
||||
Name: b.config.AMIName,
|
||||
AMIMappings: b.config.AMIBlockDevices.AMIMappings,
|
||||
},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
|
|
|
@ -129,9 +129,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
},
|
||||
&awscommon.StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
|
@ -189,19 +190,24 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
DisableStopInstance: b.config.DisableStopInstance,
|
||||
},
|
||||
&awscommon.StepModifyEBSBackedInstance{
|
||||
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&StepSnapshotNewRootVolume{
|
||||
NewRootMountPoint: b.config.RootDevice.SourceDeviceName,
|
||||
},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||
AMIName: b.config.AMIName,
|
||||
Regions: b.config.AMIRegions,
|
||||
},
|
||||
&StepRegisterAMI{
|
||||
RootDevice: b.config.RootDevice,
|
||||
BlockDevices: b.config.BlockDevices.BuildAMIDevices(),
|
||||
RootDevice: b.config.RootDevice,
|
||||
BlockDevices: b.config.BlockDevices.BuildAMIDevices(),
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&awscommon.StepCreateEncryptedAMICopy{
|
||||
KeyID: b.config.AMIKmsKeyId,
|
||||
|
|
|
@ -12,9 +12,11 @@ import (
|
|||
|
||||
// StepRegisterAMI creates the AMI.
|
||||
type StepRegisterAMI struct {
|
||||
RootDevice RootBlockDevice
|
||||
BlockDevices []*ec2.BlockDeviceMapping
|
||||
image *ec2.Image
|
||||
RootDevice RootBlockDevice
|
||||
BlockDevices []*ec2.BlockDeviceMapping
|
||||
EnableAMIENASupport bool
|
||||
EnableAMISriovNetSupport bool
|
||||
image *ec2.Image
|
||||
}
|
||||
|
||||
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
@ -35,16 +37,16 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
|||
BlockDeviceMappings: blockDevicesExcludingRoot,
|
||||
}
|
||||
|
||||
if config.AMIEnhancedNetworking {
|
||||
if s.EnableAMISriovNetSupport {
|
||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||
registerOpts.SriovNetSupport = aws.String("simple")
|
||||
|
||||
}
|
||||
if s.EnableAMIENASupport {
|
||||
// Set EnaSupport to true
|
||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||
registerOpts.EnaSupport = aws.Bool(true)
|
||||
}
|
||||
|
||||
registerResp, err := ec2conn.RegisterImage(registerOpts)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
|
||||
|
|
|
@ -23,8 +23,9 @@ type Config struct {
|
|||
awscommon.AccessConfig `mapstructure:",squash"`
|
||||
awscommon.RunConfig `mapstructure:",squash"`
|
||||
|
||||
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
|
||||
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
|
||||
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
|
||||
AMIENASupport bool `mapstructure:"ena_support"`
|
||||
AMISriovNetSupport bool `mapstructure:"sriov_support"`
|
||||
|
||||
launchBlockDevices awscommon.BlockDevices
|
||||
ctx interpolate.Context
|
||||
|
@ -103,9 +104,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
},
|
||||
&awscommon.StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
|
@ -164,7 +166,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
DisableStopInstance: b.config.DisableStopInstance,
|
||||
},
|
||||
&awscommon.StepModifyEBSBackedInstance{
|
||||
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -200,9 +200,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
AmiFilters: b.config.SourceAmiFilter,
|
||||
},
|
||||
&awscommon.StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
|
@ -258,11 +259,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Debug: b.config.PackerDebug,
|
||||
},
|
||||
&awscommon.StepDeregisterAMI{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
|
||||
AMIName: b.config.AMIName,
|
||||
Regions: b.config.AMIRegions,
|
||||
},
|
||||
&StepRegisterAMI{
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&StepRegisterAMI{},
|
||||
&awscommon.StepAMIRegionCopy{
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
Regions: b.config.AMIRegions,
|
||||
|
|
|
@ -10,7 +10,10 @@ import (
|
|||
"github.com/mitchellh/multistep"
|
||||
)
|
||||
|
||||
type StepRegisterAMI struct{}
|
||||
type StepRegisterAMI struct {
|
||||
EnableAMIENASupport bool
|
||||
EnableAMISriovNetSupport bool
|
||||
}
|
||||
|
||||
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
|
@ -29,12 +32,13 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
|||
registerOpts.VirtualizationType = aws.String(config.AMIVirtType)
|
||||
}
|
||||
|
||||
if config.AMIEnhancedNetworking {
|
||||
if s.EnableAMISriovNetSupport {
|
||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
|
||||
registerOpts.SriovNetSupport = aws.String("simple")
|
||||
|
||||
// Set EnaSupport to true.
|
||||
}
|
||||
if s.EnableAMIENASupport {
|
||||
// Set EnaSupport to true
|
||||
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
|
||||
registerOpts.EnaSupport = aws.Bool(true)
|
||||
}
|
||||
|
|
|
@ -119,7 +119,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
}
|
||||
|
||||
endpointConnectType := PublicEndpoint
|
||||
if b.isPrivateNetworkCommunication() {
|
||||
if b.isPublicPrivateNetworkCommunication() && b.isPrivateNetworkCommunication() {
|
||||
endpointConnectType = PublicEndpointInPrivateNetwork
|
||||
} else if b.isPrivateNetworkCommunication() {
|
||||
endpointConnectType = PrivateEndpoint
|
||||
}
|
||||
|
||||
|
@ -245,6 +247,10 @@ func (b *Builder) writeSSHPrivateKey(ui packer.Ui, debugKeyPath string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *Builder) isPublicPrivateNetworkCommunication() bool {
|
||||
return DefaultPrivateVirtualNetworkWithPublicIp != b.config.PrivateVirtualNetworkWithPublicIp
|
||||
}
|
||||
|
||||
func (b *Builder) isPrivateNetworkCommunication() bool {
|
||||
return b.config.VirtualNetworkName != ""
|
||||
}
|
||||
|
|
|
@ -34,10 +34,11 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
DefaultCloudEnvironmentName = "Public"
|
||||
DefaultImageVersion = "latest"
|
||||
DefaultUserName = "packer"
|
||||
DefaultVMSize = "Standard_A1"
|
||||
DefaultCloudEnvironmentName = "Public"
|
||||
DefaultImageVersion = "latest"
|
||||
DefaultUserName = "packer"
|
||||
DefaultPrivateVirtualNetworkWithPublicIp = false
|
||||
DefaultVMSize = "Standard_A1"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -73,24 +74,27 @@ type Config struct {
|
|||
Location string `mapstructure:"location"`
|
||||
VMSize string `mapstructure:"vm_size"`
|
||||
|
||||
ManagedImageResourceGroupName string `mapstructure:"managed_image_resource_group_name"`
|
||||
ManagedImageName string `mapstructure:"managed_image_name"`
|
||||
manageImageLocation string
|
||||
ManagedImageResourceGroupName string `mapstructure:"managed_image_resource_group_name"`
|
||||
ManagedImageName string `mapstructure:"managed_image_name"`
|
||||
ManagedImageStorageAccountType string `mapstructure:"managed_image_storage_account_type"`
|
||||
managedImageStorageAccountType compute.StorageAccountTypes
|
||||
manageImageLocation string
|
||||
|
||||
// Deployment
|
||||
AzureTags map[string]*string `mapstructure:"azure_tags"`
|
||||
ResourceGroupName string `mapstructure:"resource_group_name"`
|
||||
StorageAccount string `mapstructure:"storage_account"`
|
||||
TempComputeName string `mapstructure:"temp_compute_name"`
|
||||
TempResourceGroupName string `mapstructure:"temp_resource_group_name"`
|
||||
storageAccountBlobEndpoint string
|
||||
CloudEnvironmentName string `mapstructure:"cloud_environment_name"`
|
||||
cloudEnvironment *azure.Environment
|
||||
VirtualNetworkName string `mapstructure:"virtual_network_name"`
|
||||
VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"`
|
||||
VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"`
|
||||
CustomDataFile string `mapstructure:"custom_data_file"`
|
||||
customData string
|
||||
AzureTags map[string]*string `mapstructure:"azure_tags"`
|
||||
ResourceGroupName string `mapstructure:"resource_group_name"`
|
||||
StorageAccount string `mapstructure:"storage_account"`
|
||||
TempComputeName string `mapstructure:"temp_compute_name"`
|
||||
TempResourceGroupName string `mapstructure:"temp_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"`
|
||||
VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"`
|
||||
CustomDataFile string `mapstructure:"custom_data_file"`
|
||||
customData string
|
||||
|
||||
// OS
|
||||
OSType string `mapstructure:"os_type"`
|
||||
|
@ -403,6 +407,10 @@ func provideDefaultValues(c *Config) {
|
|||
c.VMSize = DefaultVMSize
|
||||
}
|
||||
|
||||
if c.ManagedImageStorageAccountType == "" {
|
||||
c.managedImageStorageAccountType = compute.StandardLRS
|
||||
}
|
||||
|
||||
if c.ImagePublisher != "" && c.ImageVersion == "" {
|
||||
c.ImageVersion = DefaultImageVersion
|
||||
}
|
||||
|
@ -526,6 +534,10 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
|||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku) or a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name"))
|
||||
}
|
||||
|
||||
if isImageUrl && c.ManagedImageResourceGroupName != "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A managed image must be created from a managed image, it cannot be created from a VHD."))
|
||||
}
|
||||
|
||||
if c.ImageUrl == "" && c.CustomManagedImageName == "" {
|
||||
if c.ImagePublisher == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified"))
|
||||
|
@ -596,4 +608,13 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
|
|||
} else {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The os_type %q is invalid", c.OSType))
|
||||
}
|
||||
|
||||
switch c.ManagedImageStorageAccountType {
|
||||
case "", string(compute.StandardLRS):
|
||||
c.managedImageStorageAccountType = compute.StandardLRS
|
||||
case string(compute.PremiumLRS):
|
||||
c.managedImageStorageAccountType = compute.PremiumLRS
|
||||
default:
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The managed_image_storage_account_type %q is invalid", c.ManagedImageStorageAccountType))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/compute"
|
||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
@ -48,6 +49,10 @@ func TestConfigShouldProvideReasonableDefaultValues(t *testing.T) {
|
|||
if c.ObjectID != "" {
|
||||
t.Errorf("Expected 'ObjectID' to be nil, but it was '%s'!", c.ObjectID)
|
||||
}
|
||||
|
||||
if c.managedImageStorageAccountType == "" {
|
||||
t.Errorf("Expected 'managedImageStorageAccountType' to be populated, but it was empty!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
|
||||
|
@ -56,6 +61,7 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
|
|||
builderValues["ssh_username"] = "override_username"
|
||||
builderValues["vm_size"] = "override_vm_size"
|
||||
builderValues["communicator"] = "ssh"
|
||||
builderValues["managed_image_storage_account_type"] = "Premium_LRS"
|
||||
|
||||
c, _, err := newConfig(builderValues, getPackerConfiguration())
|
||||
|
||||
|
@ -64,23 +70,27 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
|
|||
}
|
||||
|
||||
if c.Password != "override_password" {
|
||||
t.Errorf("Expected 'Password' to be set to 'override_password', but found '%s'!", c.Password)
|
||||
t.Errorf("Expected 'Password' to be set to 'override_password', but found %q!", c.Password)
|
||||
}
|
||||
|
||||
if c.Comm.SSHPassword != "override_password" {
|
||||
t.Errorf("Expected 'c.Comm.SSHPassword' to be set to 'override_password', but found '%s'!", c.Comm.SSHPassword)
|
||||
t.Errorf("Expected 'c.Comm.SSHPassword' to be set to 'override_password', but found %q!", c.Comm.SSHPassword)
|
||||
}
|
||||
|
||||
if c.UserName != "override_username" {
|
||||
t.Errorf("Expected 'UserName' to be set to 'override_username', but found '%s'!", c.UserName)
|
||||
t.Errorf("Expected 'UserName' to be set to 'override_username', but found %q!", c.UserName)
|
||||
}
|
||||
|
||||
if c.Comm.SSHUsername != "override_username" {
|
||||
t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found '%s'!", c.Comm.SSHUsername)
|
||||
t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found %q!", c.Comm.SSHUsername)
|
||||
}
|
||||
|
||||
if c.VMSize != "override_vm_size" {
|
||||
t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found '%s'!", c.VMSize)
|
||||
t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found %q!", c.VMSize)
|
||||
}
|
||||
|
||||
if c.managedImageStorageAccountType != compute.PremiumLRS {
|
||||
t.Errorf("Expected 'managed_image_storage_account_type' to be set to 'Premium_LRS', but found %q!", c.managedImageStorageAccountType)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -782,6 +792,26 @@ func TestConfigShouldRejectVhdAndManagedImageOutput(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// If the user specified a build of a VHD, but started with a managed image it should be rejected.
|
||||
func TestConfigShouldRejectManagedImageSourceAndVhdOutput(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"image_url": "ignore",
|
||||
"location": "ignore",
|
||||
"subscription_id": "ignore",
|
||||
"communicator": "none",
|
||||
"managed_image_resource_group_name": "ignore",
|
||||
"managed_image_name": "ignore",
|
||||
|
||||
// Does not matter for this test case, just pick one.
|
||||
"os_type": constants.Target_Linux,
|
||||
}
|
||||
|
||||
_, _, err := newConfig(config, getPackerConfiguration())
|
||||
if err == nil {
|
||||
t.Fatal("expected config to reject VHD and Managed Image build")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigShouldRejectCustomAndPlatformManagedImageBuild(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"custom_managed_image_resource_group_name": "ignore",
|
||||
|
@ -826,6 +856,52 @@ func TestConfigShouldRejectCustomAndImageUrlForManagedImageBuild(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfigShouldRejectMalformedManageImageStorageAccountTypes(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"custom_managed_image_resource_group_name": "ignore",
|
||||
"custom_managed_image_name": "ignore",
|
||||
"location": "ignore",
|
||||
"subscription_id": "ignore",
|
||||
"communicator": "none",
|
||||
"managed_image_resource_group_name": "ignore",
|
||||
"managed_image_name": "ignore",
|
||||
"managed_image_storage_account_type": "--invalid--",
|
||||
|
||||
// Does not matter for this test case, just pick one.
|
||||
"os_type": constants.Target_Linux,
|
||||
}
|
||||
|
||||
_, _, err := newConfig(config, getPackerConfiguration())
|
||||
if err == nil {
|
||||
t.Fatal("expected config to reject custom and platform input for a managed image build")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigShouldAcceptManagedImageStorageAccountTypes(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"custom_managed_image_resource_group_name": "ignore",
|
||||
"custom_managed_image_name": "ignore",
|
||||
"location": "ignore",
|
||||
"subscription_id": "ignore",
|
||||
"communicator": "none",
|
||||
"managed_image_resource_group_name": "ignore",
|
||||
"managed_image_name": "ignore",
|
||||
|
||||
// Does not matter for this test case, just pick one.
|
||||
"os_type": constants.Target_Linux,
|
||||
}
|
||||
|
||||
storage_account_types := []string{"Premium_LRS", "Standard_LRS"}
|
||||
|
||||
for _, x := range storage_account_types {
|
||||
config["managed_image_storage_account_type"] = x
|
||||
_, _, err := newConfig(config, getPackerConfiguration())
|
||||
if err != nil {
|
||||
t.Fatalf("expected config to accept a managed_image_storage_account_type of %q", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getArmBuilderConfiguration() map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, v := range requiredConfigValues {
|
||||
|
|
|
@ -16,12 +16,14 @@ type EndpointType int
|
|||
const (
|
||||
PublicEndpoint EndpointType = iota
|
||||
PrivateEndpoint
|
||||
PublicEndpointInPrivateNetwork
|
||||
)
|
||||
|
||||
var (
|
||||
EndpointCommunicationText = map[EndpointType]string{
|
||||
PublicEndpoint: "PublicEndpoint",
|
||||
PrivateEndpoint: "PrivateEndpoint",
|
||||
PublicEndpoint: "PublicEndpoint",
|
||||
PrivateEndpoint: "PrivateEndpoint",
|
||||
PublicEndpointInPrivateNetwork: "PublicEndpointInPrivateNetwork",
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -46,6 +48,8 @@ func NewStepGetIPAddress(client *AzureClient, ui packer.Ui, endpoint EndpointTyp
|
|||
step.get = step.getPrivateIP
|
||||
case PublicEndpoint:
|
||||
step.get = step.getPublicIP
|
||||
case PublicEndpointInPrivateNetwork:
|
||||
step.get = step.getPublicIPInPrivateNetwork
|
||||
}
|
||||
|
||||
return step
|
||||
|
@ -70,6 +74,11 @@ func (s *StepGetIPAddress) getPublicIP(resourceGroupName string, ipAddressName s
|
|||
return *resp.IPAddress, nil
|
||||
}
|
||||
|
||||
func (s *StepGetIPAddress) getPublicIPInPrivateNetwork(resourceGroupName string, ipAddressName string, interfaceName string) (string, error) {
|
||||
s.getPrivateIP(resourceGroupName, ipAddressName, interfaceName)
|
||||
return s.getPublicIP(resourceGroupName, ipAddressName, interfaceName)
|
||||
}
|
||||
|
||||
func (s *StepGetIPAddress) Run(state multistep.StateBag) multistep.StepAction {
|
||||
s.say("Getting the VM's IP address ...")
|
||||
|
||||
|
|
|
@ -12,42 +12,50 @@ import (
|
|||
)
|
||||
|
||||
func TestStepGetIPAddressShouldFailIfGetFails(t *testing.T) {
|
||||
var testSubject = &StepGetIPAddress{
|
||||
get: func(string, string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
endpoint: PublicEndpoint,
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
||||
|
||||
stateBag := createTestStateBagStepGetIPAddress()
|
||||
for _, endpoint := range endpoints {
|
||||
var testSubject = &StepGetIPAddress{
|
||||
get: func(string, string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
|
||||
endpoint: endpoint,
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
var result = testSubject.Run(stateBag)
|
||||
if result != multistep.ActionHalt {
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
stateBag := createTestStateBagStepGetIPAddress()
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
||||
var result = testSubject.Run(stateBag)
|
||||
if result != multistep.ActionHalt {
|
||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepGetIPAddressShouldPassIfGetPasses(t *testing.T) {
|
||||
var testSubject = &StepGetIPAddress{
|
||||
get: func(string, string, string) (string, error) { return "", nil },
|
||||
endpoint: PublicEndpoint,
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
||||
|
||||
stateBag := createTestStateBagStepGetIPAddress()
|
||||
for _, endpoint := range endpoints {
|
||||
var testSubject = &StepGetIPAddress{
|
||||
get: func(string, string, string) (string, error) { return "", nil },
|
||||
endpoint: endpoint,
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
var result = testSubject.Run(stateBag)
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
stateBag := createTestStateBagStepGetIPAddress()
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
||||
var result = testSubject.Run(stateBag)
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,50 +63,53 @@ func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|||
var actualResourceGroupName string
|
||||
var actualIPAddressName string
|
||||
var actualNicName string
|
||||
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
||||
|
||||
var testSubject = &StepGetIPAddress{
|
||||
get: func(resourceGroupName string, ipAddressName string, nicName string) (string, error) {
|
||||
actualResourceGroupName = resourceGroupName
|
||||
actualIPAddressName = ipAddressName
|
||||
actualNicName = nicName
|
||||
for _, endpoint := range endpoints {
|
||||
var testSubject = &StepGetIPAddress{
|
||||
get: func(resourceGroupName string, ipAddressName string, nicName string) (string, error) {
|
||||
actualResourceGroupName = resourceGroupName
|
||||
actualIPAddressName = ipAddressName
|
||||
actualNicName = nicName
|
||||
|
||||
return "127.0.0.1", nil
|
||||
},
|
||||
endpoint: PublicEndpoint,
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
return "127.0.0.1", nil
|
||||
},
|
||||
endpoint: endpoint,
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
}
|
||||
|
||||
stateBag := createTestStateBagStepGetIPAddress()
|
||||
var result = testSubject.Run(stateBag)
|
||||
stateBag := createTestStateBagStepGetIPAddress()
|
||||
var result = testSubject.Run(stateBag)
|
||||
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
if result != multistep.ActionContinue {
|
||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
||||
}
|
||||
|
||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
||||
var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string)
|
||||
var expectedNicName = stateBag.Get(constants.ArmNicName).(string)
|
||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
||||
var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string)
|
||||
var expectedNicName = stateBag.Get(constants.ArmNicName).(string)
|
||||
|
||||
if actualIPAddressName != expectedIPAddressName {
|
||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.")
|
||||
}
|
||||
if actualIPAddressName != expectedIPAddressName {
|
||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.")
|
||||
}
|
||||
|
||||
if actualResourceGroupName != expectedResourceGroupName {
|
||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
||||
}
|
||||
if actualResourceGroupName != expectedResourceGroupName {
|
||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
||||
}
|
||||
|
||||
if actualNicName != expectedNicName {
|
||||
t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.")
|
||||
}
|
||||
if actualNicName != expectedNicName {
|
||||
t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.")
|
||||
}
|
||||
|
||||
expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost)
|
||||
if !ok {
|
||||
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost)
|
||||
}
|
||||
expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost)
|
||||
if !ok {
|
||||
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost)
|
||||
}
|
||||
|
||||
if expectedIPAddress != "127.0.0.1" {
|
||||
t.Fatalf("Expected the value of stateBag[%s] to be '127.0.0.1', but got '%s'.", constants.SSHHost, expectedIPAddress)
|
||||
if expectedIPAddress != "127.0.0.1" {
|
||||
t.Fatalf("Expected the value of stateBag[%s] to be '127.0.0.1', but got '%s'.", constants.SSHHost, expectedIPAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
|||
if config.ImageUrl != "" {
|
||||
builder.SetImageUrl(config.ImageUrl, osType)
|
||||
} else if config.CustomManagedImageName != "" {
|
||||
builder.SetManagedDiskUrl(config.customManagedImageID)
|
||||
builder.SetManagedDiskUrl(config.customManagedImageID, config.managedImageStorageAccountType)
|
||||
} else if config.ManagedImageName != "" && config.ImagePublisher != "" {
|
||||
imageID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/ArtifactTypes/vmimage/offers/%s/skus/%s/versions/%s",
|
||||
config.SubscriptionID,
|
||||
|
@ -63,7 +63,7 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
|||
config.ImageSku,
|
||||
config.ImageVersion)
|
||||
|
||||
builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID)
|
||||
builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID, config.managedImageStorageAccountType)
|
||||
} else {
|
||||
builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion)
|
||||
}
|
||||
|
@ -76,7 +76,12 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
|
|||
builder.SetCustomData(config.customData)
|
||||
}
|
||||
|
||||
if config.VirtualNetworkName != "" {
|
||||
if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp {
|
||||
builder.SetPrivateVirtualNetworWithPublicIp(
|
||||
config.VirtualNetworkResourceGroupName,
|
||||
config.VirtualNetworkName,
|
||||
config.VirtualNetworkSubnetName)
|
||||
} else if config.VirtualNetworkName != "" {
|
||||
builder.SetVirtualNetwork(
|
||||
config.VirtualNetworkResourceGroupName,
|
||||
config.VirtualNetworkName,
|
||||
|
|
|
@ -129,6 +129,9 @@
|
|||
"osDisk": {
|
||||
"caching": "ReadWrite",
|
||||
"createOption": "fromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "Standard_LRS"
|
||||
},
|
||||
"name": "osdisk",
|
||||
"osType": "Linux"
|
||||
}
|
||||
|
|
|
@ -132,6 +132,9 @@
|
|||
"osDisk": {
|
||||
"caching": "ReadWrite",
|
||||
"createOption": "fromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "Standard_LRS"
|
||||
},
|
||||
"name": "osdisk",
|
||||
"osType": "Linux"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"adminPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string"
|
||||
},
|
||||
"dnsNameForPublicIP": {
|
||||
"type": "string"
|
||||
},
|
||||
"osDiskName": {
|
||||
"type": "string"
|
||||
},
|
||||
"storageAccountBlobEndpoint": {
|
||||
"type": "string"
|
||||
},
|
||||
"vmName": {
|
||||
"type": "string"
|
||||
},
|
||||
"vmSize": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
||||
"location": "[variables('location')]",
|
||||
"name": "[variables('publicIPAddressName')]",
|
||||
"properties": {
|
||||
"dnsSettings": {
|
||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
||||
},
|
||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
||||
},
|
||||
"type": "Microsoft.Network/publicIPAddresses"
|
||||
},
|
||||
{
|
||||
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"
|
||||
],
|
||||
"location": "[variables('location')]",
|
||||
"name": "[variables('nicName')]",
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig",
|
||||
"properties": {
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"publicIPAddress": {
|
||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
|
||||
},
|
||||
"subnet": {
|
||||
"id": "[variables('subnetRef')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "Microsoft.Network/networkInterfaces"
|
||||
},
|
||||
{
|
||||
"apiVersion": "[variables('apiVersion')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
|
||||
],
|
||||
"location": "[variables('location')]",
|
||||
"name": "[parameters('vmName')]",
|
||||
"properties": {
|
||||
"diagnosticsProfile": {
|
||||
"bootDiagnostics": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('vmSize')]"
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"osProfile": {
|
||||
"adminPassword": "[parameters('adminPassword')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"computerName": "[parameters('vmName')]",
|
||||
"linuxConfiguration": {
|
||||
"ssh": {
|
||||
"publicKeys": [
|
||||
{
|
||||
"keyData": "",
|
||||
"path": "[variables('sshKeyPath')]"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"storageProfile": {
|
||||
"imageReference": {
|
||||
"offer": "--image-offer--",
|
||||
"publisher": "--image-publisher--",
|
||||
"sku": "--image-sku--",
|
||||
"version": "--version--"
|
||||
},
|
||||
"osDisk": {
|
||||
"caching": "ReadWrite",
|
||||
"createOption": "fromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "Standard_LRS"
|
||||
},
|
||||
"name": "osdisk",
|
||||
"osType": "Linux"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "Microsoft.Compute/virtualMachines"
|
||||
}
|
||||
],
|
||||
"variables": {
|
||||
"addressPrefix": "10.0.0.0/16",
|
||||
"apiVersion": "2017-03-30",
|
||||
"location": "[resourceGroup().location]",
|
||||
"managedDiskApiVersion": "2017-03-30",
|
||||
"networkInterfacesApiVersion": "2017-04-01",
|
||||
"nicName": "packerNic",
|
||||
"publicIPAddressApiVersion": "2017-04-01",
|
||||
"publicIPAddressName": "packerPublicIP",
|
||||
"publicIPAddressType": "Dynamic",
|
||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
||||
"subnetAddressPrefix": "10.0.0.0/24",
|
||||
"subnetName": "--virtual_network_subnet_name--",
|
||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
||||
"virtualNetworkName": "--virtual_network_name--",
|
||||
"virtualNetworkResourceGroup": "--virtual_network_resource_group_name--",
|
||||
"virtualNetworksApiVersion": "2017-04-01",
|
||||
"vmStorageAccountContainerName": "images",
|
||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
||||
}
|
||||
}
|
|
@ -318,6 +318,43 @@ func TestVirtualMachineDeployment09(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure the VM template is correct when building with PublicIp and connect to Private Network
|
||||
func TestVirtualMachineDeployment10(t *testing.T) {
|
||||
config := map[string]interface{}{
|
||||
"location": "ignore",
|
||||
"subscription_id": "ignore",
|
||||
"os_type": constants.Target_Linux,
|
||||
"communicator": "none",
|
||||
"image_publisher": "--image-publisher--",
|
||||
"image_offer": "--image-offer--",
|
||||
"image_sku": "--image-sku--",
|
||||
"image_version": "--version--",
|
||||
|
||||
"virtual_network_resource_group_name": "--virtual_network_resource_group_name--",
|
||||
"virtual_network_name": "--virtual_network_name--",
|
||||
"virtual_network_subnet_name": "--virtual_network_subnet_name--",
|
||||
"private_virtual_network_with_public_ip": true,
|
||||
|
||||
"managed_image_name": "ManagedImageName",
|
||||
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
||||
}
|
||||
|
||||
c, _, err := newConfig(config, getPackerConfiguration())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
deployment, err := GetVirtualMachineDeployment(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the link values are not set, and the concrete values are set.
|
||||
func TestKeyVaultDeployment00(t *testing.T) {
|
||||
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
|
||||
|
|
|
@ -45,6 +45,7 @@ type OSDiskUnion struct {
|
|||
Caching compute.CachingTypes `json:"caching,omitempty"`
|
||||
CreateOption compute.DiskCreateOptionTypes `json:"createOption,omitempty"`
|
||||
DiskSizeGB *int32 `json:"diskSizeGB,omitempty"`
|
||||
ManagedDisk *compute.ManagedDiskParameters `json:"managedDisk,omitempty"`
|
||||
}
|
||||
|
||||
// Union of the StorageProfile and ImageStorageProfile types.
|
||||
|
|
|
@ -101,7 +101,7 @@ func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string) error {
|
||||
func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccountType compute.StorageAccountTypes) error {
|
||||
resource, err := s.getResourceByType(resourceVirtualMachine)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -115,11 +115,14 @@ func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string) error {
|
|||
profile.OsDisk.OsType = s.osType
|
||||
profile.OsDisk.CreateOption = compute.FromImage
|
||||
profile.OsDisk.Vhd = nil
|
||||
profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{
|
||||
StorageAccountType: storageAccountType,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version, imageID string) error {
|
||||
func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version, imageID string, storageAccountType compute.StorageAccountTypes) error {
|
||||
resource, err := s.getResourceByType(resourceVirtualMachine)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -137,6 +140,9 @@ func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer,
|
|||
profile.OsDisk.OsType = s.osType
|
||||
profile.OsDisk.CreateOption = compute.FromImage
|
||||
profile.OsDisk.Vhd = nil
|
||||
profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{
|
||||
StorageAccountType: storageAccountType,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -219,6 +225,24 @@ func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtual
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *TemplateBuilder) SetPrivateVirtualNetworWithPublicIp(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error {
|
||||
s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup)
|
||||
s.setVariable("virtualNetworkName", virtualNetworkName)
|
||||
s.setVariable("subnetName", subnetName)
|
||||
|
||||
s.deleteResourceByType(resourceVirtualNetworks)
|
||||
resource, err := s.getResourceByType(resourceNetworkInterfaces)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.deleteResourceDependency(resource, func(s string) bool {
|
||||
return strings.Contains(s, "Microsoft.Network/virtualNetworks")
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TemplateBuilder) SetTags(tags *map[string]*string) error {
|
||||
if tags == nil || len(*tags) == 0 {
|
||||
return nil
|
||||
|
|
|
@ -63,7 +63,7 @@ func TestBMPString(t *testing.T) {
|
|||
|
||||
// some character outside the BMP should error
|
||||
tst = "\U0001f000 East wind (Mahjong)"
|
||||
str, err = bmpString(tst)
|
||||
_, err = bmpString(tst)
|
||||
if err == nil {
|
||||
t.Errorf("expected '%s' to throw error because the first character is not in the BMP", tst)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher
|
|||
|
||||
k := deriveKeyByAlg[algorithmName](params.Salt, password, params.Iterations)
|
||||
iv := deriveIVByAlg[algorithmName](params.Salt, password, params.Iterations)
|
||||
password = nil
|
||||
|
||||
code, err := blockcodeByAlg[algorithmName](k)
|
||||
if err != nil {
|
||||
|
@ -34,7 +33,6 @@ func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher
|
|||
|
||||
func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) {
|
||||
cbc, err := pbDecrypterFor(info.GetAlgorithm(), password)
|
||||
password = nil
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -113,7 +113,6 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID
|
|||
for len(P) < times*v {
|
||||
P = append(P, password...)
|
||||
}
|
||||
password = nil
|
||||
P = P[:times*v]
|
||||
}
|
||||
|
||||
|
|
|
@ -66,9 +66,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
},
|
||||
&stepSetupNetworking{},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.Comm,
|
||||
Host: commHost,
|
||||
SSHConfig: sshConfig,
|
||||
Config: &b.config.Comm,
|
||||
Host: commHost,
|
||||
SSHConfig: SSHConfig(
|
||||
b.config.Comm.SSHAgentAuth,
|
||||
b.config.Comm.SSHUsername,
|
||||
b.config.Comm.SSHPassword),
|
||||
},
|
||||
&common.StepProvision{},
|
||||
&stepShutdownInstance{},
|
||||
|
|
|
@ -79,10 +79,29 @@ func NewConfig(raws ...interface{}) (*Config, error) {
|
|||
var errs *packer.MultiError
|
||||
|
||||
// Set some defaults.
|
||||
if c.APIURL == "" {
|
||||
// Default to environment variable for api_url, if it exists
|
||||
c.APIURL = os.Getenv("CLOUDSTACK_API_URL")
|
||||
}
|
||||
|
||||
if c.APIKey == "" {
|
||||
// Default to environment variable for api_key, if it exists
|
||||
c.APIKey = os.Getenv("CLOUDSTACK_API_KEY")
|
||||
}
|
||||
|
||||
if c.SecretKey == "" {
|
||||
// Default to environment variable for secret_key, if it exists
|
||||
c.SecretKey = os.Getenv("CLOUDSTACK_SECRET_KEY")
|
||||
}
|
||||
|
||||
if c.AsyncTimeout == 0 {
|
||||
c.AsyncTimeout = 30 * time.Minute
|
||||
}
|
||||
|
||||
if len(c.CIDRList) == 0 && !c.UseLocalIPAddress {
|
||||
c.CIDRList = []string{"0.0.0.0/0"}
|
||||
}
|
||||
|
||||
if c.InstanceName == "" {
|
||||
c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
||||
}
|
||||
|
@ -114,10 +133,6 @@ func NewConfig(raws ...interface{}) (*Config, error) {
|
|||
errs = packer.MultiErrorAppend(errs, errors.New("a secret_key must be specified"))
|
||||
}
|
||||
|
||||
if len(c.CIDRList) == 0 && !c.UseLocalIPAddress {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("a cidr_list must be specified"))
|
||||
}
|
||||
|
||||
if c.Network == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("a network must be specified"))
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestNewConfig(t *testing.T) {
|
|||
"source_template": "d31e6af5-94a8-4756-abf3-6493c38db7e5",
|
||||
},
|
||||
Nullify: "cidr_list",
|
||||
Err: true,
|
||||
Err: false,
|
||||
},
|
||||
"no_cidr_list_with_use_local_ip_address": {
|
||||
Config: map[string]interface{}{
|
||||
|
|
|
@ -2,12 +2,14 @@ package cloudstack
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
packerssh "github.com/hashicorp/packer/communicator/ssh"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/xanzy/go-cloudstack/cloudstack"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
func commHost(state multistep.StateBag) (string, error) {
|
||||
|
@ -26,32 +28,54 @@ func commHost(state multistep.StateBag) (string, error) {
|
|||
return config.hostAddress, nil
|
||||
}
|
||||
|
||||
func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) {
|
||||
config := state.Get("config").(*Config)
|
||||
func SSHConfig(useAgent bool, username, password string) func(state multistep.StateBag) (*ssh.ClientConfig, error) {
|
||||
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
|
||||
if useAgent {
|
||||
authSock := os.Getenv("SSH_AUTH_SOCK")
|
||||
if authSock == "" {
|
||||
return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
|
||||
}
|
||||
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: config.Comm.SSHUsername,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(config.Comm.SSHPassword),
|
||||
ssh.KeyboardInteractive(
|
||||
packerssh.PasswordKeyboardInteractive(config.Comm.SSHPassword)),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
sshAgent, err := net.Dial("unix", authSock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
|
||||
}
|
||||
|
||||
if config.Comm.SSHPrivateKey != "" {
|
||||
privateKey, err := ioutil.ReadFile(config.Comm.SSHPrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error loading configured private key file: %s", err)
|
||||
return &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
||||
privateKey, hasKey := state.GetOk("privateKey")
|
||||
|
||||
if hasKey {
|
||||
signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
||||
}
|
||||
|
||||
return &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}, nil
|
||||
|
||||
} else {
|
||||
|
||||
return &ssh.ClientConfig{
|
||||
User: username,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(password),
|
||||
ssh.KeyboardInteractive(
|
||||
packerssh.PasswordKeyboardInteractive(password)),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
clientConfig.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
|
||||
}
|
||||
|
||||
return clientConfig, nil
|
||||
}
|
||||
|
|
|
@ -47,7 +47,9 @@ func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction
|
|||
// Retrieve the instance ID from the previously saved state.
|
||||
instanceID, ok := state.Get("instance_id").(string)
|
||||
if !ok || instanceID == "" {
|
||||
state.Put("error", fmt.Errorf("Could not retrieve instance_id from state!"))
|
||||
err := fmt.Errorf("Could not retrieve instance_id from state!")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -56,7 +58,9 @@ func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction
|
|||
cloudstack.WithProject(config.Project),
|
||||
)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Failed to retrieve the network object: %s", err))
|
||||
err := fmt.Errorf("Failed to retrieve the network object: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -79,7 +83,9 @@ func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction
|
|||
// Associate a new public IP address.
|
||||
ipAddr, err := client.Address.AssociateIpAddress(p)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Failed to associate public IP address: %s", err))
|
||||
err := fmt.Errorf("Failed to associate public IP address: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -107,7 +113,10 @@ func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction
|
|||
// Create the port forward.
|
||||
forward, err := client.Firewall.CreatePortForwardingRule(p)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to create port forward: %s", err))
|
||||
err := fmt.Errorf("Failed to create port forward: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Store the port forward ID.
|
||||
|
@ -117,7 +126,9 @@ func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction
|
|||
ui.Message("Creating network ACL rule...")
|
||||
|
||||
if network.Aclid == "" {
|
||||
state.Put("error", fmt.Errorf("Failed to configure the firewall: no ACL connected to the VPC network"))
|
||||
err := fmt.Errorf("Failed to configure the firewall: no ACL connected to the VPC network")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -135,7 +146,9 @@ func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction
|
|||
// Create the network ACL rule.
|
||||
aclRule, err := client.NetworkACL.CreateNetworkACL(p)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Failed to create network ACL rule: %s", err))
|
||||
err := fmt.Errorf("Failed to create network ACL rule: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -154,7 +167,9 @@ func (s *stepSetupNetworking) Run(state multistep.StateBag) multistep.StepAction
|
|||
|
||||
fwRule, err := client.Firewall.CreateFirewallRule(p)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Failed to create firewall rule: %s", err))
|
||||
err := fmt.Errorf("Failed to create firewall rule: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,9 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
|||
// Retrieve the zone object.
|
||||
zone, _, err := client.Zone.GetZoneByID(config.Zone)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to get zone %s by ID: %s", config.Zone, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -80,7 +82,9 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
|||
httpPort := state.Get("http_port").(uint)
|
||||
httpIP, err := hostIP()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to determine host IP: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
common.SetHTTPIP(httpIP)
|
||||
|
@ -93,6 +97,7 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
|||
ud, err := s.generateUserData(config.UserData, config.HTTPGetOnly)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -102,7 +107,9 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
|||
// Create the new instance.
|
||||
instance, err := client.VirtualMachine.DeployVirtualMachine(p)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error creating new instance %s: %s", config.InstanceName, err))
|
||||
err := fmt.Errorf("Error creating new instance %s: %s", config.InstanceName, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ func (s *stepCreateTemplate) Run(state multistep.StateBag) multistep.StepAction
|
|||
// Retrieve the instance ID from the previously saved state.
|
||||
instanceID, ok := state.Get("instance_id").(string)
|
||||
if !ok || instanceID == "" {
|
||||
state.Put("error", fmt.Errorf("Could not retrieve instance_id from state!"))
|
||||
err := fmt.Errorf("Could not retrieve instance_id from state!")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -51,6 +53,7 @@ func (s *stepCreateTemplate) Run(state multistep.StateBag) multistep.StepAction
|
|||
volumeID, err := getRootVolumeID(client, instanceID)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
@ -60,7 +63,9 @@ func (s *stepCreateTemplate) Run(state multistep.StateBag) multistep.StepAction
|
|||
ui.Message("Creating the new template...")
|
||||
template, err := client.Template.CreateTemplate(p)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error creating the new template %s: %s", config.TemplateName, err))
|
||||
err := fmt.Errorf("Error creating the new template %s: %s", config.TemplateName, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,15 @@ func (s *stepPrepareConfig) Run(state multistep.StateBag) multistep.StepAction {
|
|||
var err error
|
||||
var errs *packer.MultiError
|
||||
|
||||
if config.Comm.SSHPrivateKey != "" {
|
||||
privateKey, err := ioutil.ReadFile(config.Comm.SSHPrivateKey)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error loading configured private key file: %s", err))
|
||||
}
|
||||
|
||||
state.Put("privateKey", privateKey)
|
||||
}
|
||||
|
||||
// First get the project and zone UUID's so we can use them in other calls when needed.
|
||||
if config.Project != "" && !isUUID(config.Project) {
|
||||
config.Project, _, err = client.Project.GetProjectID(config.Project)
|
||||
|
@ -134,6 +143,7 @@ func (s *stepPrepareConfig) Run(state multistep.StateBag) multistep.StepAction {
|
|||
// nil interface.
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
state.Put("error", errs)
|
||||
ui.Error(errs.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,9 @@ func (s *stepShutdownInstance) Run(state multistep.StateBag) multistep.StepActio
|
|||
// Shutdown the virtual machine.
|
||||
_, err := client.VirtualMachine.StopVirtualMachine(p)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error shutting down instance %s: %s", config.InstanceName, err))
|
||||
err := fmt.Errorf("Error shutting down instance %s: %s", config.InstanceName, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
|
|
@ -85,12 +85,12 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
|
|||
}
|
||||
imageTransfer, _, err := client.ImageActions.Transfer(context.TODO(), images[0].ID, transferRequest)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error transfering snapshot: %s", err)
|
||||
err := fmt.Errorf("Error transferring snapshot: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Transfering Snapshot ID: %d", imageTransfer.ID))
|
||||
ui.Say(fmt.Sprintf("transferring Snapshot ID: %d", imageTransfer.ID))
|
||||
if err := waitForImageState(godo.ActionCompleted, imageTransfer.ID, action.ID,
|
||||
client, 20*time.Minute); err != nil {
|
||||
// If we get an error the first time, actually report it
|
||||
|
|
|
@ -60,40 +60,85 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Upload uploads a file to the docker container
|
||||
func (c *Communicator) Upload(dst string, src io.Reader, fi *os.FileInfo) error {
|
||||
if fi == nil {
|
||||
return c.uploadReader(dst, src)
|
||||
}
|
||||
return c.uploadFile(dst, src, fi)
|
||||
}
|
||||
|
||||
// uploadReader writes an io.Reader to a temporary file before uploading
|
||||
func (c *Communicator) uploadReader(dst string, src io.Reader) error {
|
||||
// Create a temporary file to store the upload
|
||||
tempfile, err := ioutil.TempFile(c.HostDir, "upload")
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("Failed to open temp file for writing: %s", err)
|
||||
}
|
||||
defer os.Remove(tempfile.Name())
|
||||
defer tempfile.Close()
|
||||
|
||||
// Copy the contents to the temporary file
|
||||
_, err = io.Copy(tempfile, src)
|
||||
if _, err := io.Copy(tempfile, src); err != nil {
|
||||
return fmt.Errorf("Failed to copy upload file to tempfile: %s", err)
|
||||
}
|
||||
tempfile.Seek(0, 0)
|
||||
fi, err := tempfile.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting tempfile info: %s", err)
|
||||
}
|
||||
return c.uploadFile(dst, tempfile, &fi)
|
||||
}
|
||||
|
||||
// uploadFile uses docker cp to copy the file from the host to the container
|
||||
func (c *Communicator) uploadFile(dst string, src io.Reader, fi *os.FileInfo) error {
|
||||
|
||||
// command format: docker cp /path/to/infile containerid:/path/to/outfile
|
||||
log.Printf("Copying to %s on container %s.", dst, c.ContainerId)
|
||||
|
||||
localCmd := exec.Command("docker", "cp", "-",
|
||||
fmt.Sprintf("%s:%s", c.ContainerId, filepath.Dir(dst)))
|
||||
|
||||
stderrP, err := localCmd.StderrPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to open pipe: %s", err)
|
||||
}
|
||||
|
||||
stdin, err := localCmd.StdinPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to open pipe: %s", err)
|
||||
}
|
||||
|
||||
if err := localCmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
archive := tar.NewWriter(stdin)
|
||||
header, err := tar.FileInfoHeader(*fi, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header.Name = filepath.Base(dst)
|
||||
archive.WriteHeader(header)
|
||||
numBytes, err := io.Copy(archive, src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to pipe upload: %s", err)
|
||||
}
|
||||
log.Printf("Copied %d bytes for %s", numBytes, dst)
|
||||
|
||||
if err := archive.Close(); err != nil {
|
||||
return fmt.Errorf("Failed to close archive: %s", err)
|
||||
}
|
||||
if err := stdin.Close(); err != nil {
|
||||
return fmt.Errorf("Failed to close stdin: %s", err)
|
||||
}
|
||||
|
||||
stderrOut, err := ioutil.ReadAll(stderrP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fi != nil {
|
||||
tempfile.Chmod((*fi).Mode())
|
||||
}
|
||||
tempfile.Close()
|
||||
|
||||
// Copy the file into place by copying the temporary file we put
|
||||
// into the shared folder into the proper location in the container
|
||||
cmd := &packer.RemoteCmd{
|
||||
Command: fmt.Sprintf("command cp %s/%s %s", c.ContainerDir,
|
||||
filepath.Base(tempfile.Name()), dst),
|
||||
}
|
||||
|
||||
if err := c.Start(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the copy to complete
|
||||
cmd.Wait()
|
||||
if cmd.ExitStatus != 0 {
|
||||
return fmt.Errorf("Upload failed with non-zero exit status: %d", cmd.ExitStatus)
|
||||
if err := localCmd.Wait(); err != nil {
|
||||
return fmt.Errorf("Failed to upload to '%s' in container: %s. %s.", dst, stderrOut, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -173,7 +218,7 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error
|
|||
|
||||
// Make the directory, then copy into it
|
||||
cmd := &packer.RemoteCmd{
|
||||
Command: fmt.Sprintf("set -e; mkdir -p %s; cd %s; command cp -R `ls -A .` %s",
|
||||
Command: fmt.Sprintf("set -e; mkdir -p %s; command cp -R %s/. %s",
|
||||
containerDst, containerSrc, containerDst),
|
||||
}
|
||||
if err := c.Start(cmd); err != nil {
|
||||
|
|
|
@ -71,6 +71,7 @@ func TestUploadDownload(t *testing.T) {
|
|||
upload,
|
||||
download,
|
||||
},
|
||||
ProvisionerTypes: []string{"", ""},
|
||||
},
|
||||
}
|
||||
hook := &packer.DispatchHook{Mapping: hooks}
|
||||
|
@ -161,6 +162,7 @@ func TestLargeDownload(t *testing.T) {
|
|||
downloadCupcake,
|
||||
downloadBigcake,
|
||||
},
|
||||
ProvisionerTypes: []string{"", "", ""},
|
||||
},
|
||||
}
|
||||
hook := &packer.DispatchHook{Mapping: hooks}
|
||||
|
|
|
@ -23,18 +23,19 @@ type Config struct {
|
|||
common.PackerConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
Commit bool
|
||||
Discard bool
|
||||
ExportPath string `mapstructure:"export_path"`
|
||||
Image string
|
||||
Pty bool
|
||||
Pull bool
|
||||
RunCommand []string `mapstructure:"run_command"`
|
||||
Volumes map[string]string
|
||||
Privileged bool `mapstructure:"privileged"`
|
||||
Author string
|
||||
Changes []string
|
||||
Message string
|
||||
Commit bool
|
||||
Discard bool
|
||||
ExportPath string `mapstructure:"export_path"`
|
||||
Image string
|
||||
Pty bool
|
||||
Pull bool
|
||||
RunCommand []string `mapstructure:"run_command"`
|
||||
Volumes map[string]string
|
||||
Privileged bool `mapstructure:"privileged"`
|
||||
Author string
|
||||
Changes []string
|
||||
Message string
|
||||
ContainerDir string `mapstructure:"container_dir"`
|
||||
|
||||
// This is used to login to dockerhub to pull a private base container. For
|
||||
// pushing to dockerhub, see the docker post-processors
|
||||
|
@ -112,6 +113,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if c.ContainerDir == "" {
|
||||
c.ContainerDir = "/packer-files"
|
||||
}
|
||||
|
||||
if c.EcrLogin && c.LoginServer == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("ECR login requires login server to be provided."))
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ func (s *StepConnectDocker) Run(state multistep.StateBag) multistep.StepAction {
|
|||
comm := &Communicator{
|
||||
ContainerId: containerId,
|
||||
HostDir: tempDir,
|
||||
ContainerDir: "/packer-files",
|
||||
ContainerDir: config.ContainerDir,
|
||||
Version: version,
|
||||
Config: config,
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
|
|||
for host, container := range config.Volumes {
|
||||
runConfig.Volumes[host] = container
|
||||
}
|
||||
runConfig.Volumes[tempDir] = "/packer-files"
|
||||
runConfig.Volumes[tempDir] = config.ContainerDir
|
||||
|
||||
ui.Say("Starting docker container...")
|
||||
containerId, err := driver.StartContainer(&runConfig)
|
||||
|
|
|
@ -21,7 +21,7 @@ GetMetadata () {
|
|||
echo "$(curl -f -H "Metadata-Flavor: Google" ${BASEMETADATAURL}/${1} 2> /dev/null)"
|
||||
}
|
||||
|
||||
ZONE=$(GetMetadata zone | grep -oP "[^/]*$")
|
||||
ZONE=$(basename $(GetMetadata zone))
|
||||
|
||||
SetMetadata () {
|
||||
gcloud compute instances add-metadata ${HOSTNAME} --metadata ${1}=${2} --zone ${ZONE}
|
||||
|
|
|
@ -229,10 +229,6 @@ func TestStepCreateInstance_errorTimeout(t *testing.T) {
|
|||
state.Put("ssh_public_key", "key")
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
<-time.After(10 * time.Millisecond)
|
||||
errCh <- nil
|
||||
}()
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.stateTimeout = 1 * time.Microsecond
|
||||
|
|
|
@ -92,7 +92,8 @@ type Config struct {
|
|||
// Prepare processes the build configuration parameters.
|
||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"boot_command",
|
||||
|
|
|
@ -13,7 +13,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/xmlpath.v2"
|
||||
"github.com/ChrisTrenkamp/goxpath"
|
||||
"github.com/ChrisTrenkamp/goxpath/tree/xmltree"
|
||||
)
|
||||
|
||||
// Parallels9Driver is a base type for Parallels builders.
|
||||
|
@ -78,13 +79,19 @@ func getConfigValueFromXpath(path, xpath string) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
xpathComp := xmlpath.MustCompile(xpath)
|
||||
root, err := xmlpath.Parse(file)
|
||||
|
||||
doc, err := xmltree.ParseXML(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
value, _ := xpathComp.String(root)
|
||||
return value, nil
|
||||
|
||||
xpExec := goxpath.MustParse(xpath)
|
||||
node, err := xpExec.Exec(doc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return node.String(), nil
|
||||
}
|
||||
|
||||
// Finds an application bundle by identifier (for "darwin" platform only)
|
||||
|
|
|
@ -58,3 +58,29 @@ func TestIPAddress(t *testing.T) {
|
|||
t.Fatalf("Should have found 10.211.55.124, not %s!\n", ip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXMLParseConfig(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "configpvs")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating temp file: %s", err)
|
||||
}
|
||||
defer os.Remove(td)
|
||||
|
||||
config := []byte(`
|
||||
<ExampleParallelsConfig>
|
||||
<SystemConfig>
|
||||
<DiskSize>20</DiskSize>
|
||||
</SystemConfig>
|
||||
</ExampleParallelsConfig>
|
||||
`)
|
||||
ioutil.WriteFile(td+"/config.pvs", config, 0666)
|
||||
|
||||
result, err := getConfigValueFromXpath(td, "//DiskSize")
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing XML: %s", err)
|
||||
}
|
||||
|
||||
if result != "20" {
|
||||
t.Fatalf("Expected %q, got %q", "20", result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ func (s *stepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
|
|||
"create", name,
|
||||
"--distribution", config.GuestOSType,
|
||||
"--dst", config.OutputDir,
|
||||
"--vmtype", "vm",
|
||||
"--no-hdd",
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
}
|
||||
|
||||
if c.PBUrl == "" {
|
||||
c.PBUrl = "https://api.profitbricks.com/rest/v2"
|
||||
c.PBUrl = "https://api.profitbricks.com/cloudapi/v4"
|
||||
}
|
||||
|
||||
if c.Cores == 0 {
|
||||
|
|
|
@ -26,34 +26,33 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction {
|
|||
}
|
||||
ui.Say("Creating Virtual Data Center...")
|
||||
img := s.getImageId(c.Image, c)
|
||||
alias := ""
|
||||
if img == "" {
|
||||
alias = s.getImageAlias(c.Image, c.Region, ui)
|
||||
}
|
||||
|
||||
datacenter := profitbricks.Datacenter{
|
||||
Properties: profitbricks.DatacenterProperties{
|
||||
Name: c.SnapshotName,
|
||||
Location: c.Region,
|
||||
},
|
||||
Entities: profitbricks.DatacenterEntities{
|
||||
Servers: &profitbricks.Servers{
|
||||
Items: []profitbricks.Server{
|
||||
}
|
||||
server := profitbricks.Server{
|
||||
Properties: profitbricks.ServerProperties{
|
||||
Name: c.SnapshotName,
|
||||
Ram: c.Ram,
|
||||
Cores: c.Cores,
|
||||
},
|
||||
Entities: &profitbricks.ServerEntities{
|
||||
Volumes: &profitbricks.Volumes{
|
||||
Items: []profitbricks.Volume{
|
||||
{
|
||||
Properties: profitbricks.ServerProperties{
|
||||
Name: c.SnapshotName,
|
||||
Ram: c.Ram,
|
||||
Cores: c.Cores,
|
||||
},
|
||||
Entities: &profitbricks.ServerEntities{
|
||||
Volumes: &profitbricks.Volumes{
|
||||
Items: []profitbricks.Volume{
|
||||
{
|
||||
Properties: profitbricks.VolumeProperties{
|
||||
Type: c.DiskType,
|
||||
Size: c.DiskSize,
|
||||
Name: c.SnapshotName,
|
||||
Image: img,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Properties: profitbricks.VolumeProperties{
|
||||
Type: c.DiskType,
|
||||
Size: c.DiskSize,
|
||||
Name: c.SnapshotName,
|
||||
ImageAlias: alias,
|
||||
Image: img,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -61,11 +60,11 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction {
|
|||
},
|
||||
}
|
||||
if c.SSHKey != "" {
|
||||
datacenter.Entities.Servers.Items[0].Entities.Volumes.Items[0].Properties.SshKeys = []string{c.SSHKey}
|
||||
server.Entities.Volumes.Items[0].Properties.SshKeys = []string{c.SSHKey}
|
||||
}
|
||||
|
||||
if c.Comm.SSHPassword != "" {
|
||||
datacenter.Entities.Servers.Items[0].Entities.Volumes.Items[0].Properties.ImagePassword = c.Comm.SSHPassword
|
||||
server.Entities.Volumes.Items[0].Properties.ImagePassword = c.Comm.SSHPassword
|
||||
}
|
||||
|
||||
datacenter = profitbricks.CompositeCreateDatacenter(datacenter)
|
||||
|
@ -94,8 +93,20 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction {
|
|||
|
||||
state.Put("datacenter_id", datacenter.Id)
|
||||
|
||||
lan := profitbricks.CreateLan(datacenter.Id, profitbricks.Lan{
|
||||
Properties: profitbricks.LanProperties{
|
||||
server = profitbricks.CreateServer(datacenter.Id, server)
|
||||
if server.StatusCode > 299 {
|
||||
ui.Error(fmt.Sprintf("Error occurred %s", parseErrorMessage(server.Response)))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = s.waitTillProvisioned(server.Headers.Get("Location"), *c)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error occurred while creating a server %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
lan := profitbricks.CreateLan(datacenter.Id, profitbricks.CreateLanRequest{
|
||||
Properties: profitbricks.CreateLanProperties{
|
||||
Public: true,
|
||||
Name: c.SnapshotName,
|
||||
},
|
||||
|
@ -113,8 +124,8 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction {
|
|||
}
|
||||
|
||||
lanId, _ := strconv.Atoi(lan.Id)
|
||||
nic := profitbricks.CreateNic(datacenter.Id, datacenter.Entities.Servers.Items[0].Id, profitbricks.Nic{
|
||||
Properties: profitbricks.NicProperties{
|
||||
nic := profitbricks.CreateNic(datacenter.Id, server.Id, profitbricks.Nic{
|
||||
Properties: &profitbricks.NicProperties{
|
||||
Name: c.SnapshotName,
|
||||
Lan: lanId,
|
||||
Dhcp: true,
|
||||
|
@ -132,9 +143,9 @@ func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction {
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("volume_id", datacenter.Entities.Servers.Items[0].Entities.Volumes.Items[0].Id)
|
||||
state.Put("volume_id", server.Entities.Volumes.Items[0].Id)
|
||||
|
||||
server := profitbricks.GetServer(datacenter.Id, datacenter.Entities.Servers.Items[0].Id)
|
||||
server = profitbricks.GetServer(datacenter.Id, server.Id)
|
||||
|
||||
state.Put("server_ip", server.Entities.Nics.Items[0].Properties.Ips[0])
|
||||
|
||||
|
@ -225,6 +236,25 @@ func (d *stepCreateServer) getImageId(imageName string, c *Config) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui packer.Ui) string {
|
||||
if imageAlias == "" {
|
||||
return ""
|
||||
}
|
||||
locations := profitbricks.GetLocation(location)
|
||||
if len(locations.Properties.ImageAliases) > 0 {
|
||||
for _, i := range locations.Properties.ImageAliases {
|
||||
alias := ""
|
||||
if i != "" {
|
||||
alias = i
|
||||
}
|
||||
if alias != "" && strings.ToLower(alias) == strings.ToLower(imageAlias) {
|
||||
return alias
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseErrorMessage(raw string) (toreturn string) {
|
||||
var tmp map[string]interface{}
|
||||
json.Unmarshal([]byte(raw), &tmp)
|
||||
|
|
|
@ -22,7 +22,7 @@ func (s *stepTakeSnapshot) Run(state multistep.StateBag) multistep.StepAction {
|
|||
dcId := state.Get("datacenter_id").(string)
|
||||
volumeId := state.Get("volume_id").(string)
|
||||
|
||||
snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName)
|
||||
snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "")
|
||||
|
||||
state.Put("snapshotname", c.SnapshotName)
|
||||
|
||||
|
|
|
@ -14,13 +14,14 @@ const BuilderId = "mitchellh.vmware"
|
|||
// Artifact is the result of running the VMware builder, namely a set
|
||||
// of files associated with the resulting machine.
|
||||
type localArtifact struct {
|
||||
id string
|
||||
dir string
|
||||
f []string
|
||||
}
|
||||
|
||||
// NewLocalArtifact returns a VMware artifact containing the files
|
||||
// in the given directory.
|
||||
func NewLocalArtifact(dir string) (packer.Artifact, error) {
|
||||
func NewLocalArtifact(id string, dir string) (packer.Artifact, error) {
|
||||
files := make([]string, 0, 5)
|
||||
visit := func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
|
@ -37,6 +38,7 @@ func NewLocalArtifact(dir string) (packer.Artifact, error) {
|
|||
}
|
||||
|
||||
return &localArtifact{
|
||||
id: id,
|
||||
dir: dir,
|
||||
f: files,
|
||||
}, nil
|
||||
|
@ -50,8 +52,8 @@ func (a *localArtifact) Files() []string {
|
|||
return a.f
|
||||
}
|
||||
|
||||
func (*localArtifact) Id() string {
|
||||
return "VM"
|
||||
func (a *localArtifact) Id() string {
|
||||
return a.id
|
||||
}
|
||||
|
||||
func (a *localArtifact) String() string {
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestNewLocalArtifact(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
a, err := NewLocalArtifact(td)
|
||||
a, err := NewLocalArtifact("vm1", td)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ func TestNewLocalArtifact(t *testing.T) {
|
|||
if a.BuilderId() != BuilderId {
|
||||
t.Fatalf("bad: %#v", a.BuilderId())
|
||||
}
|
||||
if a.Id() != "vm1" {
|
||||
t.Fatalf("bad: %#v", a.Id())
|
||||
}
|
||||
if len(a.Files()) != 1 {
|
||||
t.Fatalf("should length 1: %d", len(a.Files()))
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
// of files associated with the resulting machine.
|
||||
type Artifact struct {
|
||||
builderId string
|
||||
id string
|
||||
dir OutputDir
|
||||
f []string
|
||||
}
|
||||
|
@ -20,8 +21,8 @@ func (a *Artifact) Files() []string {
|
|||
return a.f
|
||||
}
|
||||
|
||||
func (*Artifact) Id() string {
|
||||
return "VM"
|
||||
func (a *Artifact) Id() string {
|
||||
return a.id
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
|
|
|
@ -349,6 +349,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
|
||||
return &Artifact{
|
||||
builderId: builderId,
|
||||
id: b.config.VMName,
|
||||
dir: dir,
|
||||
f: files,
|
||||
}, nil
|
||||
|
|
|
@ -45,6 +45,7 @@ func (s *StepExport) Run(state multistep.StateBag) multistep.StepAction {
|
|||
}
|
||||
|
||||
if c.RemoteType != "esx5" || s.Format == "" {
|
||||
ui.Say("Skipping export of virtual machine (export is allowed only for ESXi and the format needs to be specified)...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
return vmwcommon.NewLocalArtifact(b.config.OutputDir)
|
||||
return vmwcommon.NewLocalArtifact(b.config.VMName, b.config.OutputDir)
|
||||
}
|
||||
|
||||
// Cancel.
|
||||
|
|
|
@ -51,7 +51,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
|
||||
// Defaults
|
||||
if c.VMName == "" {
|
||||
c.VMName = fmt.Sprintf("packer-%s-{{timestamp}}", c.PackerBuildName)
|
||||
c.VMName = fmt.Sprintf(
|
||||
"packer-%s-%d", c.PackerBuildName, interpolate.InitTime.Unix())
|
||||
}
|
||||
|
||||
// Prepare the errors
|
||||
|
|
|
@ -136,7 +136,13 @@ func (c *ISOConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs [
|
|||
}
|
||||
|
||||
func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error {
|
||||
errNotFound := fmt.Errorf("No checksum for %q found at: %s", filepath.Base(c.ISOUrls[0]), c.ISOChecksumURL)
|
||||
u, err := url.Parse(c.ISOUrls[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := filepath.Base(u.Path)
|
||||
|
||||
errNotFound := fmt.Errorf("No checksum for %q found at: %s", filename, c.ISOChecksumURL)
|
||||
for {
|
||||
line, err := rd.ReadString('\n')
|
||||
if err != nil && line == "" {
|
||||
|
@ -148,7 +154,7 @@ func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error {
|
|||
}
|
||||
if strings.ToLower(parts[0]) == c.ISOChecksumType {
|
||||
// BSD-style checksum
|
||||
if parts[1] == fmt.Sprintf("(%s)", filepath.Base(c.ISOUrls[0])) {
|
||||
if parts[1] == fmt.Sprintf("(%s)", filename) {
|
||||
c.ISOChecksum = parts[3]
|
||||
return nil
|
||||
}
|
||||
|
@ -158,7 +164,7 @@ func (c *ISOConfig) parseCheckSumFile(rd *bufio.Reader) error {
|
|||
// Binary mode
|
||||
parts[1] = parts[1][1:]
|
||||
}
|
||||
if parts[1] == filepath.Base(c.ISOUrls[0]) {
|
||||
if parts[1] == filename {
|
||||
c.ISOChecksum = parts[0]
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -152,6 +152,25 @@ func TestISOConfigPrepare_ISOChecksumURL(t *testing.T) {
|
|||
t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum)
|
||||
}
|
||||
|
||||
// Test good - ISOChecksumURL GNU style with query parameters
|
||||
i = testISOConfig()
|
||||
i.ISOChecksum = ""
|
||||
i.RawSingleISOUrl = "http://www.packer.io/the-OS.iso?stuff=boo"
|
||||
|
||||
cs_file, _ = ioutil.TempFile("", "packer-test-")
|
||||
ioutil.WriteFile(cs_file.Name(), []byte(cs_gnu_style), 0666)
|
||||
i.ISOChecksumURL = fmt.Sprintf("%s%s", filePrefix, cs_file.Name())
|
||||
warns, err = i.Prepare(nil)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if i.ISOChecksum != "bar0" {
|
||||
t.Fatalf("should've found \"bar0\" got: %s", i.ISOChecksum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestISOConfigPrepare_ISOChecksumType(t *testing.T) {
|
||||
|
|
|
@ -2,9 +2,10 @@ package none
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type comm struct {
|
||||
|
|
|
@ -46,8 +46,8 @@ type Config struct {
|
|||
// Pty, if true, will request a pty from the remote end.
|
||||
Pty bool
|
||||
|
||||
// DisableAgent, if true, will not forward the SSH agent.
|
||||
DisableAgent bool
|
||||
// DisableAgentForwarding, if true, will not forward the SSH agent.
|
||||
DisableAgentForwarding bool
|
||||
|
||||
// HandshakeTimeout limits the amount of time we'll wait to handshake before
|
||||
// saying the connection failed.
|
||||
|
@ -327,7 +327,7 @@ func (c *comm) connectToAgent() {
|
|||
return
|
||||
}
|
||||
|
||||
if c.config.DisableAgent {
|
||||
if c.config.DisableAgentForwarding {
|
||||
log.Printf("[INFO] SSH agent forwarding is disabled.")
|
||||
return
|
||||
}
|
||||
|
@ -693,6 +693,11 @@ func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) er
|
|||
// Otherwise, we have an ExitErorr, meaning we can just read
|
||||
// the exit status
|
||||
log.Printf("non-zero exit status: %d", exitErr.ExitStatus())
|
||||
stdoutB, err := ioutil.ReadAll(stdoutR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("scp output: %s", stdoutB)
|
||||
|
||||
// If we exited with status 127, it means SCP isn't available.
|
||||
// Return a more descriptive error for that.
|
||||
|
|
|
@ -1,66 +1,68 @@
|
|||
#compdef packer
|
||||
|
||||
local -a _packer_cmds
|
||||
_packer_cmds=(
|
||||
_packer () {
|
||||
local -a sub_commands && sub_commands=(
|
||||
'build:Build image(s) from template'
|
||||
'fix:Fixes templates from old versions of packer'
|
||||
'inspect:See components of a template'
|
||||
'push:Push template files to a Packer build service'
|
||||
'validate:Check that a template is valid'
|
||||
'version:Prints the Packer version'
|
||||
)
|
||||
)
|
||||
|
||||
__build() {
|
||||
_arguments \
|
||||
'-debug[Debug mode enabled for builds]' \
|
||||
'-force[Force a build to continue if artifacts exist, deletes existing artifacts]' \
|
||||
'-machine-readable[Machine-readable output]' \
|
||||
'-except=[(foo,bar,baz) Build all builds other than these]' \
|
||||
'-only=[(foo,bar,baz) Only build the given builds by name]' \
|
||||
'-parallel=[(false) Disable parallelization (on by default)]' \
|
||||
'-var[("key=value") Variable for templates, can be used multiple times.]' \
|
||||
'-var-file=[(path) JSON file containing user variables.]'
|
||||
}
|
||||
|
||||
|
||||
__inspect() {
|
||||
_arguments \
|
||||
'-machine-readable[Machine-readable output]'
|
||||
}
|
||||
|
||||
__push() {
|
||||
_arguments \
|
||||
'-name=[(<name>) The destination build in Atlas.]' \
|
||||
'-token=[(<token>) Access token to use to upload.]' \
|
||||
'-var[("key=value") Variable for templates, can be used multiple times.]' \
|
||||
local -a build_arguments && build_arguments=(
|
||||
'-debug[Debug mode enabled for builds]'
|
||||
'-force[Force a build to continue if artifacts exist, deletes existing artifacts]'
|
||||
'-machine-readable[Machine-readable output]'
|
||||
'-except=[(foo,bar,baz) Build all builds other than these]'
|
||||
'-only=[(foo,bar,baz) Only build the given builds by name]'
|
||||
'-parallel=[(false) Disable parallelization (on by default)]'
|
||||
'-var[("key=value") Variable for templates, can be used multiple times.]'
|
||||
'-var-file=[(path) JSON file containing user variables.]'
|
||||
'(-)*:files:_files -g "*.json"'
|
||||
)
|
||||
|
||||
local -a inspect_arguments && inspect_arguments=(
|
||||
'-machine-readable[Machine-readable output]'
|
||||
'(-)*:files:_files -g "*.json"'
|
||||
)
|
||||
|
||||
local -a push_arguments && push_arguments=(
|
||||
'-name=[(<name>) The destination build in Atlas.]'
|
||||
'-token=[(<token>) Access token to use to upload.]'
|
||||
'-var[("key=value") Variable for templates, can be used multiple times.]'
|
||||
'-var-file=[(path) JSON file containing user variables.]'
|
||||
'(-)*:files:_files -g "*.json"'
|
||||
)
|
||||
|
||||
local -a validate_arguments && validate_arguments=(
|
||||
'-syntax-only[Only check syntax. Do not verify config of the template.]'
|
||||
'-except=[(foo,bar,baz) Validate all builds other than these]'
|
||||
'-only=[(foo,bar,baz) Validate only these builds]'
|
||||
'-var[("key=value") Variable for templates, can be used multiple times.]'
|
||||
'-var-file=[(path) JSON file containing user variables.]'
|
||||
'(-)*:files:_files -g "*.json"'
|
||||
)
|
||||
|
||||
_arguments -C \
|
||||
':command:->command' \
|
||||
'*::options:->options'
|
||||
|
||||
case $state in
|
||||
command)
|
||||
_describe -t commands 'command' sub_commands ;;
|
||||
options)
|
||||
case $line[1] in
|
||||
build)
|
||||
_arguments -s -S : $build_arguments ;;
|
||||
inspect)
|
||||
_arguments -s -S : $inspect_arguments ;;
|
||||
push)
|
||||
_arguments -s -S : $push_arguments ;;
|
||||
validate)
|
||||
_arguments -s -S : $validate_arguments ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
__validate() {
|
||||
_arguments \
|
||||
'-syntax-only[Only check syntax. Do not verify config of the template.]' \
|
||||
'-except=[(foo,bar,baz) Validate all builds other than these]' \
|
||||
'-only=[(foo,bar,baz) Validate only these builds]' \
|
||||
'-var[("key=value") Variable for templates, can be used multiple times.]' \
|
||||
'-var-file=[(path) JSON file containing user variables.]'
|
||||
}
|
||||
|
||||
|
||||
_arguments '*:: :->command'
|
||||
|
||||
if (( CURRENT == 1 )); then
|
||||
_describe -t commands "packer command" _packer_cmds
|
||||
return
|
||||
fi
|
||||
|
||||
local -a _command_args
|
||||
case "$words[1]" in
|
||||
build)
|
||||
__build ;;
|
||||
inspect)
|
||||
__inspect ;;
|
||||
push)
|
||||
__push ;;
|
||||
validate)
|
||||
__validate ;;
|
||||
esac
|
||||
_packer "$@"
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
"access_key":"{{user `access_key`}}",
|
||||
"secret_key":"{{user `secret_key`}}",
|
||||
"region":"cn-beijing",
|
||||
"image_name":"packer_basi",
|
||||
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
|
||||
"image_name":"packer_basic",
|
||||
"source_image":"centos_7_2_64_40G_base_20170222.vhd",
|
||||
"ssh_username":"root",
|
||||
"instance_type":"ecs.n1.tiny",
|
||||
"io_optimized":"true"
|
||||
|
@ -18,7 +18,7 @@
|
|||
"type": "shell",
|
||||
"inline": [
|
||||
"sleep 30",
|
||||
"apt-get update -yy"
|
||||
"yum install redis.x86_64 -y"
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"secret_key":"{{user `secret_key`}}",
|
||||
"region":"cn-beijing",
|
||||
"image_name":"packer_test",
|
||||
"source_image":"win2012_64_datactr_r2_en_40G_alibase_20160622.vhd",
|
||||
"source_image":"win2008r2_64_ent_sp1_zh-cn_40G_alibase_20170622.vhd",
|
||||
"instance_type":"ecs.n1.tiny",
|
||||
"io_optimized":"true",
|
||||
"image_force_delete":"true",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"secret_key":"{{user `secret_key`}}",
|
||||
"region":"cn-beijing",
|
||||
"image_name":"packer_with_data_disk",
|
||||
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
|
||||
"source_image":"centos_7_2_64_40G_base_20170222.vhd",
|
||||
"ssh_username":"root",
|
||||
"instance_type":"ecs.n1.tiny",
|
||||
"io_optimized":"true",
|
||||
|
@ -19,7 +19,7 @@
|
|||
"type": "shell",
|
||||
"inline": [
|
||||
"sleep 30",
|
||||
"apt-get update -yy"
|
||||
"yum install redis.x86_64 -y"
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"secret_key":"{{user `secret_key`}}",
|
||||
"region":"cn-beijing",
|
||||
"image_name":"packer_chef2",
|
||||
"source_image":"ubuntu_14_0405_64_40G_base_20170222.vhd",
|
||||
"source_image":"ubuntu_14_0405_64_40G_alibase_20170625.vhd",
|
||||
"ssh_username":"root",
|
||||
"instance_type":"ecs.n1.medium",
|
||||
"io_optimized":"true",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/sh
|
||||
#if the related deb pkg not found, please replace with it other avaiable repository url
|
||||
HOSTNAME=`ifconfig eth1|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1`
|
||||
if [ not $HOSTNAME ] ; then
|
||||
HOSTNAME=`ifconfig eth0|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1`
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"variables": {
|
||||
"access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
|
||||
"secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
|
||||
},
|
||||
"builders": [{
|
||||
"type":"alicloud-ecs",
|
||||
"access_key":"{{user `access_key`}}",
|
||||
"secret_key":"{{user `secret_key`}}",
|
||||
"region":"cn-beijing",
|
||||
"image_name":"packer_jenkins",
|
||||
"source_image":"ubuntu_14_0405_64_40G_alibase_20170625.vhd",
|
||||
"ssh_username":"root",
|
||||
"instance_type":"ecs.n1.medium",
|
||||
"io_optimized":"true",
|
||||
"image_force_delete":"true",
|
||||
"ssh_password":"Test12345"
|
||||
}],
|
||||
"provisioners": [{
|
||||
"type": "file",
|
||||
"source": "examples/alicloud/jenkins/jenkins.sh",
|
||||
"destination": "/root/"
|
||||
},{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"cd /root/",
|
||||
"chmod 755 jenkins.sh",
|
||||
"./jenkins.sh"
|
||||
]
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#!/bin/sh
|
||||
|
||||
JENKINS_URL='http://mirrors.jenkins.io/war-stable/2.32.2/jenkins.war'
|
||||
|
||||
TOMCAT_VERSION='7.0.77'
|
||||
TOMCAT_NAME="apache-tomcat-$TOMCAT_VERSION"
|
||||
TOMCAT_PACKAGE="$TOMCAT_NAME.tar.gz"
|
||||
TOMCAT_URL="http://mirror.bit.edu.cn/apache/tomcat/tomcat-7/v$TOMCAT_VERSION/bin/$TOMCAT_PACKAGE"
|
||||
TOMCAT_PATH="/opt/$TOMCAT_NAME"
|
||||
|
||||
#install jdk
|
||||
if grep -Eqi "Ubuntu|Debian|Raspbian" /etc/issue || grep -Eq "Ubuntu|Debian|Raspbian" /etc/*-release; then
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y openjdk-7-jdk
|
||||
elif grep -Eqi "CentOS|Fedora|Red Hat Enterprise Linux Server" /etc/issue || grep -Eq "CentOS|Fedora|Red Hat Enterprise Linux Server" /etc/*-release; then
|
||||
sudo yum update -y
|
||||
sudo yum install -y openjdk-7-jdk
|
||||
else
|
||||
echo "Unknown OS type."
|
||||
fi
|
||||
|
||||
#install jenkins server
|
||||
mkdir ~/work
|
||||
cd ~/work
|
||||
|
||||
#install tomcat
|
||||
wget $TOMCAT_URL
|
||||
tar -zxvf $TOMCAT_PACKAGE
|
||||
mv $TOMCAT_NAME /opt
|
||||
|
||||
#install
|
||||
wget $JENKINS_URL
|
||||
mv jenkins.war $TOMCAT_PATH/webapps/
|
||||
|
||||
#set emvironment
|
||||
echo "TOMCAT_PATH=\"$TOMCAT_PATH\"">>/etc/profile
|
||||
echo "JENKINS_HOME=\"$TOMCAT_PATH/webapps/jenkins\"">>/etc/profile
|
||||
echo PATH="\"\$PATH:\$TOMCAT_PATH:\$JENKINS_HOME\"">>/etc/profile
|
||||
. /etc/profile
|
||||
|
||||
#start tomcat & jenkins
|
||||
$TOMCAT_PATH/bin/startup.sh
|
||||
|
||||
#set start on boot
|
||||
sed -i "/#!\/bin\/sh/a$TOMCAT_PATH/bin/startup.sh" /etc/rc.local
|
||||
|
||||
#clean
|
||||
rm -rf ~/work
|
|
@ -0,0 +1,60 @@
|
|||
{"variables": {
|
||||
"box_basename": "centos-6.8",
|
||||
"build_timestamp": "{{isotime \"20060102150405\"}}",
|
||||
"cpus": "1",
|
||||
"disk_size": "4096",
|
||||
"git_revision": "__unknown_git_revision__",
|
||||
"headless": "",
|
||||
"http_proxy": "{{env `http_proxy`}}",
|
||||
"https_proxy": "{{env `https_proxy`}}",
|
||||
"iso_checksum": "0ca12fe5f28c2ceed4f4084b41ff8a0b",
|
||||
"iso_checksum_type": "md5",
|
||||
"iso_name": "CentOS-6.8-x86_64-minimal.iso",
|
||||
"ks_path": "centos-6.8/ks.cfg",
|
||||
"memory": "512",
|
||||
"metadata": "floppy/dummy_metadata.json",
|
||||
"mirror": "http://mirrors.aliyun.com/centos",
|
||||
"mirror_directory": "6.8/isos/x86_64",
|
||||
"name": "centos-6.8",
|
||||
"no_proxy": "{{env `no_proxy`}}",
|
||||
"template": "centos-6.8-x86_64",
|
||||
"version": "2.1.TIMESTAMP"
|
||||
},
|
||||
"builders":[
|
||||
{
|
||||
"boot_command": [
|
||||
"<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/{{user `ks_path`}}<enter><wait>"
|
||||
],
|
||||
"boot_wait": "10s",
|
||||
"disk_size": "{{user `disk_size`}}",
|
||||
"headless": "{{ user `headless` }}",
|
||||
"http_directory": "http",
|
||||
"iso_checksum": "{{user `iso_checksum`}}",
|
||||
"iso_checksum_type": "{{user `iso_checksum_type`}}",
|
||||
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}",
|
||||
"output_directory": "packer-{{user `template`}}-qemu",
|
||||
"shutdown_command": "echo 'vagrant'|sudo -S /sbin/halt -h -p",
|
||||
"ssh_password": "vagrant",
|
||||
"ssh_port": 22,
|
||||
"ssh_username": "root",
|
||||
"ssh_wait_timeout": "10000s",
|
||||
"type": "qemu",
|
||||
"vm_name": "{{ user `template` }}.raw",
|
||||
"net_device": "virtio-net",
|
||||
"disk_interface": "virtio",
|
||||
"format": "raw"
|
||||
}
|
||||
],
|
||||
"post-processors":[
|
||||
{
|
||||
"type":"alicloud-import",
|
||||
"oss_bucket_name": "packer",
|
||||
"image_name": "packer_import",
|
||||
"image_os_type": "linux",
|
||||
"image_platform": "CentOS",
|
||||
"image_architecture": "x86_64",
|
||||
"image_system_size": "40",
|
||||
"region":"cn-beijing"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
install
|
||||
cdrom
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto=dhcp
|
||||
rootpw vagrant
|
||||
firewall --disabled
|
||||
selinux --permissive
|
||||
timezone UTC
|
||||
unsupported_hardware
|
||||
bootloader --location=mbr
|
||||
text
|
||||
skipx
|
||||
zerombr
|
||||
clearpart --all --initlabel
|
||||
autopart
|
||||
auth --enableshadow --passalgo=sha512 --kickstart
|
||||
firstboot --disabled
|
||||
reboot
|
||||
user --name=vagrant --plaintext --password vagrant
|
||||
key --skip
|
||||
|
||||
%packages --nobase --ignoremissing --excludedocs
|
||||
# vagrant needs this to copy initial files via scp
|
||||
openssh-clients
|
||||
sudo
|
||||
kernel-headers
|
||||
kernel-devel
|
||||
gcc
|
||||
make
|
||||
perl
|
||||
wget
|
||||
nfs-utils
|
||||
-fprintd-pam
|
||||
-intltool
|
||||
|
||||
# unnecessary firmware
|
||||
-aic94xx-firmware
|
||||
-atmel-firmware
|
||||
-b43-openfwwf
|
||||
-bfa-firmware
|
||||
-ipw2100-firmware
|
||||
-ipw2200-firmware
|
||||
-ivtv-firmware
|
||||
-iwl100-firmware
|
||||
-iwl1000-firmware
|
||||
-iwl3945-firmware
|
||||
-iwl4965-firmware
|
||||
-iwl5000-firmware
|
||||
-iwl5150-firmware
|
||||
-iwl6000-firmware
|
||||
-iwl6000g2a-firmware
|
||||
-iwl6050-firmware
|
||||
-libertas-usb8388-firmware
|
||||
-ql2100-firmware
|
||||
-ql2200-firmware
|
||||
-ql23xx-firmware
|
||||
-ql2400-firmware
|
||||
-ql2500-firmware
|
||||
-rt61pci-firmware
|
||||
-rt73usb-firmware
|
||||
-xorg-x11-drv-ati-firmware
|
||||
-zd1211-firmware
|
||||
|
||||
%post
|
||||
# Force to set SELinux to a permissive mode
|
||||
sed -i -e 's/\(^SELINUX=\).*$/\1permissive/' /etc/selinux/config
|
||||
# sudo
|
||||
echo "%vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vagrant
|
|
@ -4,7 +4,8 @@
|
|||
"client_secret": "{{env `ARM_CLIENT_SECRET`}}",
|
||||
"resource_group": "{{env `ARM_RESOURCE_GROUP`}}",
|
||||
"storage_account": "{{env `ARM_STORAGE_ACCOUNT`}}",
|
||||
"subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}"
|
||||
"subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
|
||||
"object_id": "{{env `ARM_OJBECT_ID`}}"
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
|
@ -15,6 +16,7 @@
|
|||
"resource_group_name": "{{user `resource_group`}}",
|
||||
"storage_account": "{{user `storage_account`}}",
|
||||
"subscription_id": "{{user `subscription_id`}}",
|
||||
"object_id": "{{user `object_id`}}",
|
||||
|
||||
"capture_container_name": "images",
|
||||
"capture_name_prefix": "packer",
|
||||
|
|
|
@ -29,8 +29,10 @@ func init() {
|
|||
"parallels-headless": new(FixerParallelsHeadless),
|
||||
"parallels-deprecations": new(FixerParallelsDeprecations),
|
||||
"sshkeypath": new(FixerSSHKeyPath),
|
||||
"sshdisableagent": new(FixerSSHDisableAgent),
|
||||
"manifest-filename": new(FixerManifestFilename),
|
||||
"amazon-shutdown_behavior": new(FixerAmazonShutdownBehavior),
|
||||
"enhanced-networking": new(FixerEnhancedNetworking),
|
||||
}
|
||||
|
||||
FixerOrder = []string{
|
||||
|
@ -43,7 +45,9 @@ func init() {
|
|||
"parallels-headless",
|
||||
"parallels-deprecations",
|
||||
"sshkeypath",
|
||||
"sshdisableagent",
|
||||
"manifest-filename",
|
||||
"amazon-shutdown_behavior",
|
||||
"enhanced-networking",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// FixerEnhancedNetworking is a Fixer that replaces the "enhanced_networking" configuration key
|
||||
// with the clearer "ena_support". This disambiguates ena_support from sriov_support.
|
||||
type FixerEnhancedNetworking struct{}
|
||||
|
||||
func (FixerEnhancedNetworking) Fix(input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// Our template type we'll use for this fixer only
|
||||
type template struct {
|
||||
Builders []map[string]interface{}
|
||||
}
|
||||
|
||||
// Decode the input into our structure, if we can
|
||||
var tpl template
|
||||
if err := mapstructure.Decode(input, &tpl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Go through each builder and replace the enhanced_networking if we can
|
||||
for _, builder := range tpl.Builders {
|
||||
enhancedNetworkingRaw, ok := builder["enhanced_networking"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
enhancedNetworkingString, ok := enhancedNetworkingRaw.(bool)
|
||||
if !ok {
|
||||
// TODO: error?
|
||||
continue
|
||||
}
|
||||
|
||||
delete(builder, "enhanced_networking")
|
||||
builder["ena_support"] = enhancedNetworkingString
|
||||
}
|
||||
|
||||
input["builders"] = tpl.Builders
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (FixerEnhancedNetworking) Synopsis() string {
|
||||
return `Replaces "enhanced_networking" in builders with "ena_support"`
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFixerEnhancedNetworking_Impl(t *testing.T) {
|
||||
var _ Fixer = new(FixerEnhancedNetworking)
|
||||
}
|
||||
|
||||
func TestFixerEnhancedNetworking(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input map[string]interface{}
|
||||
Expected map[string]interface{}
|
||||
}{
|
||||
// Attach field == false
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"type": "ebs",
|
||||
"enhanced_networking": false,
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"type": "ebs",
|
||||
"ena_support": false,
|
||||
},
|
||||
},
|
||||
|
||||
// Attach field == true
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"type": "ebs",
|
||||
"enhanced_networking": true,
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"type": "ebs",
|
||||
"ena_support": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
var f FixerEnhancedNetworking
|
||||
|
||||
input := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Input},
|
||||
}
|
||||
|
||||
expected := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Expected},
|
||||
}
|
||||
|
||||
output, err := f.Fix(input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(output, expected) {
|
||||
t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// FixerSSHDisableAgent changes the "ssh_disable_agent" of a template
|
||||
// to "ssh_disable_agent_forwarding".
|
||||
type FixerSSHDisableAgent struct{}
|
||||
|
||||
func (FixerSSHDisableAgent) Fix(input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// The type we'll decode into; we only care about builders
|
||||
type template struct {
|
||||
Builders []map[string]interface{}
|
||||
}
|
||||
|
||||
// Decode the input into our structure, if we can
|
||||
var tpl template
|
||||
if err := mapstructure.Decode(input, &tpl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, builder := range tpl.Builders {
|
||||
sshDisableAgentRaw, ok := builder["ssh_disable_agent"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
sshDisableAgent, ok := sshDisableAgentRaw.(bool)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// only assign to ssh_disable_agent_forwarding if it doesn't
|
||||
// already exist; otherwise we'll just ignore ssh_disable_agent
|
||||
_, sshDisableAgentIncluded := builder["ssh_disable_agent_forwarding"]
|
||||
if !sshDisableAgentIncluded {
|
||||
builder["ssh_disable_agent_forwarding"] = sshDisableAgent
|
||||
}
|
||||
|
||||
delete(builder, "ssh_disable_agent")
|
||||
}
|
||||
|
||||
input["builders"] = tpl.Builders
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (FixerSSHDisableAgent) Synopsis() string {
|
||||
return `Updates builders using "ssh_disable_agent" to use "ssh_disable_agent_forwarding"`
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFixerSSHDisableAgent_Impl(t *testing.T) {
|
||||
var _ Fixer = new(FixerSSHDisableAgent)
|
||||
}
|
||||
|
||||
func TestFixerSSHDisableAgent_Fix(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input map[string]interface{}
|
||||
Expected map[string]interface{}
|
||||
}{
|
||||
// No disable_agent field
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"type": "virtualbox",
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"type": "virtualbox",
|
||||
},
|
||||
},
|
||||
|
||||
// disable_agent_forwarding without disable_agent
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"ssh_disable_agent_forwarding": true,
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"ssh_disable_agent_forwarding": true,
|
||||
},
|
||||
},
|
||||
|
||||
// disable_agent without disable_agent_forwarding
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"ssh_disable_agent": true,
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"ssh_disable_agent_forwarding": true,
|
||||
},
|
||||
},
|
||||
|
||||
// disable_agent and disable_agent_forwarding
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"ssh_disable_agent": true,
|
||||
"ssh_disable_agent_forwarding": false,
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"ssh_disable_agent_forwarding": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
var f FixerSSHDisableAgent
|
||||
|
||||
input := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Input},
|
||||
}
|
||||
|
||||
expected := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Expected},
|
||||
}
|
||||
|
||||
output, err := f.Fix(input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(output, expected) {
|
||||
t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,23 +16,23 @@ type Config struct {
|
|||
Type string `mapstructure:"communicator"`
|
||||
|
||||
// SSH
|
||||
SSHHost string `mapstructure:"ssh_host"`
|
||||
SSHPort int `mapstructure:"ssh_port"`
|
||||
SSHUsername string `mapstructure:"ssh_username"`
|
||||
SSHPassword string `mapstructure:"ssh_password"`
|
||||
SSHPrivateKey string `mapstructure:"ssh_private_key_file"`
|
||||
SSHPty bool `mapstructure:"ssh_pty"`
|
||||
SSHTimeout time.Duration `mapstructure:"ssh_timeout"`
|
||||
SSHAgentAuth bool `mapstructure:"ssh_agent_auth"`
|
||||
SSHDisableAgent bool `mapstructure:"ssh_disable_agent"`
|
||||
SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"`
|
||||
SSHBastionHost string `mapstructure:"ssh_bastion_host"`
|
||||
SSHBastionPort int `mapstructure:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth bool `mapstructure:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername string `mapstructure:"ssh_bastion_username"`
|
||||
SSHBastionPassword string `mapstructure:"ssh_bastion_password"`
|
||||
SSHBastionPrivateKey string `mapstructure:"ssh_bastion_private_key_file"`
|
||||
SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"`
|
||||
SSHHost string `mapstructure:"ssh_host"`
|
||||
SSHPort int `mapstructure:"ssh_port"`
|
||||
SSHUsername string `mapstructure:"ssh_username"`
|
||||
SSHPassword string `mapstructure:"ssh_password"`
|
||||
SSHPrivateKey string `mapstructure:"ssh_private_key_file"`
|
||||
SSHPty bool `mapstructure:"ssh_pty"`
|
||||
SSHTimeout time.Duration `mapstructure:"ssh_timeout"`
|
||||
SSHAgentAuth bool `mapstructure:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding bool `mapstructure:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"`
|
||||
SSHBastionHost string `mapstructure:"ssh_bastion_host"`
|
||||
SSHBastionPort int `mapstructure:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth bool `mapstructure:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername string `mapstructure:"ssh_bastion_username"`
|
||||
SSHBastionPassword string `mapstructure:"ssh_bastion_password"`
|
||||
SSHBastionPrivateKey string `mapstructure:"ssh_bastion_private_key_file"`
|
||||
SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"`
|
||||
|
||||
// WinRM
|
||||
WinRMUser string `mapstructure:"winrm_username"`
|
||||
|
|
|
@ -160,11 +160,11 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan stru
|
|||
|
||||
// Then we attempt to connect via SSH
|
||||
config := &ssh.Config{
|
||||
Connection: connFunc,
|
||||
SSHConfig: sshConfig,
|
||||
Pty: s.Config.SSHPty,
|
||||
DisableAgent: s.Config.SSHDisableAgent,
|
||||
UseSftp: s.Config.SSHFileTransferMethod == "sftp",
|
||||
Connection: connFunc,
|
||||
SSHConfig: sshConfig,
|
||||
Pty: s.Config.SSHPty,
|
||||
DisableAgentForwarding: s.Config.SSHDisableAgentForwarding,
|
||||
UseSftp: s.Config.SSHFileTransferMethod == "sftp",
|
||||
}
|
||||
|
||||
log.Println("[INFO] Attempting SSH connection...")
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/mitchellh/iochan"
|
||||
)
|
||||
|
@ -154,11 +155,11 @@ OutputLoop:
|
|||
// Make sure we finish off stdout/stderr because we may have gotten
|
||||
// a message from the exit channel before finishing these first.
|
||||
for output := range stdoutCh {
|
||||
ui.Message(strings.TrimSpace(output))
|
||||
ui.Message(r.cleanOutputLine(output))
|
||||
}
|
||||
|
||||
for output := range stderrCh {
|
||||
ui.Message(strings.TrimSpace(output))
|
||||
ui.Message(r.cleanOutputLine(output))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -196,7 +197,7 @@ func (r *RemoteCmd) Wait() {
|
|||
// UI output when we're reading from a remote command.
|
||||
func (r *RemoteCmd) cleanOutputLine(line string) string {
|
||||
// Trim surrounding whitespace
|
||||
line = strings.TrimSpace(line)
|
||||
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
|
||||
// Trim up to the first carriage return, since that text would be
|
||||
// lost anyways.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build darwin freebsd linux netbsd openbsd
|
||||
// +build darwin freebsd linux netbsd openbsd solaris
|
||||
|
||||
package packer
|
||||
|
||||
|
|
|
@ -2,12 +2,13 @@ package plugin
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func helperProcess(s ...string) *exec.Cmd {
|
||||
|
@ -48,7 +49,7 @@ func TestHelperProcess(*testing.T) {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
cmd, args := args[0], args[1:]
|
||||
cmd, _ := args[0], args[1:]
|
||||
switch cmd {
|
||||
case "bad-version":
|
||||
fmt.Printf("%s1|tcp|:1234\n", APIVersion)
|
||||
|
|
|
@ -71,10 +71,8 @@ func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) {
|
|||
var wg sync.WaitGroup
|
||||
|
||||
if cmd.Stdin != nil {
|
||||
wg.Add(1)
|
||||
args.StdinStreamId = c.mux.NextId()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
serveSingleCopy("stdin", c.mux, args.StdinStreamId, nil, cmd.Stdin)
|
||||
}()
|
||||
}
|
||||
|
@ -113,8 +111,7 @@ func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) {
|
|||
|
||||
var finished CommandFinished
|
||||
decoder := gob.NewDecoder(conn)
|
||||
err = decoder.Decode(&finished)
|
||||
if err != nil {
|
||||
if err := decoder.Decode(&finished); err != nil {
|
||||
log.Printf("[ERR] Error decoding response stream %d: %s",
|
||||
responseStreamId, err)
|
||||
cmd.SetExited(123)
|
||||
|
@ -132,12 +129,7 @@ func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) {
|
|||
func (c *communicator) Upload(path string, r io.Reader, fi *os.FileInfo) (err error) {
|
||||
// Pipe the reader through to the connection
|
||||
streamId := c.mux.NextId()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
serveSingleCopy("uploadData", c.mux, streamId, nil, r)
|
||||
}()
|
||||
go serveSingleCopy("uploadData", c.mux, streamId, nil, r)
|
||||
|
||||
args := CommunicatorUploadArgs{
|
||||
Path: path,
|
||||
|
@ -149,7 +141,6 @@ func (c *communicator) Upload(path string, r io.Reader, fi *os.FileInfo) (err er
|
|||
}
|
||||
|
||||
err = c.client.Call("Communicator.Upload", &args, new(interface{}))
|
||||
wg.Wait()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue