Add support for a provisioner to support post-processing actions like unplugging the ssh-forwarding, fake net1, adapter.

This commit is contained in:
Grant Gongaware 2017-05-24 10:18:06 -07:00
parent c2eed29132
commit 2f2c5dff98
5 changed files with 117 additions and 8 deletions

View File

@ -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"
}
}
```

View File

@ -11,5 +11,8 @@ func main() {
ProviderFunc: func() terraform.ResourceProvider {
return proxmox.Provider()
},
ProvisionerFunc: func() terraform.ResourceProvisioner {
return proxmox.Provisioner()
},
})
}

View File

@ -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
}

58
proxmox/provisioner.go Normal file
View File

@ -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
}

View File

@ -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)