diff --git a/builder/proxmox/artifact.go b/builder/proxmox/artifact.go deleted file mode 100644 index 99d8e1a20..000000000 --- a/builder/proxmox/artifact.go +++ /dev/null @@ -1,44 +0,0 @@ -package proxmox - -import ( - "fmt" - "log" - "strconv" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/packer" -) - -type Artifact struct { - templateID int - proxmoxClient *proxmox.Client -} - -// Artifact implements packer.Artifact -var _ packer.Artifact = &Artifact{} - -func (*Artifact) BuilderId() string { - return BuilderId -} - -func (*Artifact) Files() []string { - return nil -} - -func (a *Artifact) Id() string { - return strconv.Itoa(a.templateID) -} - -func (a *Artifact) String() string { - return fmt.Sprintf("A template was created: %d", a.templateID) -} - -func (a *Artifact) State(name string) interface{} { - return nil -} - -func (a *Artifact) Destroy() error { - log.Printf("Destroying template: %d", a.templateID) - _, err := a.proxmoxClient.DeleteVm(proxmox.NewVmRef(a.templateID)) - return err -} diff --git a/builder/proxmox/bootcommand_driver.go b/builder/proxmox/bootcommand_driver.go deleted file mode 100644 index 1570845fe..000000000 --- a/builder/proxmox/bootcommand_driver.go +++ /dev/null @@ -1,117 +0,0 @@ -package proxmox - -import ( - "fmt" - "time" - "unicode" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/common/bootcommand" -) - -type proxmoxDriver struct { - client commandTyper - vmRef *proxmox.VmRef - specialMap map[string]string - runeMap map[rune]string - interval time.Duration -} - -func NewProxmoxDriver(c commandTyper, vmRef *proxmox.VmRef, interval time.Duration) *proxmoxDriver { - // Mappings for packer shorthand to qemu qkeycodes - sMap := map[string]string{ - "spacebar": "spc", - "bs": "backspace", - "del": "delete", - "return": "ret", - "enter": "ret", - "pageUp": "pgup", - "pageDown": "pgdn", - } - // Mappings for runes that need to be translated to special qkeycodes - // Taken from https://github.com/qemu/qemu/blob/master/pc-bios/keymaps/en-us - rMap := map[rune]string{ - // Clean mappings - ' ': "spc", - '.': "dot", - ',': "comma", - ';': "semicolon", - '*': "asterisk", - '-': "minus", - '[': "bracket_left", - ']': "bracket_right", - '=': "equal", - '\'': "apostrophe", - '`': "grave_accent", - '/': "slash", - '\\': "backslash", - - '!': "shift-1", // "exclam" - '@': "shift-2", // "at" - '#': "shift-3", // "numbersign" - '$': "shift-4", // "dollar" - '%': "shift-5", // "percent" - '^': "shift-6", // "asciicircum" - '&': "shift-7", // "ampersand" - '(': "shift-9", // "parenleft" - ')': "shift-0", // "parenright" - '{': "shift-bracket_left", // "braceleft" - '}': "shift-bracket_right", // "braceright" - '"': "shift-apostrophe", // "quotedbl" - '+': "shift-equal", // "plus" - '_': "shift-minus", // "underscore" - ':': "shift-semicolon", // "colon" - '<': "shift-comma", // "less" is recognized, but seem to map to '/'? - '>': "shift-dot", // "greater" - '~': "shift-grave_accent", // "asciitilde" - '?': "shift-slash", // "question" - '|': "shift-backslash", // "bar" - } - - return &proxmoxDriver{ - client: c, - vmRef: vmRef, - specialMap: sMap, - runeMap: rMap, - interval: interval, - } -} - -func (p *proxmoxDriver) SendKey(key rune, action bootcommand.KeyAction) error { - if special, ok := p.runeMap[key]; ok { - return p.send(special) - } - - var keys string - if unicode.IsUpper(key) { - keys = fmt.Sprintf("shift-%c", unicode.ToLower(key)) - } else { - keys = fmt.Sprintf("%c", key) - } - - return p.send(keys) -} - -func (p *proxmoxDriver) SendSpecial(special string, action bootcommand.KeyAction) error { - keys := special - if replacement, ok := p.specialMap[special]; ok { - keys = replacement - } - - return p.send(keys) -} - -func (p *proxmoxDriver) send(keys string) error { - res, err := p.client.MonitorCmd(p.vmRef, "sendkey "+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 -} - -func (p *proxmoxDriver) Flush() error { return nil } diff --git a/builder/proxmox/builder.go b/builder/proxmox/builder.go deleted file mode 100644 index 59e271380..000000000 --- a/builder/proxmox/builder.go +++ /dev/null @@ -1,122 +0,0 @@ -package proxmox - -import ( - "context" - "crypto/tls" - "fmt" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/common" - "github.com/hashicorp/packer/helper/communicator" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -// The unique id for the builder -const BuilderId = "proxmox.builder" - -type Builder struct { - config Config - runner multistep.Runner - proxmoxClient *proxmox.Client -} - -// Builder implements packer.Builder -var _ packer.Builder = &Builder{} - -var pluginVersion = "1.0.0" - -func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, warnings, errs := NewConfig(raws...) - if errs != nil { - return warnings, errs - } - b.config = *config - return nil, nil -} - -func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { - var err error - tlsConfig := &tls.Config{ - InsecureSkipVerify: b.config.SkipCertValidation, - } - b.proxmoxClient, err = proxmox.NewClient(b.config.ProxmoxURL.String(), nil, tlsConfig) - if err != nil { - return nil, err - } - - err = b.proxmoxClient.Login(b.config.Username, b.config.Password) - if err != nil { - return nil, err - } - - // Set up the state - state := new(multistep.BasicStateBag) - state.Put("config", &b.config) - state.Put("proxmoxClient", b.proxmoxClient) - state.Put("hook", hook) - state.Put("ui", ui) - - // Build the steps - steps := []multistep.Step{ - &stepStartVM{}, - &common.StepHTTPServer{ - HTTPDir: b.config.HTTPDir, - HTTPPortMin: b.config.HTTPPortMin, - HTTPPortMax: b.config.HTTPPortMax, - }, - &stepTypeBootCommand{ - BootConfig: b.config.BootConfig, - Ctx: b.config.ctx, - }, - &communicator.StepConnect{ - Config: &b.config.Comm, - Host: getVMIP, - SSHConfig: b.config.Comm.SSHConfigFunc(), - }, - &common.StepProvision{}, - &common.StepCleanupTempKeys{ - Comm: &b.config.Comm, - }, - &stepConvertToTemplate{}, - &stepFinalizeTemplateConfig{}, - &stepSuccess{}, - } - // Run the steps - b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) - b.runner.Run(ctx, state) - // If there was an error, return that - if rawErr, ok := state.GetOk("error"); ok { - return nil, rawErr.(error) - } - - artifact := &Artifact{ - templateID: state.Get("template_id").(int), - proxmoxClient: b.proxmoxClient, - } - - return artifact, nil -} - -func getVMIP(state multistep.StateBag) (string, error) { - c := state.Get("proxmoxClient").(*proxmox.Client) - vmRef := state.Get("vmRef").(*proxmox.VmRef) - - ifs, err := c.GetVmAgentNetworkInterfaces(vmRef) - if err != nil { - return "", err - } - - // TODO: Do something smarter here? Allow specifying interface? Or address family? - // For now, just go for first non-loopback - for _, iface := range ifs { - for _, addr := range iface.IPAddresses { - if addr.IsLoopback() { - continue - } - return addr.String(), nil - } - } - - return "", fmt.Errorf("Found no IP addresses on VM") -} diff --git a/builder/proxmox/config.go b/builder/proxmox/config.go deleted file mode 100644 index 8b60c509f..000000000 --- a/builder/proxmox/config.go +++ /dev/null @@ -1,196 +0,0 @@ -package proxmox - -import ( - "errors" - "fmt" - "log" - "net/url" - "os" - "time" - - "github.com/hashicorp/packer/common" - "github.com/hashicorp/packer/common/bootcommand" - "github.com/hashicorp/packer/common/uuid" - "github.com/hashicorp/packer/helper/communicator" - "github.com/hashicorp/packer/helper/config" - "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/template/interpolate" - "github.com/mitchellh/mapstructure" -) - -type Config struct { - common.PackerConfig `mapstructure:",squash"` - common.HTTPConfig `mapstructure:",squash"` - bootcommand.BootConfig `mapstructure:",squash"` - RawBootKeyInterval string `mapstructure:"boot_key_interval"` - BootKeyInterval time.Duration `` - Comm communicator.Config `mapstructure:",squash"` - - ProxmoxURLRaw string `mapstructure:"proxmox_url"` - ProxmoxURL *url.URL - SkipCertValidation bool `mapstructure:"insecure_skip_tls_verify"` - Username string `mapstructure:"username"` - Password string `mapstructure:"password"` - Node string `mapstructure:"node"` - - VMName string `mapstructure:"vm_name"` - VMID int `mapstructure:"vm_id"` - - Memory int `mapstructure:"memory"` - Cores int `mapstructure:"cores"` - Sockets int `mapstructure:"sockets"` - OS string `mapstructure:"os"` - NICs []nicConfig `mapstructure:"network_adapters"` - Disks []diskConfig `mapstructure:"disks"` - ISOFile string `mapstructure:"iso_file"` - - TemplateName string `mapstructure:"template_name"` - TemplateDescription string `mapstructure:"template_description"` - UnmountISO bool `mapstructure:"unmount_iso"` - - ctx interpolate.Context -} - -type nicConfig struct { - Model string `mapstructure:"model"` - MACAddress string `mapstructure:"mac_address"` - Bridge string `mapstructure:"bridge"` - VLANTag string `mapstructure:"vlan_tag"` -} -type diskConfig struct { - Type string `mapstructure:"type"` - StoragePool string `mapstructure:"storage_pool"` - StoragePoolType string `mapstructure:"storage_pool_type"` - Size string `mapstructure:"disk_size"` - CacheMode string `mapstructure:"cache_mode"` - DiskFormat string `mapstructure:"format"` -} - -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) - - var md mapstructure.Metadata - err := config.Decode(c, &config.DecodeOpts{ - Metadata: &md, - Interpolate: true, - InterpolateContext: &c.ctx, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{ - "boot_command", - }, - }, - }, raws...) - if err != nil { - return nil, nil, err - } - - var errs *packer.MultiError - - // Defaults - if c.ProxmoxURLRaw == "" { - c.ProxmoxURLRaw = os.Getenv("PROXMOX_URL") - } - if c.Username == "" { - c.Username = os.Getenv("PROXMOX_USERNAME") - } - if c.Password == "" { - c.Password = os.Getenv("PROXMOX_PASSWORD") - } - if c.RawBootKeyInterval == "" { - c.RawBootKeyInterval = os.Getenv(common.PackerKeyEnv) - } - if c.RawBootKeyInterval == "" { - c.BootKeyInterval = common.PackerKeyDefault - } else { - if interval, err := time.ParseDuration(c.RawBootKeyInterval); err == nil { - c.BootKeyInterval = interval - } else { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("Could not parse boot_key_interval: %v", err)) - } - } - - if c.VMName == "" { - // Default to packer-[time-ordered-uuid] - c.VMName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) - } - if c.Memory < 16 { - log.Printf("Memory %d is too small, using default: 512", c.Memory) - c.Memory = 512 - } - if c.Cores < 1 { - log.Printf("Number of cores %d is too small, using default: 1", c.Cores) - c.Cores = 1 - } - if c.Sockets < 1 { - log.Printf("Number of sockets %d is too small, using default: 1", c.Sockets) - c.Sockets = 1 - } - if c.OS == "" { - log.Printf("OS not set, using default 'other'") - c.OS = "other" - } - for idx := range c.NICs { - if c.NICs[idx].Model == "" { - log.Printf("NIC %d model not set, using default 'e1000'", idx) - c.NICs[idx].Model = "e1000" - } - } - for idx := range c.Disks { - if c.Disks[idx].Type == "" { - log.Printf("Disk %d type not set, using default 'scsi'", idx) - c.Disks[idx].Type = "scsi" - } - if c.Disks[idx].Size == "" { - log.Printf("Disk %d size not set, using default '20G'", idx) - c.Disks[idx].Size = "20G" - } - if c.Disks[idx].CacheMode == "" { - log.Printf("Disk %d cache mode not set, using default 'none'", idx) - c.Disks[idx].CacheMode = "none" - } - } - - errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...) - errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...) - errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...) - - // Required configurations that will display errors if not set - if c.Username == "" { - errs = packer.MultiErrorAppend(errs, errors.New("username must be specified")) - } - if c.Password == "" { - errs = packer.MultiErrorAppend(errs, errors.New("password must be specified")) - } - if c.ProxmoxURLRaw == "" { - errs = packer.MultiErrorAppend(errs, errors.New("proxmox_url must be specified")) - } - if c.ProxmoxURL, err = url.Parse(c.ProxmoxURLRaw); err != nil { - errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("Could not parse proxmox_url: %s", err))) - } - if c.ISOFile == "" { - errs = packer.MultiErrorAppend(errs, errors.New("iso_file must be specified")) - } - if c.Node == "" { - errs = packer.MultiErrorAppend(errs, errors.New("node must be specified")) - } - for idx := range c.NICs { - if c.NICs[idx].Bridge == "" { - errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("network_adapters[%d].bridge must be specified", idx))) - } - } - for idx := range c.Disks { - if c.Disks[idx].StoragePool == "" { - errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("disks[%d].storage_pool must be specified", idx))) - } - if c.Disks[idx].StoragePoolType == "" { - errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("disks[%d].storage_pool_type must be specified", idx))) - } - } - - if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs - } - - packer.LogSecretFilter.Set(c.Password) - return c, nil, nil -} diff --git a/builder/proxmox/config_test.go b/builder/proxmox/config_test.go deleted file mode 100644 index e04f4a0d5..000000000 --- a/builder/proxmox/config_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package proxmox - -import ( - "strings" - "testing" - - "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/template" -) - -func TestRequiredParameters(t *testing.T) { - _, _, err := NewConfig(make(map[string]interface{})) - if err == nil { - t.Fatal("Expected empty configuration to fail") - } - errs, ok := err.(*packer.MultiError) - if !ok { - t.Fatal("Expected errors to be packer.MultiError") - } - - required := []string{"username", "password", "proxmox_url", "iso_file", "node", "ssh_username"} - for _, param := range required { - found := false - for _, err := range errs.Errors { - if strings.Contains(err.Error(), param) { - found = true - break - } - } - if !found { - t.Errorf("Expected error about missing parameter %q", required) - } - } -} - -func TestBasicExampleFromDocsIsValid(t *testing.T) { - const config = `{ - "builders": [ - { - "type": "proxmox", - "proxmox_url": "https://my-proxmox.my-domain:8006/api2/json", - "insecure_skip_tls_verify": true, - "username": "apiuser@pve", - "password": "supersecret", - - "node": "my-proxmox", - "network_adapters": [ - { - "bridge": "vmbr0" - } - ], - "disks": [ - { - "type": "scsi", - "disk_size": "5G", - "storage_pool": "local-lvm", - "storage_pool_type": "lvm" - } - ], - - "iso_file": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso", - "http_directory":"config", - "boot_wait": "10s", - "boot_command": [ - " ip=dhcp inst.cmdline inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/ks.cfg" - ], - - "ssh_username": "root", - "ssh_timeout": "15m", - "ssh_password": "packer", - - "unmount_iso": true, - "template_name": "fedora-29", - "template_description": "Fedora 29-1.2, generated on {{ isotime \"2006-01-02T15:04:05Z\" }}" - } - ] -}` - tpl, err := template.Parse(strings.NewReader(config)) - if err != nil { - t.Fatal(err) - } - - b := &Builder{} - warn, err := b.Prepare(tpl.Builders["proxmox"].Config) - if err != nil { - t.Fatal(err, warn) - } - - // The example config does not set a number of optional fields. Validate that: - // Memory 0 is too small, using default: 512 - // Number of cores 0 is too small, using default: 1 - // Number of sockets 0 is too small, using default: 1 - // OS not set, using default 'other' - // NIC 0 model not set, using default 'e1000' - // Disk 0 cache mode not set, using default 'none' - - if b.config.Memory != 512 { - t.Errorf("Expected Memory to be 512, got %d", b.config.Memory) - } - if b.config.Cores != 1 { - t.Errorf("Expected Cores to be 1, got %d", b.config.Cores) - } - if b.config.Sockets != 1 { - t.Errorf("Expected Sockets to be 1, got %d", b.config.Sockets) - } - if b.config.OS != "other" { - t.Errorf("Expected OS to be 'other', got %s", b.config.OS) - } - if b.config.NICs[0].Model != "e1000" { - t.Errorf("Expected NIC model to be 'e1000', got %s", b.config.NICs[0].Model) - } - if b.config.Disks[0].CacheMode != "none" { - t.Errorf("Expected disk cache mode to be 'none', got %s", b.config.Disks[0].CacheMode) - } -} diff --git a/builder/proxmox/step_convert_to_template.go b/builder/proxmox/step_convert_to_template.go deleted file mode 100644 index 712badd81..000000000 --- a/builder/proxmox/step_convert_to_template.go +++ /dev/null @@ -1,53 +0,0 @@ -package proxmox - -import ( - "context" - "fmt" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -// stepConvertToTemplate takes the running VM configured in earlier steps, stops it, and -// converts it into a Proxmox template. -// -// It sets the template_id state which is used for Artifact lookup. -type stepConvertToTemplate struct{} - -type templateConverter interface { - ShutdownVm(*proxmox.VmRef) (string, error) - CreateTemplate(*proxmox.VmRef) error -} - -var _ templateConverter = &proxmox.Client{} - -func (s *stepConvertToTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - client := state.Get("proxmoxClient").(templateConverter) - vmRef := state.Get("vmRef").(*proxmox.VmRef) - - ui.Say("Stopping VM") - _, err := client.ShutdownVm(vmRef) - if err != nil { - err := fmt.Errorf("Error converting VM to template, could not stop: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - ui.Say("Converting VM to template") - err = client.CreateTemplate(vmRef) - if err != nil { - err := fmt.Errorf("Error converting VM to template: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - state.Put("template_id", vmRef.VmId()) - - return multistep.ActionContinue -} - -func (s *stepConvertToTemplate) Cleanup(state multistep.StateBag) {} diff --git a/builder/proxmox/step_convert_to_template_test.go b/builder/proxmox/step_convert_to_template_test.go deleted file mode 100644 index a7f472e3a..000000000 --- a/builder/proxmox/step_convert_to_template_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package proxmox - -import ( - "context" - "fmt" - "testing" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -type converterMock struct { - shutdownVm func(*proxmox.VmRef) (string, error) - createTemplate func(*proxmox.VmRef) error -} - -func (m converterMock) ShutdownVm(r *proxmox.VmRef) (string, error) { - return m.shutdownVm(r) -} -func (m converterMock) CreateTemplate(r *proxmox.VmRef) error { - return m.createTemplate(r) -} - -var _ templateConverter = converterMock{} - -func TestConvertToTemplate(t *testing.T) { - cs := []struct { - name string - shutdownErr error - expectCallCreateTemplate bool - createTemplateErr error - expectedAction multistep.StepAction - expectTemplateIdSet bool - }{ - { - name: "no errors returns continue and sets template id", - expectCallCreateTemplate: true, - expectedAction: multistep.ActionContinue, - expectTemplateIdSet: true, - }, - { - name: "when shutdown fails, don't try to create template and halt", - shutdownErr: fmt.Errorf("failed to stop vm"), - expectCallCreateTemplate: false, - expectedAction: multistep.ActionHalt, - expectTemplateIdSet: false, - }, - { - name: "when create template fails, halt", - expectCallCreateTemplate: true, - createTemplateErr: fmt.Errorf("failed to stop vm"), - expectedAction: multistep.ActionHalt, - expectTemplateIdSet: false, - }, - } - - const vmid = 123 - - for _, c := range cs { - t.Run(c.name, func(t *testing.T) { - converter := converterMock{ - shutdownVm: func(r *proxmox.VmRef) (string, error) { - if r.VmId() != vmid { - t.Errorf("ShutdownVm called with unexpected id, expected %d, got %d", vmid, r.VmId()) - } - return "", c.shutdownErr - }, - createTemplate: func(r *proxmox.VmRef) error { - if r.VmId() != vmid { - t.Errorf("CreateTemplate called with unexpected id, expected %d, got %d", vmid, r.VmId()) - } - if !c.expectCallCreateTemplate { - t.Error("Did not expect CreateTemplate to be called") - } - - return c.createTemplateErr - }, - } - - state := new(multistep.BasicStateBag) - state.Put("ui", packer.TestUi(t)) - state.Put("vmRef", proxmox.NewVmRef(vmid)) - state.Put("proxmoxClient", converter) - - step := stepConvertToTemplate{} - action := step.Run(context.TODO(), state) - if action != c.expectedAction { - t.Errorf("Expected action to be %v, got %v", c.expectedAction, action) - } - - id, wasSet := state.GetOk("template_id") - - if c.expectTemplateIdSet != wasSet { - t.Errorf("Expected template_id state present=%v was present=%v", c.expectTemplateIdSet, wasSet) - } - - if c.expectTemplateIdSet && id != vmid { - t.Errorf("Expected template_id state to be set to %d, got %v", vmid, id) - } - }) - } -} diff --git a/builder/proxmox/step_finalize_template_config.go b/builder/proxmox/step_finalize_template_config.go deleted file mode 100644 index df90fc9f8..000000000 --- a/builder/proxmox/step_finalize_template_config.go +++ /dev/null @@ -1,72 +0,0 @@ -package proxmox - -import ( - "context" - "fmt" - "strings" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -// stepFinalizeTemplateConfig does any required modifications to the configuration _after_ -// the VM has been converted into a template, such as updating name and description, or -// unmounting the installation ISO. -type stepFinalizeTemplateConfig struct{} - -type templateFinalizer interface { - GetVmConfig(*proxmox.VmRef) (map[string]interface{}, error) - SetVmConfig(*proxmox.VmRef, map[string]interface{}) (interface{}, error) -} - -var _ templateFinalizer = &proxmox.Client{} - -func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - client := state.Get("proxmoxClient").(templateFinalizer) - c := state.Get("config").(*Config) - vmRef := state.Get("vmRef").(*proxmox.VmRef) - - changes := make(map[string]interface{}) - - if c.TemplateName != "" { - changes["name"] = c.TemplateName - } - - // During build, the description is "Packer ephemeral build VM", so if no description is - // set, we need to clear it - changes["description"] = c.TemplateDescription - - if c.UnmountISO { - vmParams, err := client.GetVmConfig(vmRef) - if err != nil { - err := fmt.Errorf("Error fetching template config: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if vmParams["ide2"] == nil || !strings.HasSuffix(vmParams["ide2"].(string), "media=cdrom") { - err := fmt.Errorf("Cannot eject ISO from cdrom drive, ide2 is not present, or not a cdrom media") - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - changes["ide2"] = "none,media=cdrom" - } - - if len(changes) > 0 { - _, err := client.SetVmConfig(vmRef, changes) - if err != nil { - err := fmt.Errorf("Error updating template: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - return multistep.ActionContinue -} - -func (s *stepFinalizeTemplateConfig) Cleanup(state multistep.StateBag) {} diff --git a/builder/proxmox/step_finalize_template_config_test.go b/builder/proxmox/step_finalize_template_config_test.go deleted file mode 100644 index 501dddef2..000000000 --- a/builder/proxmox/step_finalize_template_config_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package proxmox - -import ( - "context" - "fmt" - "testing" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -type finalizerMock struct { - getConfig func() (map[string]interface{}, error) - setConfig func(map[string]interface{}) (string, error) -} - -func (m finalizerMock) GetVmConfig(*proxmox.VmRef) (map[string]interface{}, error) { - return m.getConfig() -} -func (m finalizerMock) SetVmConfig(vmref *proxmox.VmRef, c map[string]interface{}) (interface{}, error) { - return m.setConfig(c) -} - -var _ templateFinalizer = finalizerMock{} - -func TestTemplateFinalize(t *testing.T) { - cs := []struct { - name string - builderConfig *Config - initialVMConfig map[string]interface{} - getConfigErr error - expectCallSetConfig bool - expectedVMConfig map[string]interface{} - setConfigErr error - expectedAction multistep.StepAction - }{ - { - name: "empty config changes only description", - builderConfig: &Config{}, - initialVMConfig: map[string]interface{}{ - "name": "dummy", - "description": "Packer ephemeral build VM", - "ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", - }, - expectCallSetConfig: true, - expectedVMConfig: map[string]interface{}{ - "name": nil, - "description": "", - "ide2": nil, - }, - expectedAction: multistep.ActionContinue, - }, - { - name: "all options", - builderConfig: &Config{ - TemplateName: "my-template", - TemplateDescription: "some-description", - UnmountISO: true, - }, - initialVMConfig: map[string]interface{}{ - "name": "dummy", - "description": "Packer ephemeral build VM", - "ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", - }, - expectCallSetConfig: true, - expectedVMConfig: map[string]interface{}{ - "name": "my-template", - "description": "some-description", - "ide2": "none,media=cdrom", - }, - expectedAction: multistep.ActionContinue, - }, - { - name: "no cd-drive with unmount=true should returns halt", - builderConfig: &Config{ - TemplateName: "my-template", - TemplateDescription: "some-description", - UnmountISO: true, - }, - initialVMConfig: map[string]interface{}{ - "name": "dummy", - "description": "Packer ephemeral build VM", - "ide1": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", - }, - expectCallSetConfig: false, - expectedAction: multistep.ActionHalt, - }, - { - name: "GetVmConfig error should return halt", - builderConfig: &Config{ - TemplateName: "my-template", - TemplateDescription: "some-description", - UnmountISO: true, - }, - getConfigErr: fmt.Errorf("some error"), - expectCallSetConfig: false, - expectedAction: multistep.ActionHalt, - }, - { - name: "SetVmConfig error should return halt", - builderConfig: &Config{ - TemplateName: "my-template", - TemplateDescription: "some-description", - UnmountISO: true, - }, - initialVMConfig: map[string]interface{}{ - "name": "dummy", - "description": "Packer ephemeral build VM", - "ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", - }, - expectCallSetConfig: true, - setConfigErr: fmt.Errorf("some error"), - expectedAction: multistep.ActionHalt, - }, - } - - for _, c := range cs { - t.Run(c.name, func(t *testing.T) { - finalizer := finalizerMock{ - getConfig: func() (map[string]interface{}, error) { - return c.initialVMConfig, c.getConfigErr - }, - setConfig: func(cfg map[string]interface{}) (string, error) { - if !c.expectCallSetConfig { - t.Error("Did not expect SetVmConfig to be called") - } - for key, val := range c.expectedVMConfig { - if cfg[key] != val { - t.Errorf("Expected %q to be %q, got %q", key, val, cfg[key]) - } - } - - return "", c.setConfigErr - }, - } - - state := new(multistep.BasicStateBag) - state.Put("ui", packer.TestUi(t)) - state.Put("config", c.builderConfig) - state.Put("vmRef", proxmox.NewVmRef(1)) - state.Put("proxmoxClient", finalizer) - - step := stepFinalizeTemplateConfig{} - action := step.Run(context.TODO(), state) - if action != c.expectedAction { - t.Errorf("Expected action to be %v, got %v", c.expectedAction, action) - } - }) - } -} diff --git a/builder/proxmox/step_start_vm.go b/builder/proxmox/step_start_vm.go deleted file mode 100644 index 779394023..000000000 --- a/builder/proxmox/step_start_vm.go +++ /dev/null @@ -1,150 +0,0 @@ -package proxmox - -import ( - "context" - "fmt" - "log" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -// stepStartVM takes the given configuration and starts a VM on the given Proxmox node. -// -// It sets the vmRef state which is used throughout the later steps to reference the VM -// in API calls. -type stepStartVM struct{} - -func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - client := state.Get("proxmoxClient").(*proxmox.Client) - c := state.Get("config").(*Config) - - ui.Say("Creating VM") - config := proxmox.ConfigQemu{ - Name: c.VMName, - Agent: "1", - Description: "Packer ephemeral build VM", - Memory: c.Memory, - QemuCores: c.Cores, - QemuSockets: c.Sockets, - QemuOs: c.OS, - QemuIso: c.ISOFile, - QemuNetworks: generateProxmoxNetworkAdapters(c.NICs), - QemuDisks: generateProxmoxDisks(c.Disks), - } - - if c.VMID == 0 { - ui.Say("No VM ID given, getting next free from Proxmox") - for n := 0; n < 5; n++ { - id, err := proxmox.MaxVmId(client) - if err != nil { - log.Printf("Error getting max used VM ID: %v (attempt %d/5)", err, n+1) - continue - } - c.VMID = id + 1 - break - } - if c.VMID == 0 { - err := fmt.Errorf("Failed to get free VM ID") - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - vmRef := proxmox.NewVmRef(c.VMID) - vmRef.SetNode(c.Node) - - err := config.CreateVm(vmRef, client) - if err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Store the vm id for later - state.Put("vmRef", vmRef) - - ui.Say("Starting VM") - _, err = client.StartVm(vmRef) - if err != nil { - err := fmt.Errorf("Error starting VM: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func generateProxmoxNetworkAdapters(nics []nicConfig) proxmox.QemuDevices { - devs := make(proxmox.QemuDevices) - for idx := range nics { - devs[idx] = make(proxmox.QemuDevice) - setDeviceParamIfDefined(devs[idx], "model", nics[idx].Model) - setDeviceParamIfDefined(devs[idx], "macaddr", nics[idx].MACAddress) - setDeviceParamIfDefined(devs[idx], "bridge", nics[idx].Bridge) - setDeviceParamIfDefined(devs[idx], "tag", nics[idx].VLANTag) - } - return devs -} -func generateProxmoxDisks(disks []diskConfig) proxmox.QemuDevices { - devs := make(proxmox.QemuDevices) - for idx := range disks { - devs[idx] = make(proxmox.QemuDevice) - setDeviceParamIfDefined(devs[idx], "type", disks[idx].Type) - setDeviceParamIfDefined(devs[idx], "size", disks[idx].Size) - setDeviceParamIfDefined(devs[idx], "storage", disks[idx].StoragePool) - setDeviceParamIfDefined(devs[idx], "storage_type", disks[idx].StoragePoolType) - setDeviceParamIfDefined(devs[idx], "cache", disks[idx].CacheMode) - setDeviceParamIfDefined(devs[idx], "format", disks[idx].DiskFormat) - } - return devs -} - -func setDeviceParamIfDefined(dev proxmox.QemuDevice, key, value string) { - if value != "" { - dev[key] = value - } -} - -type startedVMCleaner interface { - StopVm(*proxmox.VmRef) (string, error) - DeleteVm(*proxmox.VmRef) (string, error) -} - -var _ startedVMCleaner = &proxmox.Client{} - -func (s *stepStartVM) Cleanup(state multistep.StateBag) { - vmRefUntyped, ok := state.GetOk("vmRef") - // If not ok, we probably errored out before creating the VM - if !ok { - return - } - vmRef := vmRefUntyped.(*proxmox.VmRef) - - // The vmRef will actually refer to the created template if everything - // finished successfully, so in that case we shouldn't cleanup - if _, ok := state.GetOk("success"); ok { - return - } - - client := state.Get("proxmoxClient").(startedVMCleaner) - ui := state.Get("ui").(packer.Ui) - - // Destroy the server we just created - 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)) - return - } - - ui.Say("Deleting VM") - _, err = client.DeleteVm(vmRef) - if err != nil { - ui.Error(fmt.Sprintf("Error deleting VM. Please delete it manually: %s", err)) - return - } -} diff --git a/builder/proxmox/step_start_vm_test.go b/builder/proxmox/step_start_vm_test.go deleted file mode 100644 index cb19670f5..000000000 --- a/builder/proxmox/step_start_vm_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package proxmox - -import ( - "fmt" - "testing" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -type startedVMCleanerMock struct { - stopVm func() (string, error) - deleteVm func() (string, error) -} - -func (m startedVMCleanerMock) StopVm(*proxmox.VmRef) (string, error) { - return m.stopVm() -} -func (m startedVMCleanerMock) DeleteVm(*proxmox.VmRef) (string, error) { - return m.deleteVm() -} - -var _ startedVMCleaner = &startedVMCleanerMock{} - -func TestCleanupStartVM(t *testing.T) { - cs := []struct { - name string - setVmRef bool - setSuccess bool - stopVMErr error - expectCallStopVM bool - deleteVMErr error - expectCallDeleteVM bool - }{ - { - name: "when vmRef state is not set, nothing should happen", - setVmRef: false, - expectCallStopVM: false, - }, - { - name: "when success state is set, nothing should happen", - setVmRef: true, - setSuccess: true, - expectCallStopVM: false, - }, - { - name: "when not successful, vm should be stopped and deleted", - setVmRef: true, - setSuccess: false, - expectCallStopVM: true, - expectCallDeleteVM: true, - }, - { - name: "if stopping fails, DeleteVm should not be called", - setVmRef: true, - setSuccess: false, - expectCallStopVM: true, - stopVMErr: fmt.Errorf("some error"), - expectCallDeleteVM: false, - }, - } - - for _, c := range cs { - t.Run(c.name, func(t *testing.T) { - var stopWasCalled, deleteWasCalled bool - - cleaner := startedVMCleanerMock{ - stopVm: func() (string, error) { - if !c.expectCallStopVM { - t.Error("Did not expect StopVm to be called") - } - - stopWasCalled = true - return "", c.stopVMErr - }, - deleteVm: func() (string, error) { - if !c.expectCallDeleteVM { - t.Error("Did not expect DeleteVm to be called") - } - - deleteWasCalled = true - return "", c.deleteVMErr - }, - } - - state := new(multistep.BasicStateBag) - state.Put("ui", packer.TestUi(t)) - state.Put("proxmoxClient", cleaner) - if c.setVmRef { - state.Put("vmRef", proxmox.NewVmRef(1)) - } - if c.setSuccess { - state.Put("success", "true") - } - - step := stepStartVM{} - step.Cleanup(state) - - if c.expectCallStopVM && !stopWasCalled { - t.Error("Expected StopVm to be called, but it wasn't") - } - if c.expectCallDeleteVM && !deleteWasCalled { - t.Error("Expected DeleteVm to be called, but it wasn't") - } - }) - } -} diff --git a/builder/proxmox/step_success.go b/builder/proxmox/step_success.go deleted file mode 100644 index 06de9f21d..000000000 --- a/builder/proxmox/step_success.go +++ /dev/null @@ -1,22 +0,0 @@ -package proxmox - -import ( - "context" - - "github.com/hashicorp/packer/helper/multistep" -) - -// stepSuccess runs after the full build has succeeded. -// -// It sets the success state, which ensures cleanup does not remove the finished template -type stepSuccess struct{} - -func (s *stepSuccess) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - // We need to ensure stepStartVM.Cleanup doesn't delete the template (no - // difference between VMs and templates when deleting) - state.Put("success", true) - - return multistep.ActionContinue -} - -func (s *stepSuccess) Cleanup(state multistep.StateBag) {} diff --git a/builder/proxmox/step_type_boot_command.go b/builder/proxmox/step_type_boot_command.go deleted file mode 100644 index 89d50fb0d..000000000 --- a/builder/proxmox/step_type_boot_command.go +++ /dev/null @@ -1,119 +0,0 @@ -package proxmox - -import ( - "context" - "errors" - "fmt" - "log" - "net" - "time" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/common" - "github.com/hashicorp/packer/common/bootcommand" - commonhelper "github.com/hashicorp/packer/helper/common" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/template/interpolate" -) - -// stepTypeBootCommand takes the started VM, and sends the keystrokes required to start -// the installation process such that Packer can later reach the VM over SSH/WinRM -type stepTypeBootCommand struct { - bootcommand.BootConfig - Ctx interpolate.Context -} - -type bootCommandTemplateData struct { - HTTPIP string - HTTPPort uint -} - -type commandTyper interface { - MonitorCmd(*proxmox.VmRef, string) (map[string]interface{}, error) -} - -var _ commandTyper = &proxmox.Client{} - -func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - c := state.Get("config").(*Config) - client := state.Get("proxmoxClient").(commandTyper) - vmRef := state.Get("vmRef").(*proxmox.VmRef) - - if len(s.BootCommand) == 0 { - log.Println("No boot command given, skipping") - return multistep.ActionContinue - } - - if int64(s.BootWait) > 0 { - ui.Say(fmt.Sprintf("Waiting %s for boot", s.BootWait.String())) - select { - case <-time.After(s.BootWait): - break - case <-ctx.Done(): - return multistep.ActionHalt - } - } - - 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) - s.Ctx.Data = &bootCommandTemplateData{ - HTTPIP: httpIP, - HTTPPort: state.Get("http_port").(uint), - } - - ui.Say("Typing the boot command") - d := NewProxmoxDriver(client, vmRef, c.BootKeyInterval) - command, err := interpolate.Render(s.FlatBootCommand(), &s.Ctx) - if err != nil { - err := fmt.Errorf("Error preparing boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - seq, err := bootcommand.GenerateExpressionSequence(command) - if err != nil { - err := fmt.Errorf("Error generating boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if err := seq.Do(ctx, d); err != nil { - err := fmt.Errorf("Error running boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (*stepTypeBootCommand) Cleanup(multistep.StateBag) { - commonhelper.RemoveSharedStateFile("ip", "") -} - -func hostIP() (string, error) { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "", err - } - - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String(), nil - } - } - } - - return "", errors.New("No host IP found") -} diff --git a/builder/proxmox/step_type_boot_command_test.go b/builder/proxmox/step_type_boot_command_test.go deleted file mode 100644 index c51b57d73..000000000 --- a/builder/proxmox/step_type_boot_command_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package proxmox - -import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/Telmate/proxmox-api-go/proxmox" - "github.com/hashicorp/packer/common/bootcommand" - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -type commandTyperMock struct { - monitorCmd func(*proxmox.VmRef, string) (map[string]interface{}, error) -} - -func (m commandTyperMock) MonitorCmd(ref *proxmox.VmRef, cmd string) (map[string]interface{}, error) { - return m.monitorCmd(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: "simple boot command is typed", - builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}}, - expectCallMonitorCmd: true, - expectedKeysSent: "hello", - expectedAction: multistep.ActionContinue, - }, - { - name: "interpolated boot command", - builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"helloworld"}}}, - expectCallMonitorCmd: 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: "without boot command monitorcmd should not be called", - builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{}}}, - expectCallMonitorCmd: false, - expectedAction: multistep.ActionContinue, - }, - { - name: "invalid boot command template function", - builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"{{ foo }}"}}}, - expectCallMonitorCmd: 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: "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, - }, - } - - for _, c := range cs { - 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) - } - - accumulator.WriteString(strings.TrimPrefix(cmd, "sendkey ")) - - return c.monitorCmdRet, c.monitorCmdErr - }, - } - - state := new(multistep.BasicStateBag) - state.Put("ui", packer.TestUi(t)) - state.Put("config", c.builderConfig) - state.Put("http_port", uint(0)) - state.Put("vmRef", proxmox.NewVmRef(1)) - state.Put("proxmoxClient", typer) - - step := stepTypeBootCommand{ - c.builderConfig.BootConfig, - c.builderConfig.ctx, - } - action := step.Run(context.TODO(), state) - step.Cleanup(state) - - if action != c.expectedAction { - t.Errorf("Expected action to be %v, got %v", c.expectedAction, action) - } - if c.expectedKeysSent != accumulator.String() { - t.Errorf("Expected keystrokes to be %q, got %q", c.expectedKeysSent, accumulator.String()) - } - }) - } -} diff --git a/command/plugin.go b/command/plugin.go index 25b9fd27a..416605a6b 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -40,7 +40,6 @@ import ( parallelsisobuilder "github.com/hashicorp/packer/builder/parallels/iso" parallelspvmbuilder "github.com/hashicorp/packer/builder/parallels/pvm" profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks" - proxmoxbuilder "github.com/hashicorp/packer/builder/proxmox" qemubuilder "github.com/hashicorp/packer/builder/qemu" scalewaybuilder "github.com/hashicorp/packer/builder/scaleway" tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm" @@ -119,7 +118,6 @@ var Builders = map[string]packer.Builder{ "parallels-iso": new(parallelsisobuilder.Builder), "parallels-pvm": new(parallelspvmbuilder.Builder), "profitbricks": new(profitbricksbuilder.Builder), - "proxmox": new(proxmoxbuilder.Builder), "qemu": new(qemubuilder.Builder), "scaleway": new(scalewaybuilder.Builder), "tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder), diff --git a/go.sum b/go.sum index 1f5f5be6a..7bb800a2c 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,6 @@ github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290 h1:K9I21XUHN github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591 h1:/P9HCl71+Eh6vDbKNyRu+rpIIR70UCZWNOGexVV3e6k= 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-20190410200643-f08824d5082d h1:igrCnHheXb+lZ1bW9Ths8JZZIjh9D4Vi/49JqiHE+cI= -github.com/Telmate/proxmox-api-go v0.0.0-20190410200643-f08824d5082d/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/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f h1:jI4DIE5Vf4oRaHfthB0oRhU+yuYuoOTurDzwAlskP00= diff --git a/vendor/github.com/Telmate/proxmox-api-go/LICENSE b/vendor/github.com/Telmate/proxmox-api-go/LICENSE deleted file mode 100644 index 57395f1b0..000000000 --- a/vendor/github.com/Telmate/proxmox-api-go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/Telmate/proxmox-api-go/proxmox/util.go b/vendor/github.com/Telmate/proxmox-api-go/proxmox/util.go deleted file mode 100644 index 5841c421f..000000000 --- a/vendor/github.com/Telmate/proxmox-api-go/proxmox/util.go +++ /dev/null @@ -1,62 +0,0 @@ -package proxmox - -import ( - "strconv" - "strings" -) - -func inArray(arr []string, str string) bool { - for _, elem := range arr { - if elem == str { - return true - } - } - - return false -} - -func Itob(i int) bool { - if i == 1 { - return true - } - return false -} - -// ParseSubConf - Parse standard sub-conf strings `key=value`. -func ParseSubConf( - element string, - separator string, -) (key string, value interface{}) { - if strings.Contains(element, separator) { - conf := strings.Split(element, separator) - key, value := conf[0], conf[1] - var interValue interface{} - - // Make sure to add value in right type, - // because all subconfig are returned as strings from Proxmox API. - if iValue, err := strconv.ParseInt(value, 10, 64); err == nil { - interValue = int(iValue) - } else if bValue, err := strconv.ParseBool(value); err == nil { - interValue = bValue - } else { - interValue = value - } - return key, interValue - } - return -} - -// ParseConf - Parse standard device conf string `key1=val1,key2=val2`. -func ParseConf( - kvString string, - confSeparator string, - subConfSeparator string, -) QemuDevice { - var confMap = QemuDevice{} - confList := strings.Split(kvString, confSeparator) - for _, item := range confList { - key, value := ParseSubConf(item, subConfSeparator) - confMap[key] = value - } - return confMap -} diff --git a/vendor/modules.txt b/vendor/modules.txt index bc658b7dc..43c4b62c5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -45,8 +45,6 @@ github.com/NaverCloudPlatform/ncloud-sdk-go/sdk github.com/NaverCloudPlatform/ncloud-sdk-go/common github.com/NaverCloudPlatform/ncloud-sdk-go/request github.com/NaverCloudPlatform/ncloud-sdk-go/oauth -# github.com/Telmate/proxmox-api-go v0.0.0-20190410200643-f08824d5082d -github.com/Telmate/proxmox-api-go/proxmox # github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f github.com/aliyun/aliyun-oss-go-sdk/oss # github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd diff --git a/website/source/docs/builders/proxmox.html.md b/website/source/docs/builders/proxmox.html.md deleted file mode 100644 index ea8beb58d..000000000 --- a/website/source/docs/builders/proxmox.html.md +++ /dev/null @@ -1,201 +0,0 @@ ---- -description: | - The proxmox Packer builder is able to create new images for use with - Proxmox VE. The builder takes an ISO source, runs any provisioning - necessary on the image after launching it, then creates a virtual machine - template. -layout: docs -page_title: 'Proxmox - Builders' -sidebar_current: 'docs-builders-proxmox' ---- - -# Proxmox Builder - -Type: `proxmox` - -The `proxmox` Packer builder is able to create new images for use with -[Proxmox](https://www.proxmox.com/en/proxmox-ve). The builder takes an ISO -image, runs any provisioning necessary on the image after launching it, then -creates a virtual machine template. This template can then be used as to -create new virtual machines within Proxmox. - -The builder does *not* manage templates. Once it creates a template, it is up -to you to use it or delete it. - -## Configuration Reference - -There are many configuration options available for the builder. They are -segmented below into two categories: required and optional parameters. Within -each category, the available configuration keys are alphabetized. - -In addition to the options listed here, a -[communicator](/docs/templates/communicator.html) can be configured for this -builder. - -### Required: - -- `proxmox_url` (string) - URL to the Proxmox API, including the full path, - so `https://:/api2/json` for example. - Can also be set via the `PROXMOX_URL` environment variable. - -- `username` (string) - Username when authenticating to Proxmox, including - the realm. For example `user@pve` to use the local Proxmox realm. - Can also be set via the `PROXMOX_USERNAME` environment variable. - -- `password` (string) - Password for the user. - Can also be set via the `PROXMOX_PASSWORD` environment variable. - -- `node` (string) - Which node in the Proxmox cluster to start the virtual - machine on during creation. - -- `iso_file` (string) - Path to the ISO file to boot from, expressed as a - proxmox datastore path, for example - `local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso` - -### Optional: -- `insecure_skip_tls_verify` (bool) - Skip validating the certificate. - -- `vm_name` (string) - Name of the virtual machine during creation. If not - given, a random uuid will be used. - -- `vm_id` (int) - The ID used to reference the virtual machine. This will - also be the ID of the final template. If not given, the next free ID on - the node will be used. - -- `memory` (int) - How much memory, in megabytes, to give the virtual - machine. Defaults to `512`. - -- `cores` (int) - How many CPU cores to give the virtual machine. Defaults - to `1`. - -- `sockets` (int) - How many CPU sockets to give the virtual machine. - Defaults to `1` - -- `os` (string) - The operating system. Can be `linux`, `windows`, `solaris` - or `other`. Defaults to `other`. - -- `network_adapters` (array of objects) - Network adapters attached to the - virtual machine. Example: - - ```json - [ - { - "model": "virtio", - "bridge": "vmbr0", - "vlan_tag": "10" - } - ] - ``` - - - `bridge` (string) - Required. Which Proxmox bridge to attach the - adapter to. - - - `model` (string) - Model of the virtual network adapter. Can be - `rtl8139`, `ne2k_pci`, `e1000`, `pcnet`, `virtio`, `ne2k_isa`, - `i82551`, `i82557b`, `i82559er`, `vmxnet3`, `e1000-82540em`, - `e1000-82544gc` or `e1000-82545em`. Defaults to `e1000`. - - - `mac_address` (string) - Give the adapter a specific MAC address. If - not set, defaults to a random MAC. - - - `vlan_tag` (string) - If the adapter should tag packets. Defaults to - no tagging. - -- `disks` (array of objects) - Disks attached to the virtual machine. - Example: - - ```json - [ - { - "type": "scsi", - "disk_size": "5G", - "storage_pool": "local-lvm", - "storage_pool_type": "lvm" - } - ] - ``` - - - `storage_pool` (string) - Required. Name of the Proxmox storage pool - to store the virtual machine disk on. A `local-lvm` pool is allocated - by the installer, for example. - - - `storage_pool_type` (string) - Required. The type of the pool, can - be `lvm`, `lvm-thin`, `zfs` or `directory`. - - - `type` (string) - The type of disk. Can be `scsi`, `sata`, `virtio` or - `ide`. Defaults to `scsi`. - - - `disk_size` (string) - The size of the disk, including a unit suffix, such - as `10G` to indicate 10 gigabytes. - - - `cache_mode` (string) - How to cache operations to the disk. Can be - `none`, `writethrough`, `writeback`, `unsafe` or `directsync`. - Defaults to `none`. - - - `format` (string) - The format of the file backing the disk. Can be - `raw`, `cow`, `qcow`, `qed`, `qcow2`, `vmdk` or `cloop`. Defaults to - `raw`. - -- `template_name` (string) - Name of the template. Defaults to the generated - name used during creation. - -- `template_description` (string) - Description of the template, visible in - the Proxmox interface. - -- `unmount_iso` (bool) - If true, remove the mounted ISO from the template - after finishing. Defaults to `false`. - - -## Example: Fedora with kickstart - -Here is a basic example creating a Fedora 29 server image with a Kickstart -file served with Packer's HTTP server. Note that the iso file needs to be -manually downloaded. - -``` json -{ - "variables": { - "username": "apiuser@pve", - "password": "supersecret" - }, - "builders": [ - { - "type": "proxmox", - "proxmox_url": "https://my-proxmox.my-domain:8006/api2/json", - "insecure_skip_tls_verify": true, - "username": "{{user `username`}}", - "password": "{{user `password`}}", - - "node": "my-proxmox", - "network_adapters": [ - { - "bridge": "vmbr0" - } - ], - "disks": [ - { - "type": "scsi", - "disk_size": "5G", - "storage_pool": "local-lvm", - "storage_pool_type": "lvm" - } - ], - - "iso_file": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso", - "http_directory":"config", - "boot_wait": "10s", - "boot_command": [ - " ip=dhcp inst.cmdline inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/ks.cfg" - ], - - "ssh_username": "root", - "ssh_timeout": "15m", - "ssh_password": "packer", - - "unmount_iso": true, - "template_name": "fedora-29", - "template_description": "Fedora 29-1.2, generated on {{ isotime \"2006-01-02T15:04:05Z\" }}" - } - ] -} -```