mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-06 05:53:58 +00:00
Continue work on container resource
This commit is contained in:
parent
6dac302a01
commit
f6c4ad2af7
40
example/resource_virtual_environment_container.tf
Normal file
40
example/resource_virtual_environment_container.tf
Normal file
@ -0,0 +1,40 @@
|
||||
resource "proxmox_virtual_environment_container" "example" {
|
||||
description = "Managed by Terraform"
|
||||
|
||||
initialization {
|
||||
dns {
|
||||
server = "1.1.1.1"
|
||||
}
|
||||
|
||||
hostname = "terraform-provider-proxmox-example-lxc"
|
||||
|
||||
ip_config {
|
||||
ipv4 {
|
||||
address = "dhcp"
|
||||
}
|
||||
}
|
||||
|
||||
user_account {
|
||||
keys = ["${trimspace(tls_private_key.example.public_key_openssh)}"]
|
||||
password = "example"
|
||||
}
|
||||
}
|
||||
|
||||
network_interface {
|
||||
name = "veth0"
|
||||
}
|
||||
|
||||
node_name = "${data.proxmox_virtual_environment_nodes.example.names[0]}"
|
||||
|
||||
operating_system {
|
||||
template_file_id = "${proxmox_virtual_environment_file.ubuntu_container_template.id}"
|
||||
type = "ubuntu"
|
||||
}
|
||||
|
||||
pool_id = "${proxmox_virtual_environment_pool.example.id}"
|
||||
vm_id = 2039
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_container_example_id" {
|
||||
value = "${proxmox_virtual_environment_container.example.id}"
|
||||
}
|
@ -1,12 +1,6 @@
|
||||
resource "proxmox_virtual_environment_file" "ubuntu_cloud_image" {
|
||||
content_type = "iso"
|
||||
datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}"
|
||||
node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}"
|
||||
|
||||
source_file {
|
||||
path = "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img"
|
||||
}
|
||||
}
|
||||
#===============================================================================
|
||||
# Cloud Config (cloud-init)
|
||||
#===============================================================================
|
||||
|
||||
resource "proxmox_virtual_environment_file" "cloud_config" {
|
||||
content_type = "snippets"
|
||||
@ -37,6 +31,20 @@ users:
|
||||
}
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# Ubuntu Cloud Image
|
||||
#===============================================================================
|
||||
|
||||
resource "proxmox_virtual_environment_file" "ubuntu_cloud_image" {
|
||||
content_type = "iso"
|
||||
datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}"
|
||||
node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}"
|
||||
|
||||
source_file {
|
||||
path = "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img"
|
||||
}
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_cloud_image_content_type" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_cloud_image.content_type}"
|
||||
}
|
||||
@ -72,3 +80,53 @@ output "resource_proxmox_virtual_environment_file_ubuntu_cloud_image_node_name"
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_cloud_image_source_file" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_cloud_image.source_file}"
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# Ubuntu Container Template
|
||||
#===============================================================================
|
||||
|
||||
resource "proxmox_virtual_environment_file" "ubuntu_container_template" {
|
||||
content_type = "vztmpl"
|
||||
datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}"
|
||||
node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}"
|
||||
|
||||
source_file {
|
||||
path = "http://download.proxmox.com/images/system/ubuntu-18.04-standard_18.04.1-1_amd64.tar.gz"
|
||||
}
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_content_type" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.content_type}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_datastore_id" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.datastore_id}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_file_modification_date" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.file_modification_date}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_file_name" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.file_name}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_file_size" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.file_size}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_file_tag" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.file_tag}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_id" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.id}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_node_name" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.node_name}"
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_file_ubuntu_container_template_source_file" {
|
||||
value = "${proxmox_virtual_environment_file.ubuntu_container_template.source_file}"
|
||||
}
|
||||
|
@ -58,21 +58,6 @@ resource "proxmox_virtual_environment_vm" "example" {
|
||||
}
|
||||
}
|
||||
|
||||
resource "local_file" "example_ssh_private_key" {
|
||||
filename = "${path.module}/autogenerated/id_rsa"
|
||||
sensitive_content = "${tls_private_key.example.private_key_pem}"
|
||||
}
|
||||
|
||||
resource "local_file" "example_ssh_public_key" {
|
||||
filename = "${path.module}/autogenerated/id_rsa.pub"
|
||||
sensitive_content = "${tls_private_key.example.public_key_openssh}"
|
||||
}
|
||||
|
||||
resource "tls_private_key" "example" {
|
||||
algorithm = "RSA"
|
||||
rsa_bits = 2048
|
||||
}
|
||||
|
||||
output "resource_proxmox_virtual_environment_vm_example_id" {
|
||||
value = "${proxmox_virtual_environment_vm.example.id}"
|
||||
}
|
||||
|
14
example/ssh.tf
Normal file
14
example/ssh.tf
Normal file
@ -0,0 +1,14 @@
|
||||
resource "local_file" "example_ssh_private_key" {
|
||||
filename = "${path.module}/autogenerated/id_rsa"
|
||||
sensitive_content = "${tls_private_key.example.private_key_pem}"
|
||||
}
|
||||
|
||||
resource "local_file" "example_ssh_public_key" {
|
||||
filename = "${path.module}/autogenerated/id_rsa.pub"
|
||||
sensitive_content = "${tls_private_key.example.public_key_openssh}"
|
||||
}
|
||||
|
||||
resource "tls_private_key" "example" {
|
||||
algorithm = "RSA"
|
||||
rsa_bits = 2048
|
||||
}
|
1
go.mod
1
go.mod
@ -8,4 +8,5 @@ require (
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/pkg/sftp v1.10.1
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55
|
||||
)
|
||||
|
@ -110,3 +110,33 @@ func (c *VirtualEnvironmentClient) WaitForContainerState(nodeName string, vmID i
|
||||
|
||||
return fmt.Errorf("Timeout while waiting for container \"%d\" to enter the state \"%s\"", vmID, state)
|
||||
}
|
||||
|
||||
// WaitForContainerLock waits for a container lock to be released.
|
||||
func (c *VirtualEnvironmentClient) WaitForContainerLock(nodeName string, vmID int, timeout int, delay int) error {
|
||||
timeDelay := int64(delay)
|
||||
timeMax := float64(timeout)
|
||||
timeStart := time.Now()
|
||||
timeElapsed := timeStart.Sub(timeStart)
|
||||
|
||||
for timeElapsed.Seconds() < timeMax {
|
||||
if int64(timeElapsed.Seconds())%timeDelay == 0 {
|
||||
data, err := c.GetContainerStatus(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if data.Lock == nil || *data.Lock == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
timeElapsed = time.Now().Sub(timeStart)
|
||||
}
|
||||
|
||||
return fmt.Errorf("Timeout while waiting for container \"%d\" to become unlocked", vmID)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ type VirtualEnvironmentContainerCreateRequestBody struct {
|
||||
Lock *string `json:"lock,omitempty" url:"lock,omitempty,int"`
|
||||
MountPoints VirtualEnvironmentContainerCustomMountPointArray `json:"mp,omitempty" url:"mp,omitempty,numbered"`
|
||||
NetworkInterfaces VirtualEnvironmentContainerCustomNetworkInterfaceArray `json:"net,omitempty" url:"net,omitempty,numbered"`
|
||||
OSTemplateFileVolume string `json:"ostemplate" url:"ostemplate"`
|
||||
OSTemplateFileVolume *string `json:"ostemplate,omitempty" url:"ostemplate,omitempty"`
|
||||
OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"`
|
||||
Password *string `json:"password,omitempty" url:"password,omitempty"`
|
||||
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
|
||||
@ -51,7 +51,7 @@ type VirtualEnvironmentContainerCreateRequestBody struct {
|
||||
TTY *int `json:"tty,omitempty" url:"tty,omitempty"`
|
||||
Unique *CustomBool `json:"unique,omitempty" url:"unique,omitempty,int"`
|
||||
Unprivileged *CustomBool `json:"unprivileged,omitempty" url:"unprivileged,omitempty,int"`
|
||||
VMID int `json:"vmid" url:"vmid"`
|
||||
VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"`
|
||||
}
|
||||
|
||||
// VirtualEnvironmentContainerCustomFeatures contains the values for the "features" property.
|
||||
@ -66,7 +66,7 @@ type VirtualEnvironmentContainerCustomFeatures struct {
|
||||
type VirtualEnvironmentContainerCustomMountPoint struct {
|
||||
ACL *CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"`
|
||||
Backup *CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"`
|
||||
DiskSize *int `json:"size,omitempty" url:"size,omitempty"`
|
||||
DiskSize *string `json:"size,omitempty" url:"size,omitempty"`
|
||||
Enabled bool `json:"-" url:"-"`
|
||||
MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"`
|
||||
MountPoint string `json:"mp" url:"mp"`
|
||||
@ -92,7 +92,7 @@ type VirtualEnvironmentContainerCustomNetworkInterface struct {
|
||||
MACAddress *string `json:"hwaddr,omitempty" url:"hwaddr,omitempty"`
|
||||
MTU *int `json:"mtu,omitempty" url:"mtu,omitempty"`
|
||||
Name string `json:"name" url:"name"`
|
||||
RateLimit *int `json:"rate,omitempty" url:"rate,omitempty"`
|
||||
RateLimit *float64 `json:"rate,omitempty" url:"rate,omitempty"`
|
||||
Tag *int `json:"tag,omitempty" url:"tag,omitempty"`
|
||||
Trunks *[]int `json:"trunks,omitempty" url:"trunks,omitempty"`
|
||||
Type *string `json:"type,omitempty" url:"type,omitempty"`
|
||||
@ -104,7 +104,7 @@ type VirtualEnvironmentContainerCustomNetworkInterfaceArray []VirtualEnvironment
|
||||
// VirtualEnvironmentContainerCustomRootFS contains the values for the "rootfs" property.
|
||||
type VirtualEnvironmentContainerCustomRootFS struct {
|
||||
ACL *CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"`
|
||||
DiskSize *int `json:"size,omitempty" url:"size,omitempty"`
|
||||
DiskSize *string `json:"size,omitempty" url:"size,omitempty"`
|
||||
MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"`
|
||||
Quota *CustomBool `json:"quota,omitempty" url:"quota,omitempty,int"`
|
||||
ReadOnly *CustomBool `json:"ro,omitempty" url:"ro,omitempty,int"`
|
||||
@ -177,16 +177,16 @@ type VirtualEnvironmentContainerGetStatusResponseBody struct {
|
||||
|
||||
// VirtualEnvironmentContainerGetStatusResponseData contains the data from a container get status response.
|
||||
type VirtualEnvironmentContainerGetStatusResponseData struct {
|
||||
CPUCount *float64 `json:"cpus,omitempty"`
|
||||
Lock *string `json:"lock,omitempty"`
|
||||
MemoryAllocation *int `json:"maxmem,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
RootDiskSize *int `json:"maxdisk,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
SwapAllocation *int `json:"maxswap,omitempty"`
|
||||
Tags *string `json:"tags,omitempty"`
|
||||
Uptime *int `json:"uptime,omitempty"`
|
||||
VMID string `json:"vmid,omitempty"`
|
||||
CPUCount *float64 `json:"cpus,omitempty"`
|
||||
Lock *string `json:"lock,omitempty"`
|
||||
MemoryAllocation *int `json:"maxmem,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
RootDiskSize *interface{} `json:"maxdisk,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
SwapAllocation *int `json:"maxswap,omitempty"`
|
||||
Tags *string `json:"tags,omitempty"`
|
||||
Uptime *int `json:"uptime,omitempty"`
|
||||
VMID string `json:"vmid,omitempty"`
|
||||
}
|
||||
|
||||
// VirtualEnvironmentContainerRebootRequestBody contains the body for a container reboot request.
|
||||
@ -265,7 +265,7 @@ func (r VirtualEnvironmentContainerCustomMountPoint) EncodeValues(key string, v
|
||||
}
|
||||
|
||||
if r.DiskSize != nil {
|
||||
values = append(values, fmt.Sprintf("size=%d", *r.DiskSize))
|
||||
values = append(values, fmt.Sprintf("size=%s", *r.DiskSize))
|
||||
}
|
||||
|
||||
if r.MountOptions != nil {
|
||||
@ -317,6 +317,15 @@ func (r VirtualEnvironmentContainerCustomMountPoint) EncodeValues(key string, v
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeValues converts a VirtualEnvironmentContainerCustomMountPointArray array to multiple URL values.
|
||||
func (r VirtualEnvironmentContainerCustomMountPointArray) EncodeValues(key string, v *url.Values) error {
|
||||
for i, d := range r {
|
||||
d.EncodeValues(fmt.Sprintf("%s%d", key, i), v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeValues converts a VirtualEnvironmentContainerCustomNetworkInterface struct to a URL vlaue.
|
||||
func (r VirtualEnvironmentContainerCustomNetworkInterface) EncodeValues(key string, v *url.Values) error {
|
||||
values := []string{}
|
||||
@ -360,7 +369,7 @@ func (r VirtualEnvironmentContainerCustomNetworkInterface) EncodeValues(key stri
|
||||
values = append(values, fmt.Sprintf("name=%s", r.Name))
|
||||
|
||||
if r.RateLimit != nil {
|
||||
values = append(values, fmt.Sprintf("rate=%d", *r.RateLimit))
|
||||
values = append(values, fmt.Sprintf("rate=%.2f", *r.RateLimit))
|
||||
}
|
||||
|
||||
if r.Tag != nil {
|
||||
@ -388,6 +397,15 @@ func (r VirtualEnvironmentContainerCustomNetworkInterface) EncodeValues(key stri
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeValues converts a VirtualEnvironmentContainerCustomNetworkInterfaceArray array to multiple URL values.
|
||||
func (r VirtualEnvironmentContainerCustomNetworkInterfaceArray) EncodeValues(key string, v *url.Values) error {
|
||||
for i, d := range r {
|
||||
d.EncodeValues(fmt.Sprintf("%s%d", key, i), v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeValues converts a VirtualEnvironmentContainerCustomRootFS struct to a URL vlaue.
|
||||
func (r VirtualEnvironmentContainerCustomRootFS) EncodeValues(key string, v *url.Values) error {
|
||||
values := []string{}
|
||||
@ -401,7 +419,7 @@ func (r VirtualEnvironmentContainerCustomRootFS) EncodeValues(key string, v *url
|
||||
}
|
||||
|
||||
if r.DiskSize != nil {
|
||||
values = append(values, fmt.Sprintf("size=%d", *r.DiskSize))
|
||||
values = append(values, fmt.Sprintf("size=%s", *r.DiskSize))
|
||||
}
|
||||
|
||||
if r.MountOptions != nil {
|
||||
@ -453,7 +471,7 @@ func (r VirtualEnvironmentContainerCustomRootFS) EncodeValues(key string, v *url
|
||||
|
||||
// EncodeValues converts a VirtualEnvironmentContainerCustomSSHKeys array to a URL vlaue.
|
||||
func (r VirtualEnvironmentContainerCustomSSHKeys) EncodeValues(key string, v *url.Values) error {
|
||||
v.Add(key, strings.ReplaceAll(url.QueryEscape(strings.Join(r, "\n")), "+", "%20"))
|
||||
v.Add(key, strings.Join(r, "\n"))
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -538,7 +556,7 @@ func (r *VirtualEnvironmentContainerCustomMountPoint) UnmarshalJSON(b []byte) er
|
||||
v := strings.Split(strings.TrimSpace(p), "=")
|
||||
|
||||
if len(v) == 1 {
|
||||
r.Volume = v[1]
|
||||
r.Volume = v[0]
|
||||
} else if len(v) == 2 {
|
||||
switch v[0] {
|
||||
case "acl":
|
||||
@ -570,13 +588,7 @@ func (r *VirtualEnvironmentContainerCustomMountPoint) UnmarshalJSON(b []byte) er
|
||||
bv := CustomBool(v[1] == "1")
|
||||
r.Shared = &bv
|
||||
case "size":
|
||||
iv, err := strconv.Atoi(v[1])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.DiskSize = &iv
|
||||
r.DiskSize = &v[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -600,7 +612,7 @@ func (r *VirtualEnvironmentContainerCustomNetworkInterface) UnmarshalJSON(b []by
|
||||
v := strings.Split(strings.TrimSpace(p), "=")
|
||||
|
||||
if len(v) == 1 {
|
||||
r.Name = v[1]
|
||||
r.Name = v[0]
|
||||
} else if len(v) == 2 {
|
||||
switch v[0] {
|
||||
case "bridge":
|
||||
@ -629,13 +641,13 @@ func (r *VirtualEnvironmentContainerCustomNetworkInterface) UnmarshalJSON(b []by
|
||||
case "name":
|
||||
r.Name = v[1]
|
||||
case "rate":
|
||||
iv, err := strconv.Atoi(v[1])
|
||||
fv, err := strconv.ParseFloat(v[1], 64)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.RateLimit = &iv
|
||||
r.RateLimit = &fv
|
||||
case "tag":
|
||||
iv, err := strconv.Atoi(v[1])
|
||||
|
||||
@ -687,7 +699,7 @@ func (r *VirtualEnvironmentContainerCustomRootFS) UnmarshalJSON(b []byte) error
|
||||
v := strings.Split(strings.TrimSpace(p), "=")
|
||||
|
||||
if len(v) == 1 {
|
||||
r.Volume = v[1]
|
||||
r.Volume = v[0]
|
||||
} else if len(v) == 2 {
|
||||
switch v[0] {
|
||||
case "acl":
|
||||
@ -714,13 +726,7 @@ func (r *VirtualEnvironmentContainerCustomRootFS) UnmarshalJSON(b []byte) error
|
||||
bv := CustomBool(v[1] == "1")
|
||||
r.Shared = &bv
|
||||
case "size":
|
||||
iv, err := strconv.Atoi(v[1])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.DiskSize = &iv
|
||||
r.DiskSize = &v[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ func Provider() *schema.Provider {
|
||||
},
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"proxmox_virtual_environment_certificate": resourceVirtualEnvironmentCertificate(),
|
||||
"proxmox_virtual_environment_container": resourceVirtualEnvironmentContainer(),
|
||||
"proxmox_virtual_environment_dns": resourceVirtualEnvironmentDNS(),
|
||||
"proxmox_virtual_environment_file": resourceVirtualEnvironmentFile(),
|
||||
"proxmox_virtual_environment_group": resourceVirtualEnvironmentGroup(),
|
||||
|
@ -5,6 +5,7 @@
|
||||
package proxmoxtf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -77,7 +78,7 @@ const (
|
||||
mkResourceVirtualEnvironmentContainerMemory = "memory"
|
||||
mkResourceVirtualEnvironmentContainerMemoryDedicated = "dedicated"
|
||||
mkResourceVirtualEnvironmentContainerMemorySwap = "swap"
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterface = "network_device"
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterface = "network_interface"
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceBridge = "bridge"
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceEnabled = "enabled"
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceMACAddress = "mac_address"
|
||||
@ -324,7 +325,7 @@ func resourceVirtualEnvironmentContainer() *schema.Resource {
|
||||
Sensitive: true,
|
||||
Default: dvResourceVirtualEnvironmentContainerInitializationUserAccountPassword,
|
||||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||||
return strings.ReplaceAll(old, "*", "") == ""
|
||||
return len(old) > 0 && strings.ReplaceAll(old, "*", "") == ""
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -459,7 +460,7 @@ func resourceVirtualEnvironmentContainer() *schema.Resource {
|
||||
},
|
||||
mkResourceVirtualEnvironmentContainerPoolID: {
|
||||
Type: schema.TypeString,
|
||||
Description: "The ID of the pool to assign the virtual machine to",
|
||||
Description: "The ID of the pool to assign the container to",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: dvResourceVirtualEnvironmentContainerPoolID,
|
||||
@ -576,7 +577,7 @@ func resourceVirtualEnvironmentContainerCreate(d *schema.ResourceData, m interfa
|
||||
initializationUserAccountBlock := initializationUserAccount[0].(map[string]interface{})
|
||||
|
||||
keys := initializationUserAccountBlock[mkResourceVirtualEnvironmentContainerInitializationUserAccountKeys].([]interface{})
|
||||
initializationUserAccountKeys := make(proxmox.VirtualEnvironmentContainerCustomSSHKeys, len(keys))
|
||||
initializationUserAccountKeys = make(proxmox.VirtualEnvironmentContainerCustomSSHKeys, len(keys))
|
||||
|
||||
for ki, kv := range keys {
|
||||
initializationUserAccountKeys[ki] = kv.(string)
|
||||
@ -606,7 +607,7 @@ func resourceVirtualEnvironmentContainerCreate(d *schema.ResourceData, m interfa
|
||||
enabled := networkInterfaceMap[mkResourceVirtualEnvironmentContainerNetworkInterfaceEnabled].(bool)
|
||||
macAddress := networkInterfaceMap[mkResourceVirtualEnvironmentContainerNetworkInterfaceMACAddress].(string)
|
||||
name := networkInterfaceMap[mkResourceVirtualEnvironmentContainerNetworkInterfaceName].(string)
|
||||
rateLimit := networkInterfaceMap[mkResourceVirtualEnvironmentContainerNetworkInterfaceRateLimit].(int)
|
||||
rateLimit := networkInterfaceMap[mkResourceVirtualEnvironmentContainerNetworkInterfaceRateLimit].(float64)
|
||||
vlanID := networkInterfaceMap[mkResourceVirtualEnvironmentContainerNetworkInterfaceVLANID].(int)
|
||||
|
||||
if bridge != "" {
|
||||
@ -637,9 +638,7 @@ func resourceVirtualEnvironmentContainerCreate(d *schema.ResourceData, m interfa
|
||||
networkInterfaceObject.MACAddress = &macAddress
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
networkInterfaceObject.Name = name
|
||||
}
|
||||
networkInterfaceObject.Name = name
|
||||
|
||||
if rateLimit != 0 {
|
||||
networkInterfaceObject.RateLimit = &rateLimit
|
||||
@ -676,20 +675,23 @@ func resourceVirtualEnvironmentContainerCreate(d *schema.ResourceData, m interfa
|
||||
}
|
||||
|
||||
// Attempt to create the resource using the retrieved values.
|
||||
datastoreID := "local-lvm"
|
||||
|
||||
body := proxmox.VirtualEnvironmentContainerCreateRequestBody{
|
||||
ConsoleEnabled: &consoleEnabled,
|
||||
ConsoleMode: &consoleMode,
|
||||
CPUArchitecture: &cpuArchitecture,
|
||||
CPUCores: &cpuCores,
|
||||
CPUUnits: &cpuUnits,
|
||||
DatastoreID: &datastoreID,
|
||||
DedicatedMemory: &memoryDedicated,
|
||||
NetworkInterfaces: networkInterfaceArray,
|
||||
OSTemplateFileVolume: operatingSystemTemplateFileID,
|
||||
OSTemplateFileVolume: &operatingSystemTemplateFileID,
|
||||
OSType: &operatingSystemType,
|
||||
StartOnBoot: &started,
|
||||
Swap: &memorySwap,
|
||||
TTY: &consoleTTYCount,
|
||||
VMID: vmID,
|
||||
VMID: &vmID,
|
||||
}
|
||||
|
||||
if description != "" {
|
||||
@ -728,6 +730,13 @@ func resourceVirtualEnvironmentContainerCreate(d *schema.ResourceData, m interfa
|
||||
|
||||
d.SetId(strconv.Itoa(vmID))
|
||||
|
||||
// Wait for the container's lock to be released.
|
||||
err = veClient.WaitForContainerLock(nodeName, vmID, 300, 5)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return resourceVirtualEnvironmentContainerCreateStart(d, m)
|
||||
}
|
||||
|
||||
@ -815,10 +824,11 @@ func resourceVirtualEnvironmentContainerRead(d *schema.ResourceData, m interface
|
||||
}
|
||||
|
||||
// Retrieve the entire configuration in order to compare it to the state.
|
||||
_, err = veClient.GetContainer(nodeName, vmID)
|
||||
containerConfig, err := veClient.GetContainer(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "HTTP 404") {
|
||||
if strings.Contains(err.Error(), "HTTP 404") ||
|
||||
(strings.Contains(err.Error(), "HTTP 500") && strings.Contains(err.Error(), "does not exist")) {
|
||||
d.SetId("")
|
||||
|
||||
return nil
|
||||
@ -827,6 +837,276 @@ func resourceVirtualEnvironmentContainerRead(d *schema.ResourceData, m interface
|
||||
return err
|
||||
}
|
||||
|
||||
// Compare the primitive values to those stored in the state.
|
||||
if containerConfig.Description != nil {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerDescription, strings.TrimSpace(*containerConfig.Description))
|
||||
} else {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerDescription, "")
|
||||
}
|
||||
|
||||
// Compare the console configuration to the one stored in the state.
|
||||
console := map[string]interface{}{}
|
||||
|
||||
if containerConfig.ConsoleEnabled != nil {
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleEnabled] = *containerConfig.ConsoleEnabled
|
||||
} else {
|
||||
// Default value of "console" is "1" according to the API documentation.
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleEnabled] = true
|
||||
}
|
||||
|
||||
if containerConfig.ConsoleMode != nil {
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleMode] = *containerConfig.ConsoleMode
|
||||
} else {
|
||||
// Default value of "cmode" is "tty" according to the API documentation.
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleMode] = "tty"
|
||||
}
|
||||
|
||||
if containerConfig.TTY != nil {
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleTTYCount] = *containerConfig.TTY
|
||||
} else {
|
||||
// Default value of "tty" is "2" according to the API documentation.
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleTTYCount] = 2
|
||||
}
|
||||
|
||||
currentConsole := d.Get(mkResourceVirtualEnvironmentContainerConsole).([]interface{})
|
||||
|
||||
if len(currentConsole) > 0 ||
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleEnabled] != proxmox.CustomBool(dvResourceVirtualEnvironmentContainerConsoleEnabled) ||
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleMode] != dvResourceVirtualEnvironmentContainerConsoleMode ||
|
||||
console[mkResourceVirtualEnvironmentContainerConsoleTTYCount] != dvResourceVirtualEnvironmentContainerConsoleTTYCount {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerConsole, []interface{}{console})
|
||||
}
|
||||
|
||||
// Compare the CPU configuration to the one stored in the state.
|
||||
cpu := map[string]interface{}{}
|
||||
|
||||
if containerConfig.CPUArchitecture != nil {
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUArchitecture] = *containerConfig.CPUArchitecture
|
||||
} else {
|
||||
// Default value of "arch" is "amd64" according to the API documentation.
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUArchitecture] = "amd64"
|
||||
}
|
||||
|
||||
if containerConfig.CPUCores != nil {
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUCores] = *containerConfig.CPUCores
|
||||
} else {
|
||||
// Default value of "cores" is "1" according to the API documentation.
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUCores] = 1
|
||||
}
|
||||
|
||||
if containerConfig.CPUUnits != nil {
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUUnits] = *containerConfig.CPUUnits
|
||||
} else {
|
||||
// Default value of "cpuunits" is "1024" according to the API documentation.
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUUnits] = 1024
|
||||
}
|
||||
|
||||
currentCPU := d.Get(mkResourceVirtualEnvironmentContainerCPU).([]interface{})
|
||||
|
||||
if len(currentCPU) > 0 ||
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUArchitecture] != dvResourceVirtualEnvironmentContainerCPUArchitecture ||
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUCores] != dvResourceVirtualEnvironmentContainerCPUCores ||
|
||||
cpu[mkResourceVirtualEnvironmentContainerCPUUnits] != dvResourceVirtualEnvironmentContainerCPUUnits {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerCPU, []interface{}{cpu})
|
||||
}
|
||||
|
||||
// Compare the memory configuration to the one stored in the state.
|
||||
memory := map[string]interface{}{}
|
||||
|
||||
if containerConfig.DedicatedMemory != nil {
|
||||
memory[mkResourceVirtualEnvironmentContainerMemoryDedicated] = *containerConfig.DedicatedMemory
|
||||
} else {
|
||||
memory[mkResourceVirtualEnvironmentContainerMemoryDedicated] = 0
|
||||
}
|
||||
|
||||
if containerConfig.Swap != nil {
|
||||
memory[mkResourceVirtualEnvironmentContainerMemorySwap] = *containerConfig.Swap
|
||||
} else {
|
||||
memory[mkResourceVirtualEnvironmentContainerMemorySwap] = 0
|
||||
}
|
||||
|
||||
currentMemory := d.Get(mkResourceVirtualEnvironmentContainerMemory).([]interface{})
|
||||
|
||||
if len(currentMemory) > 0 ||
|
||||
memory[mkResourceVirtualEnvironmentContainerMemoryDedicated] != dvResourceVirtualEnvironmentContainerMemoryDedicated ||
|
||||
memory[mkResourceVirtualEnvironmentContainerMemorySwap] != dvResourceVirtualEnvironmentContainerMemorySwap {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerMemory, []interface{}{memory})
|
||||
}
|
||||
|
||||
// Compare the initialization and network interface configuration to the one stored in the state.
|
||||
initialization := map[string]interface{}{}
|
||||
|
||||
if containerConfig.DNSDomain != nil || containerConfig.DNSServer != nil {
|
||||
initializationDNS := map[string]interface{}{}
|
||||
|
||||
if containerConfig.DNSDomain != nil {
|
||||
initializationDNS[mkResourceVirtualEnvironmentContainerInitializationDNSDomain] = *containerConfig.DNSDomain
|
||||
} else {
|
||||
initializationDNS[mkResourceVirtualEnvironmentContainerInitializationDNSDomain] = ""
|
||||
}
|
||||
|
||||
if containerConfig.DNSServer != nil {
|
||||
initializationDNS[mkResourceVirtualEnvironmentContainerInitializationDNSServer] = *containerConfig.DNSServer
|
||||
} else {
|
||||
initializationDNS[mkResourceVirtualEnvironmentContainerInitializationDNSServer] = ""
|
||||
}
|
||||
|
||||
initialization[mkResourceVirtualEnvironmentContainerInitializationDNS] = []interface{}{initializationDNS}
|
||||
}
|
||||
|
||||
if containerConfig.Hostname != nil {
|
||||
initialization[mkResourceVirtualEnvironmentContainerInitializationHostname] = *containerConfig.Hostname
|
||||
} else {
|
||||
initialization[mkResourceVirtualEnvironmentContainerInitializationHostname] = ""
|
||||
}
|
||||
|
||||
ipConfigList := []interface{}{}
|
||||
networkInterfaceArray := []*proxmox.VirtualEnvironmentContainerCustomNetworkInterface{
|
||||
containerConfig.NetworkInterface0,
|
||||
containerConfig.NetworkInterface1,
|
||||
containerConfig.NetworkInterface2,
|
||||
containerConfig.NetworkInterface3,
|
||||
containerConfig.NetworkInterface4,
|
||||
containerConfig.NetworkInterface5,
|
||||
containerConfig.NetworkInterface6,
|
||||
containerConfig.NetworkInterface7,
|
||||
}
|
||||
networkInterfaceList := []interface{}{}
|
||||
|
||||
for _, nv := range networkInterfaceArray {
|
||||
if nv == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if nv.IPv4Address != nil || nv.IPv4Gateway != nil || nv.IPv6Address != nil || nv.IPv6Gateway != nil {
|
||||
ipConfig := map[string]interface{}{}
|
||||
|
||||
if nv.IPv4Address != nil || nv.IPv4Gateway != nil {
|
||||
ip := map[string]interface{}{}
|
||||
|
||||
if nv.IPv4Address != nil {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Address] = *nv.IPv4Address
|
||||
} else {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Address] = ""
|
||||
}
|
||||
|
||||
if nv.IPv4Gateway != nil {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Gateway] = *nv.IPv4Gateway
|
||||
} else {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Gateway] = ""
|
||||
}
|
||||
|
||||
ipConfig[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4] = []interface{}{ip}
|
||||
} else {
|
||||
ipConfig[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4] = []interface{}{}
|
||||
}
|
||||
|
||||
if nv.IPv6Address != nil || nv.IPv6Gateway != nil {
|
||||
ip := map[string]interface{}{}
|
||||
|
||||
if nv.IPv6Address != nil {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Address] = *nv.IPv6Address
|
||||
} else {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Address] = ""
|
||||
}
|
||||
|
||||
if nv.IPv6Gateway != nil {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Gateway] = *nv.IPv6Gateway
|
||||
} else {
|
||||
ip[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Gateway] = ""
|
||||
}
|
||||
|
||||
ipConfig[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6] = []interface{}{ip}
|
||||
} else {
|
||||
ipConfig[mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6] = []interface{}{}
|
||||
}
|
||||
|
||||
ipConfigList = append(ipConfigList, ipConfig)
|
||||
}
|
||||
|
||||
networkInterface := map[string]interface{}{}
|
||||
|
||||
if nv.Bridge != nil {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceBridge] = *nv.Bridge
|
||||
} else {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceBridge] = ""
|
||||
}
|
||||
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceEnabled] = true
|
||||
|
||||
if nv.MACAddress != nil {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceMACAddress] = *nv.MACAddress
|
||||
} else {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceMACAddress] = ""
|
||||
}
|
||||
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceName] = nv.Name
|
||||
|
||||
if nv.RateLimit != nil {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceRateLimit] = *nv.RateLimit
|
||||
} else {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceRateLimit] = 0
|
||||
}
|
||||
|
||||
if nv.Tag != nil {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceVLANID] = *nv.Tag
|
||||
} else {
|
||||
networkInterface[mkResourceVirtualEnvironmentContainerNetworkInterfaceVLANID] = 0
|
||||
}
|
||||
|
||||
networkInterfaceList = append(networkInterfaceList, networkInterface)
|
||||
}
|
||||
|
||||
initialization[mkResourceVirtualEnvironmentContainerInitializationIPConfig] = ipConfigList
|
||||
|
||||
currentInitialization := d.Get(mkResourceVirtualEnvironmentContainerInitialization).([]interface{})
|
||||
|
||||
if len(currentInitialization) > 0 {
|
||||
currentInitializationMap := currentInitialization[0].(map[string]interface{})
|
||||
|
||||
initialization[mkResourceVirtualEnvironmentContainerInitializationUserAccount] = currentInitializationMap[mkResourceVirtualEnvironmentContainerInitializationUserAccount].([]interface{})
|
||||
}
|
||||
|
||||
if len(initialization) > 0 {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerInitialization, []interface{}{initialization})
|
||||
} else {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerInitialization, []interface{}{})
|
||||
}
|
||||
|
||||
d.Set(mkResourceVirtualEnvironmentContainerNetworkInterface, networkInterfaceList)
|
||||
|
||||
// Compare the operating system configuration to the one stored in the state.
|
||||
operatingSystem := map[string]interface{}{}
|
||||
|
||||
if containerConfig.OSType != nil {
|
||||
operatingSystem[mkResourceVirtualEnvironmentContainerOperatingSystemType] = *containerConfig.OSType
|
||||
} else {
|
||||
// Default value of "ostype" is "" according to the API documentation.
|
||||
operatingSystem[mkResourceVirtualEnvironmentContainerOperatingSystemType] = ""
|
||||
}
|
||||
|
||||
currentOperatingSystem := d.Get(mkResourceVirtualEnvironmentContainerOperatingSystem).([]interface{})
|
||||
|
||||
if len(currentOperatingSystem) > 0 {
|
||||
currentOperatingSystemMap := currentOperatingSystem[0].(map[string]interface{})
|
||||
|
||||
operatingSystem[mkResourceVirtualEnvironmentContainerOperatingSystemTemplateFileID] = currentOperatingSystemMap[mkResourceVirtualEnvironmentContainerOperatingSystemTemplateFileID]
|
||||
}
|
||||
|
||||
if len(currentOperatingSystem) > 0 ||
|
||||
operatingSystem[mkResourceVirtualEnvironmentContainerOperatingSystemType] != dvResourceVirtualEnvironmentContainerOperatingSystemType {
|
||||
d.Set(mkResourceVirtualEnvironmentContainerOperatingSystem, []interface{}{operatingSystem})
|
||||
}
|
||||
|
||||
// Determine the state of the container in order to update the "started" argument.
|
||||
status, err := veClient.GetContainerStatus(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set(mkResourceVirtualEnvironmentContainerStarted, status.Status == "running")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -845,13 +1125,132 @@ func resourceVirtualEnvironmentContainerUpdate(d *schema.ResourceData, m interfa
|
||||
return err
|
||||
}
|
||||
|
||||
// Retrieve the entire configuration as we need to process certain values.
|
||||
_, err = veClient.GetContainer(nodeName, vmID)
|
||||
// Prepare the new request object.
|
||||
body := proxmox.VirtualEnvironmentContainerUpdateRequestBody{}
|
||||
rebootRequired := false
|
||||
resource := resourceVirtualEnvironmentContainer()
|
||||
|
||||
// Prepare the new primitive values.
|
||||
if d.HasChange(mkResourceVirtualEnvironmentContainerDescription) {
|
||||
description := d.Get(mkResourceVirtualEnvironmentContainerDescription).(string)
|
||||
|
||||
body.Description = &description
|
||||
}
|
||||
|
||||
// Prepare the new console configuration.
|
||||
if d.HasChange(mkResourceVirtualEnvironmentContainerConsole) {
|
||||
consoleBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentContainerConsole}, 0, true)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
consoleEnabled := proxmox.CustomBool(consoleBlock[mkResourceVirtualEnvironmentContainerConsoleEnabled].(bool))
|
||||
consoleMode := consoleBlock[mkResourceVirtualEnvironmentContainerConsoleMode].(string)
|
||||
consoleTTYCount := consoleBlock[mkResourceVirtualEnvironmentContainerConsoleTTYCount].(int)
|
||||
|
||||
body.ConsoleEnabled = &consoleEnabled
|
||||
body.ConsoleMode = &consoleMode
|
||||
body.TTY = &consoleTTYCount
|
||||
|
||||
rebootRequired = true
|
||||
}
|
||||
|
||||
// Prepare the new CPU configuration.
|
||||
if d.HasChange(mkResourceVirtualEnvironmentContainerCPU) {
|
||||
cpuBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentContainerCPU}, 0, true)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cpuArchitecture := cpuBlock[mkResourceVirtualEnvironmentContainerCPUArchitecture].(string)
|
||||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentContainerCPUCores].(int)
|
||||
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentContainerCPUUnits].(int)
|
||||
|
||||
body.CPUArchitecture = &cpuArchitecture
|
||||
body.CPUCores = &cpuCores
|
||||
body.CPUUnits = &cpuUnits
|
||||
|
||||
rebootRequired = true
|
||||
}
|
||||
|
||||
// Prepare the new memory configuration.
|
||||
if d.HasChange(mkResourceVirtualEnvironmentContainerMemory) {
|
||||
memoryBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentContainerMemory}, 0, true)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentContainerMemoryDedicated].(int)
|
||||
memorySwap := memoryBlock[mkResourceVirtualEnvironmentContainerMemorySwap].(int)
|
||||
|
||||
body.DedicatedMemory = &memoryDedicated
|
||||
body.Swap = &memorySwap
|
||||
|
||||
rebootRequired = true
|
||||
}
|
||||
|
||||
// Update the configuration now that everything has been prepared.
|
||||
err = veClient.UpdateContainer(nodeName, vmID, &body)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Determine if the state of the container needs to be changed.
|
||||
if d.HasChange(mkResourceVirtualEnvironmentContainerStarted) {
|
||||
started := d.Get(mkResourceVirtualEnvironmentContainerStarted).(bool)
|
||||
|
||||
if started {
|
||||
err = veClient.StartContainer(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = veClient.WaitForContainerState(nodeName, vmID, "running", 120, 5)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
forceStop := proxmox.CustomBool(true)
|
||||
shutdownTimeout := 300
|
||||
|
||||
err = veClient.ShutdownContainer(nodeName, vmID, &proxmox.VirtualEnvironmentContainerShutdownRequestBody{
|
||||
ForceStop: &forceStop,
|
||||
Timeout: &shutdownTimeout,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = veClient.WaitForContainerState(nodeName, vmID, "stopped", 30, 5)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rebootRequired = false
|
||||
}
|
||||
}
|
||||
|
||||
// As a final step in the update procedure, we might need to reboot the virtual machine.
|
||||
if rebootRequired {
|
||||
rebootTimeout := 300
|
||||
|
||||
err = veClient.RebootContainer(nodeName, vmID, &proxmox.VirtualEnvironmentContainerRebootRequestBody{
|
||||
Timeout: &rebootTimeout,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return resourceVirtualEnvironmentContainerRead(d, m)
|
||||
}
|
||||
|
||||
@ -870,6 +1269,33 @@ func resourceVirtualEnvironmentContainerDelete(d *schema.ResourceData, m interfa
|
||||
return err
|
||||
}
|
||||
|
||||
// Shut down the container before deleting it.
|
||||
status, err := veClient.GetContainerStatus(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status.Status != "stopped" {
|
||||
forceStop := proxmox.CustomBool(true)
|
||||
shutdownTimeout := 300
|
||||
|
||||
err = veClient.ShutdownContainer(nodeName, vmID, &proxmox.VirtualEnvironmentContainerShutdownRequestBody{
|
||||
ForceStop: &forceStop,
|
||||
Timeout: &shutdownTimeout,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = veClient.WaitForContainerState(nodeName, vmID, "stopped", 30, 5)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = veClient.DeleteContainer(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
@ -882,6 +1308,13 @@ func resourceVirtualEnvironmentContainerDelete(d *schema.ResourceData, m interfa
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the state to become unavailable as that clearly indicates the destruction of the container.
|
||||
err = veClient.WaitForContainerState(nodeName, vmID, "", 60, 2)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("Failed to delete container \"%d\"", vmID)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
|
||||
return nil
|
||||
|
237
proxmoxtf/resource_virtual_environment_container_test.go
Normal file
237
proxmoxtf/resource_virtual_environment_container_test.go
Normal file
@ -0,0 +1,237 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package proxmoxtf
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
// TestResourceVirtualEnvironmentContainerInstantiation tests whether the ResourceVirtualEnvironmentContainer instance can be instantiated.
|
||||
func TestResourceVirtualEnvironmentContainerInstantiation(t *testing.T) {
|
||||
s := resourceVirtualEnvironmentContainer()
|
||||
|
||||
if s == nil {
|
||||
t.Fatalf("Cannot instantiate resourceVirtualEnvironmentContainer")
|
||||
}
|
||||
}
|
||||
|
||||
// TestResourceVirtualEnvironmentContainerSchema tests the resourceVirtualEnvironmentContainer schema.
|
||||
func TestResourceVirtualEnvironmentContainerSchema(t *testing.T) {
|
||||
s := resourceVirtualEnvironmentContainer()
|
||||
|
||||
testRequiredArguments(t, s, []string{
|
||||
mkResourceVirtualEnvironmentContainerNodeName,
|
||||
mkResourceVirtualEnvironmentContainerOperatingSystem,
|
||||
})
|
||||
|
||||
testOptionalArguments(t, s, []string{
|
||||
mkResourceVirtualEnvironmentContainerCPU,
|
||||
mkResourceVirtualEnvironmentContainerDescription,
|
||||
mkResourceVirtualEnvironmentContainerInitialization,
|
||||
mkResourceVirtualEnvironmentContainerMemory,
|
||||
mkResourceVirtualEnvironmentContainerPoolID,
|
||||
mkResourceVirtualEnvironmentContainerStarted,
|
||||
mkResourceVirtualEnvironmentContainerVMID,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, s, []string{
|
||||
mkResourceVirtualEnvironmentContainerCPU,
|
||||
mkResourceVirtualEnvironmentContainerDescription,
|
||||
mkResourceVirtualEnvironmentContainerInitialization,
|
||||
mkResourceVirtualEnvironmentContainerMemory,
|
||||
mkResourceVirtualEnvironmentContainerOperatingSystem,
|
||||
mkResourceVirtualEnvironmentContainerPoolID,
|
||||
mkResourceVirtualEnvironmentContainerStarted,
|
||||
mkResourceVirtualEnvironmentContainerVMID,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeList,
|
||||
schema.TypeString,
|
||||
schema.TypeList,
|
||||
schema.TypeList,
|
||||
schema.TypeList,
|
||||
schema.TypeString,
|
||||
schema.TypeBool,
|
||||
schema.TypeInt,
|
||||
})
|
||||
|
||||
cpuSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentContainerCPU)
|
||||
|
||||
testOptionalArguments(t, cpuSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerCPUArchitecture,
|
||||
mkResourceVirtualEnvironmentContainerCPUCores,
|
||||
mkResourceVirtualEnvironmentContainerCPUUnits,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, cpuSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerCPUArchitecture,
|
||||
mkResourceVirtualEnvironmentContainerCPUCores,
|
||||
mkResourceVirtualEnvironmentContainerCPUUnits,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeString,
|
||||
schema.TypeInt,
|
||||
schema.TypeInt,
|
||||
})
|
||||
|
||||
initializationSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentContainerInitialization)
|
||||
|
||||
testOptionalArguments(t, initializationSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationDNS,
|
||||
mkResourceVirtualEnvironmentContainerInitializationHostname,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfig,
|
||||
mkResourceVirtualEnvironmentContainerInitializationUserAccount,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, initializationSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationDNS,
|
||||
mkResourceVirtualEnvironmentContainerInitializationHostname,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfig,
|
||||
mkResourceVirtualEnvironmentContainerInitializationUserAccount,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeList,
|
||||
schema.TypeString,
|
||||
schema.TypeList,
|
||||
schema.TypeList,
|
||||
})
|
||||
|
||||
initializationDNSSchema := testNestedSchemaExistence(t, initializationSchema, mkResourceVirtualEnvironmentContainerInitializationDNS)
|
||||
|
||||
testOptionalArguments(t, initializationDNSSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationDNSDomain,
|
||||
mkResourceVirtualEnvironmentContainerInitializationDNSServer,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, initializationDNSSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationDNSDomain,
|
||||
mkResourceVirtualEnvironmentContainerInitializationDNSServer,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeString,
|
||||
schema.TypeString,
|
||||
})
|
||||
|
||||
initializationIPConfigSchema := testNestedSchemaExistence(t, initializationSchema, mkResourceVirtualEnvironmentContainerInitializationIPConfig)
|
||||
|
||||
testOptionalArguments(t, initializationIPConfigSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, initializationIPConfigSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeList,
|
||||
schema.TypeList,
|
||||
})
|
||||
|
||||
initializationIPConfigIPv4Schema := testNestedSchemaExistence(t, initializationIPConfigSchema, mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4)
|
||||
|
||||
testOptionalArguments(t, initializationIPConfigIPv4Schema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Address,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Gateway,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, initializationIPConfigIPv4Schema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Address,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv4Gateway,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeString,
|
||||
schema.TypeString,
|
||||
})
|
||||
|
||||
initializationIPConfigIPv6Schema := testNestedSchemaExistence(t, initializationIPConfigSchema, mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6)
|
||||
|
||||
testOptionalArguments(t, initializationIPConfigIPv6Schema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Address,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Gateway,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, initializationIPConfigIPv6Schema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Address,
|
||||
mkResourceVirtualEnvironmentContainerInitializationIPConfigIPv6Gateway,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeString,
|
||||
schema.TypeString,
|
||||
})
|
||||
|
||||
initializationUserAccountSchema := testNestedSchemaExistence(t, initializationSchema, mkResourceVirtualEnvironmentContainerInitializationUserAccount)
|
||||
|
||||
testOptionalArguments(t, initializationUserAccountSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationUserAccountKeys,
|
||||
mkResourceVirtualEnvironmentContainerInitializationUserAccountPassword,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, initializationUserAccountSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerInitializationUserAccountKeys,
|
||||
mkResourceVirtualEnvironmentContainerInitializationUserAccountPassword,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeList,
|
||||
schema.TypeString,
|
||||
})
|
||||
|
||||
memorySchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentContainerMemory)
|
||||
|
||||
testOptionalArguments(t, memorySchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerMemoryDedicated,
|
||||
mkResourceVirtualEnvironmentContainerMemorySwap,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, memorySchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerMemoryDedicated,
|
||||
mkResourceVirtualEnvironmentContainerMemorySwap,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeInt,
|
||||
schema.TypeInt,
|
||||
})
|
||||
|
||||
networkInterfaceSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentContainerNetworkInterface)
|
||||
|
||||
testRequiredArguments(t, networkInterfaceSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceName,
|
||||
})
|
||||
|
||||
testOptionalArguments(t, networkInterfaceSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceBridge,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceEnabled,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceMACAddress,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceRateLimit,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceVLANID,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, networkInterfaceSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceBridge,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceEnabled,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceMACAddress,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceName,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceRateLimit,
|
||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceVLANID,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeString,
|
||||
schema.TypeBool,
|
||||
schema.TypeString,
|
||||
schema.TypeString,
|
||||
schema.TypeFloat,
|
||||
schema.TypeInt,
|
||||
})
|
||||
|
||||
operatingSystemSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentContainerOperatingSystem)
|
||||
|
||||
testRequiredArguments(t, operatingSystemSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerOperatingSystemTemplateFileID,
|
||||
})
|
||||
|
||||
testOptionalArguments(t, operatingSystemSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerOperatingSystemType,
|
||||
})
|
||||
|
||||
testSchemaValueTypes(t, operatingSystemSchema, []string{
|
||||
mkResourceVirtualEnvironmentContainerOperatingSystemTemplateFileID,
|
||||
mkResourceVirtualEnvironmentContainerOperatingSystemType,
|
||||
}, []schema.ValueType{
|
||||
schema.TypeString,
|
||||
schema.TypeString,
|
||||
})
|
||||
}
|
@ -402,7 +402,8 @@ func resourceVirtualEnvironmentFileGetContentType(d *schema.ResourceData, m inte
|
||||
}
|
||||
|
||||
if contentType == "" {
|
||||
if strings.HasSuffix(sourceFilePath, ".tar.xz") {
|
||||
if strings.HasSuffix(sourceFilePath, ".tar.gz") ||
|
||||
strings.HasSuffix(sourceFilePath, ".tar.xz") {
|
||||
contentType = "vztmpl"
|
||||
} else {
|
||||
ext := strings.TrimLeft(strings.ToLower(filepath.Ext(sourceFilePath)), ".")
|
||||
|
@ -547,7 +547,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
||||
Sensitive: true,
|
||||
Default: dvResourceVirtualEnvironmentVMInitializationUserAccountPassword,
|
||||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||||
return strings.ReplaceAll(old, "*", "") == ""
|
||||
return len(old) > 0 && strings.ReplaceAll(old, "*", "") == ""
|
||||
},
|
||||
},
|
||||
mkResourceVirtualEnvironmentVMInitializationUserAccountUsername: {
|
||||
@ -1484,7 +1484,8 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err
|
||||
vmConfig, err := veClient.GetVM(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "HTTP 404") {
|
||||
if strings.Contains(err.Error(), "HTTP 404") ||
|
||||
(strings.Contains(err.Error(), "HTTP 500") && strings.Contains(err.Error(), "does not exist")) {
|
||||
d.SetId("")
|
||||
|
||||
return nil
|
||||
@ -2502,28 +2503,37 @@ func resourceVirtualEnvironmentVMDelete(d *schema.ResourceData, m interface{}) e
|
||||
}
|
||||
|
||||
// Shut down the virtual machine before deleting it.
|
||||
forceStop := proxmox.CustomBool(true)
|
||||
shutdownTimeout := 300
|
||||
|
||||
err = veClient.ShutdownVM(nodeName, vmID, &proxmox.VirtualEnvironmentVMShutdownRequestBody{
|
||||
ForceStop: &forceStop,
|
||||
Timeout: &shutdownTimeout,
|
||||
})
|
||||
status, err := veClient.GetVMStatus(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = veClient.WaitForVMState(nodeName, vmID, "stopped", 30, 5)
|
||||
if status.Status != "stopped" {
|
||||
forceStop := proxmox.CustomBool(true)
|
||||
shutdownTimeout := 300
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
err = veClient.ShutdownVM(nodeName, vmID, &proxmox.VirtualEnvironmentVMShutdownRequestBody{
|
||||
ForceStop: &forceStop,
|
||||
Timeout: &shutdownTimeout,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = veClient.WaitForVMState(nodeName, vmID, "stopped", 30, 5)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = veClient.DeleteVM(nodeName, vmID)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "HTTP 404") {
|
||||
if strings.Contains(err.Error(), "HTTP 404") ||
|
||||
(strings.Contains(err.Error(), "HTTP 500") && strings.Contains(err.Error(), "does not exist")) {
|
||||
d.SetId("")
|
||||
|
||||
return nil
|
||||
@ -2533,7 +2543,7 @@ func resourceVirtualEnvironmentVMDelete(d *schema.ResourceData, m interface{}) e
|
||||
}
|
||||
|
||||
// Wait for the state to become unavailable as that clearly indicates the destruction of the VM.
|
||||
err = veClient.WaitForVMState(nodeName, vmID, "", 30, 2)
|
||||
err = veClient.WaitForVMState(nodeName, vmID, "", 60, 2)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("Failed to delete VM \"%d\"", vmID)
|
||||
|
Loading…
Reference in New Issue
Block a user