From 2f2c5dff9868ede5bb313ea085b540407d93a8ff Mon Sep 17 00:00:00 2001 From: Grant Gongaware Date: Wed, 24 May 2017 10:18:06 -0700 Subject: [PATCH] Add support for a provisioner to support post-processing actions like unplugging the ssh-forwarding, fake net1, adapter. --- README.md | 13 +++++++++ main.go | 3 ++ proxmox/provider.go | 32 ++++++++++++++++++-- proxmox/provisioner.go | 58 +++++++++++++++++++++++++++++++++++++ proxmox/resource_vm_qemu.go | 19 ++++++++---- 5 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 proxmox/provisioner.go diff --git a/README.md b/README.md index 4d7ace6..38b1cf8 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,11 @@ Requires https://github.com/Telmate/proxmox-api-go ``` go build -o terraform-provider-proxmox cp terraform-provider-proxmox $GOPATH/bin +cp terraform-provider-proxmox $GOPATH/bin/terraform-provisioner-proxmox ``` +Note: this plugin is both a provider and provisioner in one, which is why it needs to be in the $GOPATH/bin/ twice. + Recommended ISO builder https://github.com/Telmate/terraform-ubuntu-proxmox-iso @@ -56,6 +59,16 @@ EOF auto eth0 iface eth0 inet dhcp EOF + + provisioner "remote-exec" { + inline = [ + "ip a" + ] + } + + provisioner "proxmox" { + action = "sshbackward" + } } ``` diff --git a/main.go b/main.go index 00ad941..6f327bc 100644 --- a/main.go +++ b/main.go @@ -11,5 +11,8 @@ func main() { ProviderFunc: func() terraform.ResourceProvider { return proxmox.Provider() }, + ProvisionerFunc: func() terraform.ResourceProvisioner { + return proxmox.Provisioner() + }, }) } diff --git a/proxmox/provider.go b/proxmox/provider.go index 01ed805..8eb35c0 100644 --- a/proxmox/provider.go +++ b/proxmox/provider.go @@ -1,8 +1,11 @@ package proxmox import ( + "fmt" pxapi "github.com/Telmate/proxmox-api-go/proxmox" "github.com/hashicorp/terraform/helper/schema" + "regexp" + "strconv" "sync" ) @@ -47,8 +50,7 @@ func Provider() *schema.Provider { } func providerConfigure(d *schema.ResourceData) (interface{}, error) { - client, _ := pxapi.NewClient(d.Get("pm_api_url").(string), nil, nil) - err := client.Login(d.Get("pm_user").(string), d.Get("pm_password").(string)) + client, err := getClient(d.Get("pm_api_url").(string), d.Get("pm_user").(string), d.Get("pm_password").(string)) if err != nil { return nil, err } @@ -57,6 +59,15 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { }, nil } +func getClient(pm_api_url string, pm_user string, pm_password string) (*pxapi.Client, error) { + client, _ := pxapi.NewClient(pm_api_url, nil, nil) + err := client.Login(pm_user, pm_password) + if err != nil { + return nil, err + } + return client, nil +} + var mutex = &sync.Mutex{} var maxVmId = 0 @@ -73,3 +84,20 @@ func nextVmId(client *pxapi.Client) (nextId int, err error) { mutex.Unlock() return nextId, nil } + +func resourceId(targetNode string, resType string, vmId int) string { + return fmt.Sprintf("%s/%s/%d", targetNode, resType, vmId) +} + +var rxRsId = regexp.MustCompile("([^/]+)/([^/]+)/(\\d+)") + +func parseResourceId(resId string) (targetNode string, resType string, vmId int, err error) { + idMatch := rxRsId.FindStringSubmatch(resId) + if idMatch == nil { + err = fmt.Errorf("Invalid resource id: %s", resId) + } + targetNode = idMatch[1] + resType = idMatch[2] + vmId, err = strconv.Atoi(idMatch[3]) + return +} diff --git a/proxmox/provisioner.go b/proxmox/provisioner.go new file mode 100644 index 0000000..17b6937 --- /dev/null +++ b/proxmox/provisioner.go @@ -0,0 +1,58 @@ +package proxmox + +import ( + "context" + "fmt" + pxapi "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +func Provisioner() terraform.ResourceProvisioner { + return &schema.Provisioner{ + Schema: map[string]*schema.Schema{ + "action": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "net1": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + + ApplyFunc: applyFn, + } +} + +var currentClient *pxapi.Client = nil + +func applyFn(ctx context.Context) error { + data := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) + state := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) + + connInfo := state.Ephemeral.ConnInfo + + act := data.Get("action").(string) + targetNode, _, vmId, err := parseResourceId(state.ID) + if err != nil { + return err + } + vmr := pxapi.NewVmRef(vmId) + vmr.SetNode(targetNode) + client := currentClient + if client == nil { + client, err = getClient(connInfo["pm_api_url"], connInfo["pm_user"], connInfo["pm_password"]) + if err != nil { + return err + } + currentClient = client + } + switch act { + case "sshbackward": + return pxapi.RemoveSshForwardUsernet(vmr, client) + default: + return fmt.Errorf("Unkown action: %s", act) + } + return nil +} diff --git a/proxmox/resource_vm_qemu.go b/proxmox/resource_vm_qemu.go index 541e3fd..f1a2203 100644 --- a/proxmox/resource_vm_qemu.go +++ b/proxmox/resource_vm_qemu.go @@ -18,6 +18,9 @@ func resourceVmQemu() *schema.Resource { Read: resourceVmQemuRead, Update: resourceVmQemuUpdate, Delete: resourceVmQemuDelete, + Importer: &schema.ResourceImporter{ + State: resourceVmQemuImport, + }, Schema: map[string]*schema.Schema{ "name": { @@ -197,7 +200,7 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error { return err } } - d.SetId(resourceId(targetNode, vmr.VmId())) + d.SetId(resourceId(targetNode, "qemu", vmr.VmId())) log.Print("[DEBUG] starting VM") _, err := client.StartVm(vmr) @@ -216,6 +219,9 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error { "port": sshPort, "user": d.Get("ssh_user").(string), "private_key": d.Get("ssh_private_key").(string), + "pm_api_url": client.ApiUrl, + "pm_user": client.Username, + "pm_password": client.Password, }) switch d.Get("os_type").(string) { @@ -292,7 +298,7 @@ func resourceVmQemuRead(d *schema.ResourceData, meta interface{}) error { if err != nil { return err } - d.SetId(resourceId(vmr.Node(), vmr.VmId())) + d.SetId(resourceId(vmr.Node(), "qemu", vmr.VmId())) d.Set("target_node", vmr.Node()) d.Set("name", config.Name) d.Set("desc", config.Description) @@ -308,6 +314,11 @@ func resourceVmQemuRead(d *schema.ResourceData, meta interface{}) error { return nil } +func resourceVmQemuImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + err := resourceVmQemuRead(d, meta) + return []*schema.ResourceData{d}, err +} + func resourceVmQemuDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*providerConfiguration).Client vmId, _ := strconv.Atoi(path.Base(d.Id())) @@ -320,10 +331,6 @@ func resourceVmQemuDelete(d *schema.ResourceData, meta interface{}) error { return err } -func resourceId(targetNode string, vmId int) string { - return fmt.Sprintf("%s/qemu/%d", targetNode, vmId) -} - func prepareDiskSize(client *pxapi.Client, vmr *pxapi.VmRef, disk_gb float64) error { clonedConfig, err := pxapi.NewConfigQemuFromApi(vmr, client)