Merge branch 'master' into circle_ci_windows
This commit is contained in:
commit
aa9cc919ab
|
@ -23,6 +23,7 @@
|
|||
* core: Fix bug where sensitive variables contianing commas were not being
|
||||
properly sanitized in UI calls. [GH-7997]
|
||||
* provisioner/ansible: Fix provisioner dropped errors [GH-8045]
|
||||
* builder/proxmox: Fix panic caused by cancelling build [GH-8067] [GH-8072]
|
||||
|
||||
## 1.4.3 (August 14, 2019)
|
||||
|
||||
|
|
|
@ -23,9 +23,10 @@ type Config struct {
|
|||
awscommon.AccessConfig `mapstructure:",squash"`
|
||||
awscommon.RunConfig `mapstructure:",squash"`
|
||||
|
||||
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
|
||||
AMIENASupport *bool `mapstructure:"ena_support"`
|
||||
AMISriovNetSupport bool `mapstructure:"sriov_support"`
|
||||
AMIENASupport *bool `mapstructure:"ena_support"`
|
||||
AMISriovNetSupport bool `mapstructure:"sriov_support"`
|
||||
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
|
||||
VolumeRunTags awscommon.TagMap `mapstructure:"run_volume_tags"`
|
||||
|
||||
launchBlockDevices awscommon.BlockDevices
|
||||
ctx interpolate.Context
|
||||
|
@ -120,8 +121,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||
BlockDevices: b.config.launchBlockDevices,
|
||||
BlockDurationMinutes: b.config.BlockDurationMinutes,
|
||||
Ctx: b.config.ctx,
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
Ctx: b.config.ctx,
|
||||
Debug: b.config.PackerDebug,
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
ExpectedRootDevice: "ebs",
|
||||
|
@ -129,12 +130,13 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
SourceAMI: b.config.SourceAmi,
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
SpotInstanceTypes: b.config.SpotInstanceTypes,
|
||||
SpotPrice: b.config.SpotPrice,
|
||||
SpotTags: b.config.SpotTags,
|
||||
Tags: b.config.RunTags,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
}
|
||||
} else {
|
||||
instanceStep = &awscommon.StepRunSourceInstance{
|
||||
|
@ -154,6 +156,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
Tags: b.config.RunTags,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
VolumeTags: b.config.VolumeRunTags,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,9 @@ package ebsvolume
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
@ -19,6 +21,7 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
|
|||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
instance := state.Get("instance").(*ec2.Instance)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
volumes := make(EbsVolumes)
|
||||
for _, instanceBlockDevices := range instance.BlockDeviceMappings {
|
||||
|
@ -36,8 +39,50 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
|
|||
state.Put("ebsvolumes", volumes)
|
||||
|
||||
if len(s.VolumeMapping) > 0 {
|
||||
ui.Say("Tagging EBS volumes...")
|
||||
// If run_volume_tags were set in the template any attached EBS
|
||||
// volume will have had these tags applied when the instance was
|
||||
// created. We now need to remove these tags to ensure only the EBS
|
||||
// volume tags are applied (if any)
|
||||
if config.VolumeRunTags.IsSet() {
|
||||
ui.Say("Removing any tags applied to EBS volumes when the source instance was created...")
|
||||
|
||||
ui.Message("Compiling list of existing tags to remove...")
|
||||
existingTags, err := config.VolumeRunTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating list of tags to remove: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
existingTags.Report(ui)
|
||||
|
||||
// Generate the list of volumes with tags to delete.
|
||||
// Looping over the instance block device mappings allows us to
|
||||
// obtain the volumeId
|
||||
volumeIds := []string{}
|
||||
for _, mapping := range s.VolumeMapping {
|
||||
for _, v := range instance.BlockDeviceMappings {
|
||||
if *v.DeviceName == mapping.DeviceName {
|
||||
volumeIds = append(volumeIds, *v.Ebs.VolumeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the tags
|
||||
ui.Message(fmt.Sprintf("Deleting 'run_volume_tags' on EBS Volumes: %s", strings.Join(volumeIds, ", ")))
|
||||
_, err = ec2conn.DeleteTags(&ec2.DeleteTagsInput{
|
||||
Resources: aws.StringSlice(volumeIds),
|
||||
Tags: existingTags,
|
||||
})
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deleting tags on EBS Volumes %s: %s", strings.Join(volumeIds, ", "), err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Tagging EBS volumes...")
|
||||
toTag := map[string][]*ec2.Tag{}
|
||||
for _, mapping := range s.VolumeMapping {
|
||||
if len(mapping.Tags) == 0 {
|
||||
|
@ -45,15 +90,19 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
|
|||
continue
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Compiling list of tags to apply to volume on %s...", mapping.DeviceName))
|
||||
tags, err := mapping.Tags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error tagging device %s with %s", mapping.DeviceName, err)
|
||||
err := fmt.Errorf("Error generating tags for device %s: %s", mapping.DeviceName, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
tags.Report(ui)
|
||||
|
||||
// Generate the map of volumes and associated tags to apply.
|
||||
// Looping over the instance block device mappings allows us to
|
||||
// obtain the volumeId
|
||||
for _, v := range instance.BlockDeviceMappings {
|
||||
if *v.DeviceName == mapping.DeviceName {
|
||||
toTag[*v.Ebs.VolumeId] = tags
|
||||
|
@ -61,9 +110,11 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
|
|||
}
|
||||
}
|
||||
|
||||
// Apply the tags
|
||||
for volumeId, tags := range toTag {
|
||||
ui.Message(fmt.Sprintf("Applying tags to EBS Volume: %s", volumeId))
|
||||
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
|
||||
Resources: []*string{&volumeId},
|
||||
Resources: aws.StringSlice([]string{volumeId}),
|
||||
Tags: tags,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -72,7 +123,6 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
|
|||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,13 +102,10 @@ func (p *proxmoxDriver) SendSpecial(special string, action bootcommand.KeyAction
|
|||
}
|
||||
|
||||
func (p *proxmoxDriver) send(keys string) error {
|
||||
res, err := p.client.MonitorCmd(p.vmRef, "sendkey "+keys)
|
||||
err := p.client.Sendkey(p.vmRef, keys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if data, ok := res["data"].(string); ok && len(data) > 0 {
|
||||
return fmt.Errorf("failed to send keys: %s", data)
|
||||
}
|
||||
|
||||
time.Sleep(p.interval)
|
||||
return nil
|
||||
|
|
|
@ -46,7 +46,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = b.proxmoxClient.Login(b.config.Username, b.config.Password)
|
||||
err = b.proxmoxClient.Login(b.config.Username, b.config.Password, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
c.RawBootKeyInterval = os.Getenv(common.PackerKeyEnv)
|
||||
}
|
||||
if c.RawBootKeyInterval == "" {
|
||||
c.BootKeyInterval = common.PackerKeyDefault
|
||||
c.BootKeyInterval = 5 * time.Millisecond
|
||||
} else {
|
||||
if interval, err := time.ParseDuration(c.RawBootKeyInterval); err == nil {
|
||||
c.BootKeyInterval = interval
|
||||
|
@ -151,6 +151,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
log.Printf("Disk %d cache mode not set, using default 'none'", idx)
|
||||
c.Disks[idx].CacheMode = "none"
|
||||
}
|
||||
// For any storage pool types which aren't in rxStorageTypes in proxmox-api/proxmox/config_qemu.go:651
|
||||
// (currently zfspool and lvm), the format parameter is mandatory. Make sure this is still up to date
|
||||
// when updating the vendored code!
|
||||
if !contains([]string{"zfspool", "lvm"}, c.Disks[idx].StoragePoolType) && c.Disks[idx].DiskFormat == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("disk format must be specified for pool type %q", c.Disks[idx].StoragePoolType)))
|
||||
}
|
||||
}
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
|
||||
|
@ -197,3 +203,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
packer.LogSecretFilter.Set(c.Password)
|
||||
return c, nil, nil
|
||||
}
|
||||
|
||||
func contains(haystack []string, needle string) bool {
|
||||
for _, candidate := range haystack {
|
||||
if candidate == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
|
|||
config := proxmox.ConfigQemu{
|
||||
Name: c.VMName,
|
||||
Agent: agent,
|
||||
Boot: "cdn", // Boot priority, c:CDROM -> d:Disk -> n:Network
|
||||
QemuCpu: "host",
|
||||
Description: "Packer ephemeral build VM",
|
||||
Memory: c.Memory,
|
||||
QemuCores: c.Cores,
|
||||
|
@ -142,7 +144,7 @@ func (s *stepStartVM) Cleanup(state multistep.StateBag) {
|
|||
ui.Say("Stopping VM")
|
||||
_, err := client.StopVm(vmRef)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error stop VM. Please stop and delete it manually: %s", err))
|
||||
ui.Error(fmt.Sprintf("Error stopping VM. Please stop and delete it manually: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ type bootCommandTemplateData struct {
|
|||
}
|
||||
|
||||
type commandTyper interface {
|
||||
MonitorCmd(*proxmox.VmRef, string) (map[string]interface{}, error)
|
||||
Sendkey(*proxmox.VmRef, string) error
|
||||
}
|
||||
|
||||
var _ commandTyper = &proxmox.Client{}
|
||||
|
|
|
@ -13,75 +13,74 @@ import (
|
|||
)
|
||||
|
||||
type commandTyperMock struct {
|
||||
monitorCmd func(*proxmox.VmRef, string) (map[string]interface{}, error)
|
||||
sendkey func(*proxmox.VmRef, string) error
|
||||
}
|
||||
|
||||
func (m commandTyperMock) MonitorCmd(ref *proxmox.VmRef, cmd string) (map[string]interface{}, error) {
|
||||
return m.monitorCmd(ref, cmd)
|
||||
func (m commandTyperMock) Sendkey(ref *proxmox.VmRef, cmd string) error {
|
||||
return m.sendkey(ref, cmd)
|
||||
}
|
||||
|
||||
var _ commandTyper = commandTyperMock{}
|
||||
|
||||
func TestTypeBootCommand(t *testing.T) {
|
||||
cs := []struct {
|
||||
name string
|
||||
builderConfig *Config
|
||||
expectCallMonitorCmd bool
|
||||
monitorCmdErr error
|
||||
monitorCmdRet map[string]interface{}
|
||||
expectedKeysSent string
|
||||
expectedAction multistep.StepAction
|
||||
name string
|
||||
builderConfig *Config
|
||||
expectCallSendkey bool
|
||||
sendkeyErr error
|
||||
expectedKeysSent string
|
||||
expectedAction multistep.StepAction
|
||||
}{
|
||||
{
|
||||
name: "simple boot command is typed",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
expectedKeysSent: "hello",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
name: "simple boot command is typed",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
|
||||
expectCallSendkey: true,
|
||||
expectedKeysSent: "hello",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "interpolated boot command",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello<enter>world"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
expectedKeysSent: "helloretworld",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
name: "interpolated boot command",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello<enter>world"}}},
|
||||
expectCallSendkey: true,
|
||||
expectedKeysSent: "helloretworld",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "merge multiple interpolated boot command",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"Hello World 2.0", "foo!bar@baz"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
expectedKeysSent: "shift-hellospcshift-worldspc2dot0fooshift-1barshift-2baz",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
name: "merge multiple interpolated boot command",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"Hello World 2.0", "foo!bar@baz"}}},
|
||||
expectCallSendkey: true,
|
||||
expectedKeysSent: "shift-hellospcshift-worldspc2dot0fooshift-1barshift-2baz",
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "without boot command monitorcmd should not be called",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{}}},
|
||||
expectCallMonitorCmd: false,
|
||||
expectedAction: multistep.ActionContinue,
|
||||
name: "without boot command sendkey should not be called",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{}}},
|
||||
expectCallSendkey: false,
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
{
|
||||
name: "invalid boot command template function",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"{{ foo }}"}}},
|
||||
expectCallMonitorCmd: false,
|
||||
expectedAction: multistep.ActionHalt,
|
||||
name: "invalid boot command template function",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"{{ foo }}"}}},
|
||||
expectCallSendkey: false,
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
{
|
||||
// When proxmox (or Qemu, really) doesn't recognize the keycode we send, we get no error back, but
|
||||
// a map {"data": "invalid parameter: X"}, where X is the keycode.
|
||||
name: "invalid keys sent to proxmox",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"x"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
monitorCmdRet: map[string]interface{}{"data": "invalid parameter: x"},
|
||||
expectedKeysSent: "x",
|
||||
expectedAction: multistep.ActionHalt,
|
||||
name: "invalid keys sent to proxmox",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"x"}}},
|
||||
expectCallSendkey: true,
|
||||
sendkeyErr: fmt.Errorf("invalid parameter: x"),
|
||||
expectedKeysSent: "x",
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
{
|
||||
name: "error in typing should return halt",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
|
||||
expectCallMonitorCmd: true,
|
||||
monitorCmdErr: fmt.Errorf("some error"),
|
||||
expectedKeysSent: "h",
|
||||
expectedAction: multistep.ActionHalt,
|
||||
name: "error in typing should return halt",
|
||||
builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
|
||||
expectCallSendkey: true,
|
||||
sendkeyErr: fmt.Errorf("some error"),
|
||||
expectedKeysSent: "h",
|
||||
expectedAction: multistep.ActionHalt,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -89,17 +88,14 @@ func TestTypeBootCommand(t *testing.T) {
|
|||
t.Run(c.name, func(t *testing.T) {
|
||||
accumulator := strings.Builder{}
|
||||
typer := commandTyperMock{
|
||||
monitorCmd: func(ref *proxmox.VmRef, cmd string) (map[string]interface{}, error) {
|
||||
if !c.expectCallMonitorCmd {
|
||||
t.Error("Did not expect MonitorCmd to be called")
|
||||
}
|
||||
if !strings.HasPrefix(cmd, "sendkey ") {
|
||||
t.Errorf("Expected all commands to be sendkey, got %s", cmd)
|
||||
sendkey: func(ref *proxmox.VmRef, cmd string) error {
|
||||
if !c.expectCallSendkey {
|
||||
t.Error("Did not expect sendkey to be called")
|
||||
}
|
||||
|
||||
accumulator.WriteString(strings.TrimPrefix(cmd, "sendkey "))
|
||||
accumulator.WriteString(cmd)
|
||||
|
||||
return c.monitorCmdRet, c.monitorCmdErr
|
||||
return c.sendkeyErr
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -35,21 +35,19 @@ param([string]$switchName, [int]$addressIndex)
|
|||
$HostVMAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $switchName
|
||||
if ($HostVMAdapter){
|
||||
$HostNetAdapter = Get-NetAdapter | Where-Object { $_.DeviceId -eq $HostVMAdapter.DeviceId }
|
||||
if ($HostNetAdapter){
|
||||
$HostNetAdapterIfIndex = @()
|
||||
$HostNetAdapterIfIndex += $HostNetAdapter.ifIndex
|
||||
$HostNetAdapterConfiguration = @(get-wmiobject win32_networkadapterconfiguration -filter "IPEnabled = 'TRUE'") | Where-Object { $HostNetAdapterIfIndex.Contains($_.InterfaceIndex) }
|
||||
if ($HostNetAdapterConfiguration){
|
||||
return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($HostNetAdapter){
|
||||
$HostNetAdapterIfIndex = @()
|
||||
$HostNetAdapterIfIndex += $HostNetAdapter.ifIndex
|
||||
$HostNetAdapterConfiguration = @(get-wmiobject win32_networkadapterconfiguration -filter "IPEnabled = 'TRUE'") | Where-Object { $HostNetAdapterIfIndex.Contains($_.InterfaceIndex)
|
||||
if ($HostNetAdapterConfiguration){
|
||||
return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$HostNetAdapterConfiguration=@(Get-NetIPAddress -CimSession $env:computername -AddressFamily IPv4 | Where-Object { ( $_.InterfaceAlias -notmatch 'Loopback' ) -and ( $_.SuffixOrigin -notmatch "Link" )})
|
||||
if ($HostNetAdapterConfiguration) {
|
||||
return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -8,7 +8,7 @@ require (
|
|||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 // indirect
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
|
||||
|
|
2
go.sum
2
go.sum
|
@ -25,6 +25,8 @@ github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591 h
|
|||
github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591/go.mod h1:EHGzQGbwozJBj/4qj3WGrTJ0FqjgOTOxLQ0VNWvPn08=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4 h1:o//09WenT9BNcQypCYfOBfRe5gtLUvUfTPq0xQqPMEI=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60 h1:iEmbIRk4brAP3wevhCr5MGAqxHUbbIDHvE+6D1/7pRA=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e h1:/8wOj52pewmIX/8d5eVO3t7Rr3astkBI/ruyg4WNqRo=
|
||||
|
|
|
@ -31,6 +31,7 @@ type Client struct {
|
|||
ApiUrl string
|
||||
Username string
|
||||
Password string
|
||||
Otp string
|
||||
}
|
||||
|
||||
// VmRef - virtual machine ref parts
|
||||
|
@ -38,6 +39,7 @@ type Client struct {
|
|||
type VmRef struct {
|
||||
vmId int
|
||||
node string
|
||||
pool string
|
||||
vmType string
|
||||
}
|
||||
|
||||
|
@ -46,11 +48,19 @@ func (vmr *VmRef) SetNode(node string) {
|
|||
return
|
||||
}
|
||||
|
||||
func (vmr *VmRef) SetPool(pool string) {
|
||||
vmr.pool = pool
|
||||
}
|
||||
|
||||
func (vmr *VmRef) SetVmType(vmType string) {
|
||||
vmr.vmType = vmType
|
||||
return
|
||||
}
|
||||
|
||||
func (vmr *VmRef) GetVmType() (string) {
|
||||
return vmr.vmType
|
||||
}
|
||||
|
||||
func (vmr *VmRef) VmId() int {
|
||||
return vmr.vmId
|
||||
}
|
||||
|
@ -59,6 +69,10 @@ func (vmr *VmRef) Node() string {
|
|||
return vmr.node
|
||||
}
|
||||
|
||||
func (vmr *VmRef) Pool() string {
|
||||
return vmr.pool
|
||||
}
|
||||
|
||||
func NewVmRef(vmId int) (vmr *VmRef) {
|
||||
vmr = &VmRef{vmId: vmId, node: "", vmType: ""}
|
||||
return
|
||||
|
@ -73,10 +87,11 @@ func NewClient(apiUrl string, hclient *http.Client, tls *tls.Config) (client *Cl
|
|||
return client, err
|
||||
}
|
||||
|
||||
func (c *Client) Login(username string, password string) (err error) {
|
||||
func (c *Client) Login(username string, password string, otp string) (err error) {
|
||||
c.Username = username
|
||||
c.Password = password
|
||||
return c.session.Login(username, password)
|
||||
c.Otp = otp
|
||||
return c.session.Login(username, password, otp)
|
||||
}
|
||||
|
||||
func (c *Client) GetJsonRetryable(url string, data *map[string]interface{}, tries int) error {
|
||||
|
@ -275,10 +290,26 @@ func (c *Client) MonitorCmd(vmr *VmRef, command string) (monitorRes map[string]i
|
|||
reqbody := ParamsToBody(map[string]interface{}{"command": command})
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/monitor", vmr.node, vmr.vmType, vmr.vmId)
|
||||
resp, err := c.session.Post(url, nil, nil, &reqbody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
monitorRes, err = ResponseJSON(resp)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) Sendkey(vmr *VmRef, qmKey string) error {
|
||||
err := c.CheckVmRef(vmr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reqbody := ParamsToBody(map[string]interface{}{"key": qmKey})
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/sendkey", vmr.node, vmr.vmType, vmr.vmId)
|
||||
// No return, even for errors: https://bugzilla.proxmox.com/show_bug.cgi?id=2275
|
||||
_, err = c.session.Put(url, nil, nil, &reqbody)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// WaitForCompletion - poll the API for task completion
|
||||
func (c *Client) WaitForCompletion(taskResponse map[string]interface{}) (waitExitStatus string, err error) {
|
||||
if taskResponse["errors"] != nil {
|
||||
|
@ -416,6 +447,29 @@ func (c *Client) CreateQemuVm(node string, vmParams map[string]interface{}) (exi
|
|||
return
|
||||
}
|
||||
|
||||
func (c *Client) CreateLxcContainer(node string, vmParams map[string]interface{}) (exitStatus string, err error) {
|
||||
reqbody := ParamsToBody(vmParams)
|
||||
url := fmt.Sprintf("/nodes/%s/lxc", node)
|
||||
var resp *http.Response
|
||||
resp, err = c.session.Post(url, nil, nil, &reqbody)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
// This might not work if we never got a body. We'll ignore errors in trying to read,
|
||||
// but extract the body if possible to give any error information back in the exitStatus
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
exitStatus = string(b)
|
||||
return exitStatus, err
|
||||
}
|
||||
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) CloneQemuVm(vmr *VmRef, vmParams map[string]interface{}) (exitStatus string, err error) {
|
||||
reqbody := ParamsToBody(vmParams)
|
||||
url := fmt.Sprintf("/nodes/%s/qemu/%d/clone", vmr.node, vmr.vmId)
|
||||
|
@ -457,6 +511,21 @@ func (c *Client) SetVmConfig(vmr *VmRef, vmParams map[string]interface{}) (exitS
|
|||
return
|
||||
}
|
||||
|
||||
// SetLxcConfig - send config options
|
||||
func (c *Client) SetLxcConfig(vmr *VmRef, vmParams map[string]interface{}) (exitStatus interface{}, err error) {
|
||||
reqbody := ParamsToBody(vmParams)
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/config", vmr.node, vmr.vmType, vmr.vmId)
|
||||
resp, err := c.session.Put(url, nil, nil, &reqbody)
|
||||
if err == nil {
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) ResizeQemuDisk(vmr *VmRef, disk string, moreSizeGB int) (exitStatus interface{}, err error) {
|
||||
// PUT
|
||||
//disk:virtio0
|
||||
|
@ -478,6 +547,23 @@ func (c *Client) ResizeQemuDisk(vmr *VmRef, disk string, moreSizeGB int) (exitSt
|
|||
return
|
||||
}
|
||||
|
||||
func (c *Client) MoveQemuDisk(vmr *VmRef, disk string, storage string) (exitStatus interface{}, err error) {
|
||||
if disk == "" {
|
||||
disk = "virtio0"
|
||||
}
|
||||
reqbody := ParamsToBody(map[string]interface{}{"disk": disk, "storage": storage, "delete": true})
|
||||
url := fmt.Sprintf("/nodes/%s/%s/%d/move_disk", vmr.node, vmr.vmType, vmr.vmId)
|
||||
resp, err := c.session.Post(url, nil, nil, &reqbody)
|
||||
if err == nil {
|
||||
taskResponse, err := ResponseJSON(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exitStatus, err = c.WaitForCompletion(taskResponse)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetNextID - Get next free VMID
|
||||
func (c *Client) GetNextID(currentID int) (nextID int, err error) {
|
||||
var data map[string]interface{}
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
package proxmox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// LXC options for the Proxmox API
|
||||
type configLxc struct {
|
||||
Ostemplate string `json:"ostemplate"`
|
||||
Arch string `json:"arch"`
|
||||
BWLimit int `json:"bwlimit,omitempty"`
|
||||
CMode string `json:"cmode"`
|
||||
Console bool `json:"console"`
|
||||
Cores int `json:"cores,omitempty"`
|
||||
CPULimit int `json:"cpulimit"`
|
||||
CPUUnits int `json:"cpuunits"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Features QemuDevice `json:"features,omitempty"`
|
||||
Force bool `json:"force,omitempty"`
|
||||
Hookscript string `json:"hookscript,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
IgnoreUnpackErrors bool `json:"ignore-unpack-errors,omitempty"`
|
||||
Lock string `json:"lock,omitempty"`
|
||||
Memory int `json:"memory"`
|
||||
Mountpoints QemuDevices `json:"mountpoints,omitempty"`
|
||||
Nameserver string `json:"nameserver,omitempty"`
|
||||
Networks QemuDevices `json:"networks,omitempty"`
|
||||
OnBoot bool `json:"onboot"`
|
||||
OsType string `json:"ostype,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Pool string `json:"pool,omitempty"`
|
||||
Protection bool `json:"protection"`
|
||||
Restore bool `json:"restore,omitempty"`
|
||||
RootFs string `json:"rootfs,omitempty"`
|
||||
SearchDomain string `json:"searchdomain,omitempty"`
|
||||
SSHPublicKeys string `json:"ssh-public-keys,omitempty"`
|
||||
Start bool `json:"start"`
|
||||
Startup string `json:"startup,omitempty"`
|
||||
Storage string `json:"storage"`
|
||||
Swap int `json:"swap"`
|
||||
Template bool `json:"template,omitempty"`
|
||||
Tty int `json:"tty"`
|
||||
Unique bool `json:"unique,omitempty"`
|
||||
Unprivileged bool `json:"unprivileged"`
|
||||
Unused []string `json:"unused,omitempty"`
|
||||
}
|
||||
|
||||
func NewConfigLxc() (configLxc) {
|
||||
return configLxc{
|
||||
Arch: "amd64",
|
||||
CMode: "tty",
|
||||
Console: true,
|
||||
CPULimit: 0,
|
||||
CPUUnits: 1024,
|
||||
Memory: 512,
|
||||
OnBoot: false,
|
||||
Protection: false,
|
||||
Start: false,
|
||||
Storage: "local",
|
||||
Swap: 512,
|
||||
Template: false,
|
||||
Tty: 2,
|
||||
Unprivileged: false,
|
||||
}
|
||||
}
|
||||
|
||||
func NewConfigLxcFromJson(io io.Reader) (config configLxc, err error) {
|
||||
config = NewConfigLxc()
|
||||
err = json.NewDecoder(io).Decode(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return config, err
|
||||
}
|
||||
log.Println(config)
|
||||
return
|
||||
}
|
||||
|
||||
func NewConfigLxcFromApi(vmr *VmRef, client *Client) (config *configLxc, err error) {
|
||||
// prepare json map to receive the information from the api
|
||||
var lxcConfig map[string]interface{}
|
||||
lxcConfig, err = client.GetVmConfig(vmr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// prepare a new lxc config to store and return\
|
||||
// the information from api
|
||||
newConfig := NewConfigLxc()
|
||||
config = &newConfig
|
||||
|
||||
arch := ""
|
||||
if _, isSet := lxcConfig["arch"]; isSet {
|
||||
arch = lxcConfig["arch"].(string)
|
||||
}
|
||||
cmode := ""
|
||||
if _, isSet := lxcConfig["cmode"]; isSet {
|
||||
cmode = lxcConfig["cmode"].(string)
|
||||
}
|
||||
console := true
|
||||
if _, isSet := lxcConfig["console"]; isSet {
|
||||
console = Itob(int(lxcConfig["console"].(float64)))
|
||||
}
|
||||
cores := 1
|
||||
if _, isSet := lxcConfig["cores"]; isSet {
|
||||
cores = int(lxcConfig["cores"].(float64))
|
||||
}
|
||||
cpulimit := 0
|
||||
if _, isSet := lxcConfig["cpulimit"]; isSet {
|
||||
cpulimit, _ = strconv.Atoi(lxcConfig["cpulimit"].(string))
|
||||
}
|
||||
cpuunits := 1024
|
||||
if _, isSet := lxcConfig["cpuunits"]; isSet {
|
||||
cpuunits = int(lxcConfig["cpuunits"].(float64))
|
||||
}
|
||||
description := ""
|
||||
if _, isSet := lxcConfig["description"]; isSet {
|
||||
description = lxcConfig["description"].(string)
|
||||
}
|
||||
|
||||
// add features, if any
|
||||
if features, isSet := lxcConfig["features"]; isSet {
|
||||
featureList := strings.Split(features.(string), ",")
|
||||
|
||||
// create new device map to store features
|
||||
featureMap := QemuDevice{}
|
||||
// add all features to device map
|
||||
featureMap.readDeviceConfig(featureList)
|
||||
// prepare empty feature map
|
||||
if config.Features == nil {
|
||||
config.Features = QemuDevice{}
|
||||
}
|
||||
// and device config to networks
|
||||
if len(featureMap) > 0 {
|
||||
config.Features = featureMap
|
||||
}
|
||||
}
|
||||
|
||||
hookscript := ""
|
||||
if _, isSet := lxcConfig["hookscript"]; isSet {
|
||||
hookscript = lxcConfig["hookscript"].(string)
|
||||
}
|
||||
hostname := ""
|
||||
if _, isSet := lxcConfig["hostname"]; isSet {
|
||||
hostname = lxcConfig["hostname"].(string)
|
||||
}
|
||||
lock := ""
|
||||
if _, isSet := lxcConfig["lock"]; isSet {
|
||||
lock = lxcConfig["lock"].(string)
|
||||
}
|
||||
memory := 512
|
||||
if _, isSet := lxcConfig["memory"]; isSet {
|
||||
memory = int(lxcConfig["memory"].(float64))
|
||||
}
|
||||
|
||||
// add mountpoints
|
||||
mpNames := []string{}
|
||||
|
||||
for k, _ := range lxcConfig {
|
||||
if mpName:= rxMpName.FindStringSubmatch(k); len(mpName) > 0 {
|
||||
mpNames = append(mpNames, mpName[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, mpName := range mpNames {
|
||||
mpConfStr := lxcConfig[mpName]
|
||||
mpConfList := strings.Split(mpConfStr.(string), ",")
|
||||
|
||||
id := rxDeviceID.FindStringSubmatch(mpName)
|
||||
mpID, _ := strconv.Atoi(id[0])
|
||||
// add mp id
|
||||
mpConfMap := QemuDevice{
|
||||
"id": mpID,
|
||||
}
|
||||
// add rest of device config
|
||||
mpConfMap.readDeviceConfig(mpConfList)
|
||||
// prepare empty mountpoint map
|
||||
if config.Mountpoints == nil {
|
||||
config.Mountpoints = QemuDevices{}
|
||||
}
|
||||
// and device config to mountpoints
|
||||
if len(mpConfMap) > 0 {
|
||||
config.Mountpoints[mpID] = mpConfMap
|
||||
}
|
||||
}
|
||||
|
||||
nameserver := ""
|
||||
if _, isSet := lxcConfig["nameserver"]; isSet {
|
||||
nameserver = lxcConfig["nameserver"].(string)
|
||||
}
|
||||
|
||||
// add networks
|
||||
nicNames := []string{}
|
||||
|
||||
for k, _ := range lxcConfig {
|
||||
if nicName := rxNicName.FindStringSubmatch(k); len(nicName) > 0 {
|
||||
nicNames = append(nicNames, nicName[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, nicName := range nicNames {
|
||||
nicConfStr := lxcConfig[nicName]
|
||||
nicConfList := strings.Split(nicConfStr.(string), ",")
|
||||
|
||||
id := rxDeviceID.FindStringSubmatch(nicName)
|
||||
nicID, _ := strconv.Atoi(id[0])
|
||||
// add nic id
|
||||
nicConfMap := QemuDevice{
|
||||
"id": nicID,
|
||||
}
|
||||
// add rest of device config
|
||||
nicConfMap.readDeviceConfig(nicConfList)
|
||||
// prepare empty network map
|
||||
if config.Networks == nil {
|
||||
config.Networks = QemuDevices{}
|
||||
}
|
||||
// and device config to networks
|
||||
if len(nicConfMap) > 0 {
|
||||
config.Networks[nicID] = nicConfMap
|
||||
}
|
||||
}
|
||||
|
||||
onboot := false
|
||||
if _, isSet := lxcConfig["onboot"]; isSet {
|
||||
onboot = Itob(int(lxcConfig["onboot"].(float64)))
|
||||
}
|
||||
ostype := ""
|
||||
if _, isSet := lxcConfig["ostype"]; isSet {
|
||||
ostype = lxcConfig["ostype"].(string)
|
||||
}
|
||||
protection := false
|
||||
if _, isSet := lxcConfig["protection"]; isSet {
|
||||
protection = Itob(int(lxcConfig["protection"].(float64)))
|
||||
}
|
||||
rootfs := ""
|
||||
if _, isSet := lxcConfig["rootfs"]; isSet {
|
||||
rootfs = lxcConfig["rootfs"].(string)
|
||||
}
|
||||
searchdomain := ""
|
||||
if _, isSet := lxcConfig["searchdomain"]; isSet {
|
||||
searchdomain = lxcConfig["searchdomain"].(string)
|
||||
}
|
||||
startup := ""
|
||||
if _, isSet := lxcConfig["startup"]; isSet {
|
||||
startup = lxcConfig["startup"].(string)
|
||||
}
|
||||
swap := 512
|
||||
if _, isSet := lxcConfig["swap"]; isSet {
|
||||
swap = int(lxcConfig["swap"].(float64))
|
||||
}
|
||||
template := false
|
||||
if _, isSet := lxcConfig["template"]; isSet {
|
||||
template = Itob(int(lxcConfig["template"].(float64)))
|
||||
}
|
||||
tty := 2
|
||||
if _, isSet := lxcConfig["tty"]; isSet {
|
||||
tty = int(lxcConfig["tty"].(float64))
|
||||
}
|
||||
unprivileged := false
|
||||
if _, isset := lxcConfig["unprivileged"]; isset {
|
||||
unprivileged = Itob(int(lxcConfig["unprivileged"].(float64)))
|
||||
}
|
||||
var unused []string
|
||||
if _, isset := lxcConfig["unused"]; isset {
|
||||
unused = lxcConfig["unused"].([]string)
|
||||
}
|
||||
|
||||
config.Arch = arch
|
||||
config.CMode = cmode
|
||||
config.Console = console
|
||||
config.Cores = cores
|
||||
config.CPULimit = cpulimit
|
||||
config.CPUUnits = cpuunits
|
||||
config.Description = description
|
||||
config.OnBoot = onboot
|
||||
config.Hookscript = hookscript
|
||||
config.Hostname = hostname
|
||||
config.Lock = lock
|
||||
config.Memory = memory
|
||||
config.Nameserver = nameserver
|
||||
config.OnBoot = onboot
|
||||
config.OsType = ostype
|
||||
config.Protection = protection
|
||||
config.RootFs = rootfs
|
||||
config.SearchDomain = searchdomain
|
||||
config.Startup = startup
|
||||
config.Swap = swap
|
||||
config.Template = template
|
||||
config.Tty = tty
|
||||
config.Unprivileged = unprivileged
|
||||
config.Unused = unused
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// create LXC container using the Proxmox API
|
||||
func (config configLxc) CreateLxc(vmr *VmRef, client *Client) (err error) {
|
||||
vmr.SetVmType("lxc")
|
||||
|
||||
// convert config to map
|
||||
params, _ := json.Marshal(&config)
|
||||
var paramMap map[string]interface{}
|
||||
json.Unmarshal(params, ¶mMap)
|
||||
|
||||
// build list of features
|
||||
// add features as parameter list to lxc parameters
|
||||
// this overwrites the orginal formatting with a
|
||||
// comma separated list of "key=value" pairs
|
||||
featuresParam := QemuDeviceParam{}
|
||||
featuresParam = featuresParam.createDeviceParam(config.Features, nil)
|
||||
paramMap["features"] = strings.Join(featuresParam, ",")
|
||||
|
||||
// build list of mountpoints
|
||||
// this does the same as for the feature list
|
||||
// except that there can be multiple of these mountpoint sets
|
||||
// and each mountpoint set comes with a new id
|
||||
for mpID, mpConfMap := range config.Mountpoints {
|
||||
mpConfParam := QemuDeviceParam{}
|
||||
mpConfParam = mpConfParam.createDeviceParam(mpConfMap, nil)
|
||||
|
||||
// add mp to lxc parameters
|
||||
mpName := fmt.Sprintf("mp%v", mpID)
|
||||
paramMap[mpName] = strings.Join(mpConfParam, ",")
|
||||
}
|
||||
|
||||
// build list of network parameters
|
||||
for nicID, nicConfMap := range config.Networks {
|
||||
nicConfParam := QemuDeviceParam{}
|
||||
nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)
|
||||
|
||||
// add nic to lxc parameters
|
||||
nicName := fmt.Sprintf("net%v", nicID)
|
||||
paramMap[nicName] = strings.Join(nicConfParam, ",")
|
||||
}
|
||||
|
||||
// build list of unused volumes for sake of completenes,
|
||||
// even if it is not recommended to change these volumes manually
|
||||
for volID, vol := range config.Unused {
|
||||
// add volume to lxc parameters
|
||||
volName := fmt.Sprintf("unused%v", volID)
|
||||
paramMap[volName] = vol
|
||||
}
|
||||
|
||||
// now that we concatenated the key value parameter
|
||||
// list for the networks, mountpoints and unused volumes,
|
||||
// remove the original keys, since the Proxmox API does
|
||||
// not know how to handle this key
|
||||
delete(paramMap, "networks")
|
||||
delete(paramMap, "mountpoints")
|
||||
delete(paramMap, "unused")
|
||||
|
||||
// amend vmid
|
||||
paramMap["vmid"] = vmr.vmId
|
||||
|
||||
exitStatus, err := client.CreateLxcContainer(vmr.node, paramMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating LXC container: %v, error status: %s (params: %v)", err, exitStatus, params)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (config configLxc) UpdateConfig(vmr *VmRef, client *Client) (err error) {
|
||||
// convert config to map
|
||||
params, _ := json.Marshal(&config)
|
||||
var paramMap map[string]interface{}
|
||||
json.Unmarshal(params, ¶mMap)
|
||||
|
||||
// build list of features
|
||||
// add features as parameter list to lxc parameters
|
||||
// this overwrites the orginal formatting with a
|
||||
// comma separated list of "key=value" pairs
|
||||
featuresParam := QemuDeviceParam{}
|
||||
featuresParam = featuresParam.createDeviceParam(config.Features, nil)
|
||||
paramMap["features"] = strings.Join(featuresParam, ",")
|
||||
|
||||
// build list of mountpoints
|
||||
// this does the same as for the feature list
|
||||
// except that there can be multiple of these mountpoint sets
|
||||
// and each mountpoint set comes with a new id
|
||||
for mpID, mpConfMap := range config.Mountpoints {
|
||||
mpConfParam := QemuDeviceParam{}
|
||||
mpConfParam = mpConfParam.createDeviceParam(mpConfMap, nil)
|
||||
|
||||
// add mp to lxc parameters
|
||||
mpName := fmt.Sprintf("mp%v", mpID)
|
||||
paramMap[mpName] = strings.Join(mpConfParam, ",")
|
||||
}
|
||||
|
||||
// build list of network parameters
|
||||
for nicID, nicConfMap := range config.Networks {
|
||||
nicConfParam := QemuDeviceParam{}
|
||||
nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)
|
||||
|
||||
// add nic to lxc parameters
|
||||
nicName := fmt.Sprintf("net%v", nicID)
|
||||
paramMap[nicName] = strings.Join(nicConfParam, ",")
|
||||
}
|
||||
|
||||
// build list of unused volumes for sake of completenes,
|
||||
// even if it is not recommended to change these volumes manually
|
||||
for volID, vol := range config.Unused {
|
||||
// add volume to lxc parameters
|
||||
volName := fmt.Sprintf("unused%v", volID)
|
||||
paramMap[volName] = vol
|
||||
}
|
||||
|
||||
// now that we concatenated the key value parameter
|
||||
// list for the networks, mountpoints and unused volumes,
|
||||
// remove the original keys, since the Proxmox API does
|
||||
// not know how to handle this key
|
||||
delete(paramMap, "networks")
|
||||
delete(paramMap, "mountpoints")
|
||||
delete(paramMap, "unused")
|
||||
|
||||
// delete parameters wich are not supported in updated operations
|
||||
delete(paramMap, "pool")
|
||||
delete(paramMap, "storage")
|
||||
delete(paramMap, "password")
|
||||
delete(paramMap, "ostemplate")
|
||||
delete(paramMap, "start")
|
||||
// even though it is listed as a PUT option in the API documentation
|
||||
// we remove it here because "it should not be modified manually";
|
||||
// also, error "500 unable to modify read-only option: 'unprivileged'"
|
||||
delete(paramMap, "unprivileged")
|
||||
|
||||
_, err = client.SetLxcConfig(vmr, paramMap)
|
||||
return err
|
||||
}
|
|
@ -31,10 +31,17 @@ type ConfigQemu struct {
|
|||
QemuOs string `json:"os"`
|
||||
QemuCores int `json:"cores"`
|
||||
QemuSockets int `json:"sockets"`
|
||||
QemuCpu string `json:"cpu"`
|
||||
QemuNuma bool `json:"numa"`
|
||||
Hotplug string `json:"hotplug"`
|
||||
QemuIso string `json:"iso"`
|
||||
FullClone *int `json:"fullclone"`
|
||||
Boot string `json:"boot"`
|
||||
BootDisk string `json:"bootdisk,omitempty"`
|
||||
Scsihw string `json:"scsihw,omitempty"`
|
||||
QemuDisks QemuDevices `json:"disk"`
|
||||
QemuNetworks QemuDevices `json:"network"`
|
||||
QemuSerials QemuDevices `json:"serial,omitempty"`
|
||||
|
||||
// Deprecated single disk.
|
||||
DiskSize float64 `json:"diskGB"`
|
||||
|
@ -50,6 +57,7 @@ type ConfigQemu struct {
|
|||
// cloud-init options
|
||||
CIuser string `json:"ciuser"`
|
||||
CIpassword string `json:"cipassword"`
|
||||
CIcustom string `json:"cicustom"`
|
||||
|
||||
Searchdomain string `json:"searchdomain"`
|
||||
Nameserver string `json:"nameserver"`
|
||||
|
@ -76,10 +84,24 @@ func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
|
|||
"ostype": config.QemuOs,
|
||||
"sockets": config.QemuSockets,
|
||||
"cores": config.QemuCores,
|
||||
"cpu": "host",
|
||||
"cpu": config.QemuCpu,
|
||||
"numa": config.QemuNuma,
|
||||
"hotplug": config.Hotplug,
|
||||
"memory": config.Memory,
|
||||
"boot": config.Boot,
|
||||
"description": config.Description,
|
||||
}
|
||||
if vmr.pool != "" {
|
||||
params["pool"] = vmr.pool
|
||||
}
|
||||
|
||||
if config.BootDisk != "" {
|
||||
params["bootdisk"] = config.BootDisk
|
||||
}
|
||||
|
||||
if config.Scsihw != "" {
|
||||
params["scsihw"] = config.Scsihw
|
||||
}
|
||||
|
||||
// Create disks config.
|
||||
config.CreateQemuDisksParams(vmr.vmId, params, false)
|
||||
|
@ -87,6 +109,9 @@ func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
|
|||
// Create networks config.
|
||||
config.CreateQemuNetworksParams(vmr.vmId, params)
|
||||
|
||||
// Create serial interfaces
|
||||
config.CreateQemuSerialsParams(vmr.vmId, params)
|
||||
|
||||
exitStatus, err := client.CreateQemuVm(vmr.node, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating VM: %v, error status: %s (params: %v)", err, exitStatus, params)
|
||||
|
@ -102,7 +127,8 @@ func (config ConfigQemu) HasCloudInit() bool {
|
|||
config.Nameserver != "" ||
|
||||
config.Sshkeys != "" ||
|
||||
config.Ipconfig0 != "" ||
|
||||
config.Ipconfig1 != ""
|
||||
config.Ipconfig1 != "" ||
|
||||
config.CIcustom != ""
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -136,6 +162,10 @@ func (config ConfigQemu) CloneVm(sourceVmr *VmRef, vmr *VmRef, client *Client) (
|
|||
"storage": storage,
|
||||
"full": fullclone,
|
||||
}
|
||||
if vmr.pool != "" {
|
||||
params["pool"] = vmr.pool
|
||||
}
|
||||
|
||||
_, err = client.CloneQemuVm(sourceVmr, params)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -151,7 +181,19 @@ func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
|
|||
"agent": config.Agent,
|
||||
"sockets": config.QemuSockets,
|
||||
"cores": config.QemuCores,
|
||||
"cpu": config.QemuCpu,
|
||||
"numa": config.QemuNuma,
|
||||
"hotplug": config.Hotplug,
|
||||
"memory": config.Memory,
|
||||
"boot": config.Boot,
|
||||
}
|
||||
|
||||
if config.BootDisk != "" {
|
||||
configParams["bootdisk"] = config.BootDisk
|
||||
}
|
||||
|
||||
if config.Scsihw != "" {
|
||||
configParams["scsihw"] = config.Scsihw
|
||||
}
|
||||
|
||||
// Create disks config.
|
||||
|
@ -160,6 +202,9 @@ func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
|
|||
// Create networks config.
|
||||
config.CreateQemuNetworksParams(vmr.vmId, configParams)
|
||||
|
||||
// Create serial interfaces
|
||||
config.CreateQemuSerialsParams(vmr.vmId, configParams)
|
||||
|
||||
// cloud-init options
|
||||
if config.CIuser != "" {
|
||||
configParams["ciuser"] = config.CIuser
|
||||
|
@ -167,6 +212,9 @@ func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
|
|||
if config.CIpassword != "" {
|
||||
configParams["cipassword"] = config.CIpassword
|
||||
}
|
||||
if config.CIcustom != "" {
|
||||
configParams["cicustom"] = config.CIcustom
|
||||
}
|
||||
if config.Searchdomain != "" {
|
||||
configParams["searchdomain"] = config.Searchdomain
|
||||
}
|
||||
|
@ -202,11 +250,13 @@ func NewConfigQemuFromJson(io io.Reader) (config *ConfigQemu, err error) {
|
|||
}
|
||||
|
||||
var (
|
||||
rxIso = regexp.MustCompile(`(.*?),media`)
|
||||
rxDeviceID = regexp.MustCompile(`\d+`)
|
||||
rxDiskName = regexp.MustCompile(`(virtio|scsi)\d+`)
|
||||
rxDiskType = regexp.MustCompile(`\D+`)
|
||||
rxNicName = regexp.MustCompile(`net\d+`)
|
||||
rxIso = regexp.MustCompile(`(.*?),media`)
|
||||
rxDeviceID = regexp.MustCompile(`\d+`)
|
||||
rxDiskName = regexp.MustCompile(`(virtio|scsi)\d+`)
|
||||
rxDiskType = regexp.MustCompile(`\D+`)
|
||||
rxNicName = regexp.MustCompile(`net\d+`)
|
||||
rxMpName = regexp.MustCompile(`mp\d+`)
|
||||
rxSerialName = regexp.MustCompile(`serial\d+`)
|
||||
)
|
||||
|
||||
func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err error) {
|
||||
|
@ -278,6 +328,32 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
|
|||
if _, isSet := vmConfig["sockets"]; isSet {
|
||||
sockets = vmConfig["sockets"].(float64)
|
||||
}
|
||||
cpu := "host"
|
||||
if _, isSet := vmConfig["cpu"]; isSet {
|
||||
cpu = vmConfig["cpu"].(string)
|
||||
}
|
||||
numa := false
|
||||
if _, isSet := vmConfig["numa"]; isSet {
|
||||
numa = Itob(int(vmConfig["numa"].(float64)))
|
||||
}
|
||||
//Can be network,disk,cpu,memory,usb
|
||||
hotplug := "network,disk,usb"
|
||||
if _, isSet := vmConfig["hotplug"]; isSet {
|
||||
hotplug = vmConfig["hotplug"].(string)
|
||||
}
|
||||
//boot by default from hard disk (c), CD-ROM (d), network (n).
|
||||
boot := "cdn"
|
||||
if _, isSet := vmConfig["boot"]; isSet {
|
||||
boot = vmConfig["boot"].(string)
|
||||
}
|
||||
bootdisk := ""
|
||||
if _, isSet := vmConfig["bootdisk"]; isSet {
|
||||
bootdisk = vmConfig["bootdisk"].(string)
|
||||
}
|
||||
scsihw := "lsi"
|
||||
if _, isSet := vmConfig["scsihw"]; isSet {
|
||||
scsihw = vmConfig["scsihw"].(string)
|
||||
}
|
||||
config = &ConfigQemu{
|
||||
Name: name,
|
||||
Description: strings.TrimSpace(description),
|
||||
|
@ -287,9 +363,16 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
|
|||
Memory: int(memory),
|
||||
QemuCores: int(cores),
|
||||
QemuSockets: int(sockets),
|
||||
QemuCpu: cpu,
|
||||
QemuNuma: numa,
|
||||
Hotplug: hotplug,
|
||||
QemuVlanTag: -1,
|
||||
Boot: boot,
|
||||
BootDisk: bootdisk,
|
||||
Scsihw: scsihw,
|
||||
QemuDisks: QemuDevices{},
|
||||
QemuNetworks: QemuDevices{},
|
||||
QemuSerials: QemuDevices{},
|
||||
}
|
||||
|
||||
if vmConfig["ide2"] != nil {
|
||||
|
@ -303,6 +386,9 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
|
|||
if _, isSet := vmConfig["cipassword"]; isSet {
|
||||
config.CIpassword = vmConfig["cipassword"].(string)
|
||||
}
|
||||
if _, isSet := vmConfig["cicustom"]; isSet {
|
||||
config.CIcustom = vmConfig["cicustom"].(string)
|
||||
}
|
||||
if _, isSet := vmConfig["searchdomain"]; isSet {
|
||||
config.Searchdomain = vmConfig["searchdomain"].(string)
|
||||
}
|
||||
|
@ -388,6 +474,30 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
|
|||
}
|
||||
}
|
||||
|
||||
// Add serials
|
||||
serialNames := []string{}
|
||||
|
||||
for k, _ := range vmConfig {
|
||||
if serialName := rxSerialName.FindStringSubmatch(k); len(serialName) > 0 {
|
||||
serialNames = append(serialNames, serialName[0])
|
||||
}
|
||||
}
|
||||
|
||||
for _, serialName := range serialNames {
|
||||
id := rxDeviceID.FindStringSubmatch(serialName)
|
||||
serialID, _ := strconv.Atoi(id[0])
|
||||
|
||||
serialConfMap := QemuDevice{
|
||||
"id": serialID,
|
||||
"type": vmConfig[serialName],
|
||||
}
|
||||
|
||||
// And device config to serials map.
|
||||
if len(serialConfMap) > 0 {
|
||||
config.QemuSerials[serialID] = serialConfMap
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -620,7 +730,9 @@ func (c ConfigQemu) CreateQemuDisksParams(
|
|||
"storage_type": "lvm", // default old style
|
||||
"cache": "none", // default old value
|
||||
}
|
||||
|
||||
if c.QemuDisks == nil {
|
||||
c.QemuDisks = make(QemuDevices)
|
||||
}
|
||||
c.QemuDisks[0] = deprecatedStyleMap
|
||||
}
|
||||
|
||||
|
@ -646,9 +758,9 @@ func (c ConfigQemu) CreateQemuDisksParams(
|
|||
|
||||
// Disk name.
|
||||
var diskFile string
|
||||
// Currently ZFS local, LVM, and Directory are considered.
|
||||
// Currently ZFS local, LVM, Ceph RBD, and Directory are considered.
|
||||
// Other formats are not verified, but could be added if they're needed.
|
||||
rxStorageTypes := `(zfspool|lvm)`
|
||||
rxStorageTypes := `(zfspool|lvm|rbd)`
|
||||
storageType := diskConfMap["storage_type"].(string)
|
||||
if matched, _ := regexp.MatchString(rxStorageTypes, storageType); matched {
|
||||
diskFile = fmt.Sprintf("file=%v:vm-%v-disk-%v", diskConfMap["storage"], vmID, diskID)
|
||||
|
@ -716,3 +828,22 @@ func (c ConfigQemu) String() string {
|
|||
jsConf, _ := json.Marshal(c)
|
||||
return string(jsConf)
|
||||
}
|
||||
|
||||
// Create parameters for serial interface
|
||||
func (c ConfigQemu) CreateQemuSerialsParams(
|
||||
vmID int,
|
||||
params map[string]interface{},
|
||||
) error {
|
||||
|
||||
// For new style with multi disk device.
|
||||
for serialID, serialConfMap := range c.QemuSerials {
|
||||
// Device name.
|
||||
deviceType := serialConfMap["type"].(string)
|
||||
qemuSerialName := "serial" + strconv.Itoa(serialID)
|
||||
|
||||
// Add back to Qemu prams.
|
||||
params[qemuSerialName] = deviceType
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -106,8 +106,12 @@ func TypedResponse(resp *http.Response, v interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) Login(username string, password string) (err error) {
|
||||
reqbody := ParamsToBody(map[string]interface{}{"username": username, "password": password})
|
||||
func (s *Session) Login(username string, password string, otp string) (err error) {
|
||||
reqUser := map[string]interface{}{"username": username, "password": password}
|
||||
if otp != "" {
|
||||
reqUser["otp"] = otp
|
||||
}
|
||||
reqbody := ParamsToBody(reqUser)
|
||||
olddebug := *Debug
|
||||
*Debug = false // don't share passwords in debug log
|
||||
resp, err := s.Post("/access/ticket", nil, nil, &reqbody)
|
||||
|
@ -127,6 +131,10 @@ func (s *Session) Login(username string, password string) (err error) {
|
|||
return fmt.Errorf("Invalid login response:\n-----\n%s\n-----", dr)
|
||||
}
|
||||
dat := jbody["data"].(map[string]interface{})
|
||||
//Check if the 2FA was required
|
||||
if dat["NeedTFA"] == 1.0 {
|
||||
return errors.New("Missing TFA code")
|
||||
}
|
||||
s.AuthTicket = dat["ticket"].(string)
|
||||
s.CsrfToken = dat["CSRFPreventionToken"].(string)
|
||||
return nil
|
||||
|
|
|
@ -50,7 +50,7 @@ github.com/NaverCloudPlatform/ncloud-sdk-go/common
|
|||
github.com/NaverCloudPlatform/ncloud-sdk-go/oauth
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go/request
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go/sdk
|
||||
# github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4
|
||||
# github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60
|
||||
github.com/Telmate/proxmox-api-go/proxmox
|
||||
# github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e
|
||||
github.com/aliyun/alibaba-cloud-sdk-go/sdk
|
||||
|
|
|
@ -16,16 +16,22 @@ power of Packer templates.
|
|||
|
||||
## Third-Party plugins
|
||||
|
||||
This is an incomplete list. If you have written your own plugin, please make a
|
||||
pull request to the website so that we can document your contribution here!
|
||||
The plugins listed below have been built by the community of Packer users and
|
||||
vendors. These plugins are not officially tested nor officially maintained by
|
||||
HashiCorp, and are listed here in order to help users find them easily.
|
||||
|
||||
To learn more about how to use community plugins, or how to build your own,
|
||||
check out the docs on [extending Packer](/docs/extending/plugins.html)
|
||||
|
||||
If you have built a plugin and would like to add it to this community list,
|
||||
please make a pull request to the website so that we can document your
|
||||
contribution here!
|
||||
|
||||
### Community Builders
|
||||
|
||||
- [ARM builder](https://github.com/solo-io/packer-builder-arm-image) - A builder
|
||||
for creating ARM images
|
||||
|
||||
- [Comment Provisioner](https://github.com/SwampDragons/packer-provisioner-comment) -
|
||||
Example provisioner that allows you to annotate your build with bubble-text
|
||||
comments.
|
||||
|
||||
- [vSphere builder](https://github.com/jetbrains-infra/packer-builder-vsphere) -
|
||||
A builder for interacting directly with the vSphere API rather than the esx
|
||||
host directly.
|
||||
|
@ -33,6 +39,13 @@ pull request to the website so that we can document your contribution here!
|
|||
- [Vultr builder](https://github.com/vultr/packer-builder-vultr) - A builder
|
||||
for creating [Vultr](https://www.vultr.com/) snapshots.
|
||||
|
||||
|
||||
### Community Provisioners
|
||||
|
||||
- [Comment Provisioner](https://github.com/SwampDragons/packer-provisioner-comment) -
|
||||
Example provisioner that allows you to annotate your build with bubble-text
|
||||
comments.
|
||||
|
||||
- [Windows Update provisioner](https://github.com/rgl/packer-provisioner-windows-update) -
|
||||
A provisioner for gracefully handling windows updates and the reboots they
|
||||
cause.
|
||||
|
|
|
@ -22,6 +22,10 @@ description: |-
|
|||
<strong>Discussion list:</strong>
|
||||
<a href="https://groups.google.com/group/packer-tool">Packer Google Group</a>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Community Forum:</strong>
|
||||
<a href="https://discuss.hashicorp.com/c/packer">Packer Community Forum</a>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Bug Tracker:</strong>
|
||||
<a href="https://github.com/hashicorp/packer/issues">Issue tracker
|
||||
|
|
|
@ -38,6 +38,11 @@ builder.
|
|||
- `access_key` (string) - The access key used to communicate with AWS. [Learn
|
||||
how to set this](/docs/builders/amazon.html#specifying-amazon-credentials)
|
||||
|
||||
- `ami_name` (string) - The name of the resulting AMI that will appear when
|
||||
managing AMIs in the AWS console or via APIs. This must be unique. To help
|
||||
make this unique, use a function like `timestamp` (see [template
|
||||
engine](../templates/engine.html) for more info).
|
||||
|
||||
- `instance_type` (string) - The EC2 instance type to use while building the
|
||||
AMI, such as `m1.small`.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
description: |
|
||||
The amazon-ebsvolume Packer builder is like the EBS builder, but is intended to
|
||||
create EBS volumes rather than a machine image.
|
||||
The amazon-ebsvolume Packer builder is like the EBS builder, but is
|
||||
intended to create EBS volumes rather than a machine image.
|
||||
layout: docs
|
||||
page_title: 'Amazon EBS Volume - Builders'
|
||||
sidebar_current: 'docs-builders-amazon-ebsvolume'
|
||||
|
@ -14,9 +14,11 @@ Type: `amazon-ebsvolume`
|
|||
The `amazon-ebsvolume` Packer builder is able to create Amazon Elastic Block
|
||||
Store volumes which are prepopulated with filesystems or data.
|
||||
|
||||
This builder builds EBS volumes by launching an EC2 instance from a source AMI,
|
||||
provisioning that running machine, and then destroying the source machine,
|
||||
keeping the volumes intact.
|
||||
This builder creates EBS volumes by launching an EC2 instance from a source
|
||||
AMI. One or more EBS volumes are attached to the running instance, allowing
|
||||
them to be provisioned into from the running machine. Once provisioning is
|
||||
complete the source machine is destroyed. The provisioned volumes are kept
|
||||
intact.
|
||||
|
||||
This is all done in your own AWS account. The builder will create temporary key
|
||||
pairs, security group rules, etc. that provide it temporary access to the
|
||||
|
@ -44,11 +46,11 @@ builder.
|
|||
- `access_key` (string) - The access key used to communicate with AWS. [Learn
|
||||
how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
|
||||
|
||||
- `instance_type` (string) - The EC2 instance type to use while building the
|
||||
AMI, such as `m1.small`.
|
||||
- `instance_type` (string) - The EC2 instance type to use while creating
|
||||
the EBS volumes, such as `m1.small`.
|
||||
|
||||
- `region` (string) - The name of the region, such as `us-east-1`, in which
|
||||
to launch the EC2 instance to create the AMI.
|
||||
to launch the EC2 instance to create the new EBS volumes.
|
||||
|
||||
- `secret_key` (string) - The secret key used to communicate with AWS. [Learn
|
||||
how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
|
||||
|
@ -60,7 +62,7 @@ builder.
|
|||
### Optional:
|
||||
|
||||
- `ebs_volumes` (array of block device mappings) - Add the block device
|
||||
mappings to the AMI. The block device mappings allow for keys:
|
||||
mappings to the running instance. The block device mappings allow for keys:
|
||||
|
||||
- `device_name` (string) - The device name exposed to the instance (for
|
||||
example, `/dev/sdh` or `xvdh`). Required for every device in the block
|
||||
|
@ -82,33 +84,39 @@ builder.
|
|||
- `iops` (number) - The number of I/O operations per second (IOPS) that
|
||||
the volume supports. See the documentation on
|
||||
[IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||
for more information
|
||||
for more information.
|
||||
|
||||
- `no_device` (boolean) - Suppresses the specified device included in the
|
||||
block device mapping of the AMI
|
||||
block device mapping of the AMI.
|
||||
|
||||
- `snapshot_id` (string) - The ID of the snapshot
|
||||
- `snapshot_id` (string) - The ID of the snapshot.
|
||||
|
||||
- `virtual_name` (string) - The virtual device name. See the
|
||||
documentation on [Block Device
|
||||
Mapping](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
|
||||
for more information
|
||||
for more information.
|
||||
|
||||
- `volume_size` (number) - The size of the volume, in GiB. Required if
|
||||
not specifying a `snapshot_id`
|
||||
not specifying a `snapshot_id`.
|
||||
|
||||
- `volume_type` (string) - The volume type. `gp2` for General Purpose
|
||||
(SSD) volumes, `io1` for Provisioned IOPS (SSD) volumes, and `standard`
|
||||
for Magnetic volumes
|
||||
for Magnetic volumes.
|
||||
|
||||
- `tags` (map) - Tags to apply to the volume. These are retained after
|
||||
the builder completes. This is a [template
|
||||
engine](/docs/templates/engine.html), see [Build template
|
||||
data](#build-template-data) for more information.
|
||||
|
||||
Note: The tags specified here may be *temporarily* overridden by
|
||||
those specified in `run_volume_tags` - but only while the instance
|
||||
is being created. Packer will replace all tags on the volume with
|
||||
those configured here as soon as the instance is reported as
|
||||
'ready'.
|
||||
|
||||
- `associate_public_ip_address` (boolean) - If using a non-default VPC,
|
||||
public IP addresses are not provided by default. If this is `true`, your
|
||||
new instance will get a Public IP. default: `false`
|
||||
new instance will get a Public IP. Default: `false`.
|
||||
|
||||
- `availability_zone` (string) - Destination availability zone to launch
|
||||
instance in. Leave this empty to allow Amazon to auto-assign.
|
||||
|
@ -158,11 +166,9 @@ builder.
|
|||
|
||||
- `ena_support` (boolean) - Enable enhanced networking (ENA but not
|
||||
SriovNetSupport) on HVM-compatible AMIs. If set, add
|
||||
`ec2:ModifyInstanceAttribute` to your AWS IAM policy. If false, this will
|
||||
disable enhanced networking in the final AMI as opposed to passing the
|
||||
setting through unchanged from the source. Note: you must make sure
|
||||
enhanced networking is enabled on your instance. See [Amazon's
|
||||
documentation on enabling enhanced
|
||||
`ec2:ModifyInstanceAttribute` to your AWS IAM policy. Note: you must
|
||||
make sure enhanced networking is enabled on your instance. See
|
||||
[Amazon's documentation on enabling enhanced
|
||||
networking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking.html#enabling_enhanced_networking).
|
||||
|
||||
- `enable_t2_unlimited` (boolean) - Enabling T2 Unlimited allows the source
|
||||
|
@ -206,11 +212,23 @@ builder.
|
|||
for more details.
|
||||
|
||||
- `run_tags` (object of key/value strings) - Tags to apply to the instance
|
||||
that is *launched* to create the AMI. These tags are *not* applied to the
|
||||
resulting AMI unless they're duplicated in `tags`. This is a [template
|
||||
that is *launched* to create the EBS volumes. This is a [template
|
||||
engine](/docs/templates/engine.html), see [Build template
|
||||
data](#build-template-data) for more information.
|
||||
|
||||
- `run_volume_tags` (object of key/value strings) - Tags to apply to the
|
||||
volumes of the instance that is *launched* to create EBS Volumes. These
|
||||
tags will *not* appear in the tags of the resulting EBS volumes unless
|
||||
they're duplicated under `tags` in the `ebs_volumes` setting. This is a
|
||||
[template engine](/docs/templates/engine.html), see [Build template
|
||||
data](#build-template-data) for more information.
|
||||
|
||||
Note: The tags specified here will be *temporarily* applied to volumes
|
||||
specified in `ebs_volumes` - but only while the instance is being
|
||||
created. Packer will replace all tags on the volume with the tags
|
||||
configured in the `ebs_volumes` section as soon as the instance is
|
||||
reported as 'ready'.
|
||||
|
||||
- `security_group_id` (string) - The ID (*not* the name) of the security
|
||||
group to assign to the instance. By default this is not set and Packer will
|
||||
automatically create a new temporary security group to allow SSH access.
|
||||
|
@ -250,16 +268,6 @@ builder.
|
|||
- `skip_region_validation` (boolean) - Set to `true` if you want to skip
|
||||
validation of the region configuration option. Defaults to `false`.
|
||||
|
||||
- `snapshot_groups` (array of strings) - A list of groups that have access to
|
||||
create volumes from the snapshot(s). By default no groups have permission
|
||||
to create volumes from the snapshot(s). `all` will make the snapshot
|
||||
publicly accessible.
|
||||
|
||||
- `snapshot_users` (array of strings) - A list of account IDs that have
|
||||
access to create volumes from the snapshot(s). By default no additional
|
||||
users other than the user creating the AMI has permissions to create
|
||||
volumes from the backing snapshot(s).
|
||||
|
||||
- `source_ami_filter` (object) - Filters used to populate the `source_ami`
|
||||
field. Example:
|
||||
|
||||
|
@ -376,9 +384,12 @@ builder.
|
|||
`packer_<UUID>`, where <UUID> is a 36 character unique identifier.
|
||||
|
||||
- `temporary_security_group_source_cidrs` (list of string) - A list of IPv4
|
||||
CIDR blocks to be authorized access to the instance, when packer is creating a temporary security group.
|
||||
CIDR blocks to be authorized access to the instance, when packer is
|
||||
creating a temporary security group.
|
||||
|
||||
The default is [`0.0.0.0/0`] (i.e., allow any IPv4 source). This is only used when `security_group_id` or `security_group_ids` is not specified.
|
||||
The default is [`0.0.0.0/0`] (i.e., allow any IPv4 source). This is
|
||||
only used when `security_group_id` or `security_group_ids` is not
|
||||
specified.
|
||||
|
||||
- `token` (string) - The access token to use. This is different from the
|
||||
access key and secret key. If you're not sure what this is, then you
|
||||
|
|
|
@ -14,6 +14,8 @@ interpolations. You may access variables in the Packer config you called the
|
|||
console with, or provide variables when you call console using the -var or
|
||||
-var-file command line options.
|
||||
|
||||
~> **Note:** `console` is available from version 1.4.2 and above.
|
||||
|
||||
Type in the interpolation to test and hit \<enter\> to see the result.
|
||||
|
||||
To exit the console, type "exit" and hit \<enter\>, or use Control-C.
|
||||
|
|
Loading…
Reference in New Issue