mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-04 12:32:59 +00:00
feat(lxc): add support for lxc mount points (#394)
* feat(lxc): add support for lxc mount points * update docs and examples * improve error handling for container creation / start operations, fix size propagation for storage mounts --------- Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
b9ee3ae10d
commit
beef9b1219
@ -45,6 +45,12 @@ resource "proxmox_virtual_environment_container" "ubuntu_container" {
|
|||||||
template_file_id = proxmox_virtual_environment_file.ubuntu_container_template.id
|
template_file_id = proxmox_virtual_environment_file.ubuntu_container_template.id
|
||||||
type = "ubuntu"
|
type = "ubuntu"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mount_point {
|
||||||
|
volume = "/mnt/bindmounts/shared"
|
||||||
|
path = "/shared"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "proxmox_virtual_environment_file" "ubuntu_container_template" {
|
resource "proxmox_virtual_environment_file" "ubuntu_container_template" {
|
||||||
@ -136,6 +142,23 @@ output "ubuntu_container_public_key" {
|
|||||||
- `dedicated` - (Optional) The dedicated memory in megabytes (defaults
|
- `dedicated` - (Optional) The dedicated memory in megabytes (defaults
|
||||||
to `512`).
|
to `512`).
|
||||||
- `swap` - (Optional) The swap size in megabytes (defaults to `0`).
|
- `swap` - (Optional) The swap size in megabytes (defaults to `0`).
|
||||||
|
- `mount_point`
|
||||||
|
- `acl` (Optional) Explicitly enable or disable ACL support.
|
||||||
|
- `backup` (Optional) Whether to include the mount point in backups (only
|
||||||
|
used for volume mount points).
|
||||||
|
- `mount_options` (Optional) List of extra mount options.
|
||||||
|
- `path` (Required) Path to the mount point as seen from inside the
|
||||||
|
container.
|
||||||
|
- `quota` (Optional) Enable user quotas inside the container (not supported
|
||||||
|
with ZFS subvolumes).
|
||||||
|
- `read_only` (Optional) Read-only mount point.
|
||||||
|
- `replicate` (Optional) Will include this volume to a storage replica job.
|
||||||
|
- `shared` (Optional) Mark this non-volume mount point as available on all
|
||||||
|
nodes.
|
||||||
|
- `size` (Optional) Volume size (only for ZFS storage backed mount points).
|
||||||
|
Can be specified with a unit suffix (e.g. `10G`).
|
||||||
|
- `volume` (Required) Volume, device or directory to mount into the
|
||||||
|
container.
|
||||||
- `network_interface` - (Optional) A network interface (multiple blocks
|
- `network_interface` - (Optional) A network interface (multiple blocks
|
||||||
supported).
|
supported).
|
||||||
- `bridge` - (Optional) The name of the network bridge (defaults
|
- `bridge` - (Optional) The name of the network bridge (defaults
|
||||||
|
@ -62,6 +62,11 @@ resource "proxmox_virtual_environment_container" "example" {
|
|||||||
hostname = "terraform-provider-proxmox-example-lxc"
|
hostname = "terraform-provider-proxmox-example-lxc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mount_point {
|
||||||
|
volume = "/mnt/bindmounts/shared"
|
||||||
|
path = "/shared"
|
||||||
|
}
|
||||||
|
|
||||||
node_name = data.proxmox_virtual_environment_nodes.example.names[0]
|
node_name = data.proxmox_virtual_environment_nodes.example.names[0]
|
||||||
pool_id = proxmox_virtual_environment_pool.example.id
|
pool_id = proxmox_virtual_environment_pool.example.id
|
||||||
vm_id = 2043
|
vm_id = 2043
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/firewall"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/firewall"
|
||||||
containerfirewall "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers/firewall"
|
containerfirewall "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers/firewall"
|
||||||
|
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/tasks"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is an interface for accessing the Proxmox container API.
|
// Client is an interface for accessing the Proxmox container API.
|
||||||
@ -34,6 +35,13 @@ func (c *Client) ExpandPath(path string) string {
|
|||||||
return ep
|
return ep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tasks returns a client for managing container tasks.
|
||||||
|
func (c *Client) Tasks() *tasks.Client {
|
||||||
|
return &tasks.Client{
|
||||||
|
Client: c.Client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Firewall returns a client for managing the container firewall.
|
// Firewall returns a client for managing the container firewall.
|
||||||
func (c *Client) Firewall() firewall.API {
|
func (c *Client) Firewall() firewall.API {
|
||||||
return &containerfirewall.Client{
|
return &containerfirewall.Client{
|
||||||
|
@ -27,15 +27,36 @@ func (c *Client) CloneContainer(ctx context.Context, d *CloneRequestBody) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateContainer creates a container.
|
// CreateContainer creates a container.
|
||||||
func (c *Client) CreateContainer(ctx context.Context, d *CreateRequestBody) error {
|
func (c *Client) CreateContainer(ctx context.Context, d *CreateRequestBody, timeout int) error {
|
||||||
err := c.DoRequest(ctx, http.MethodPost, c.basePath(), d, nil)
|
taskID, err := c.CreateContainerAsync(ctx, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating container: %w", err)
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error waiting for container created: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateContainerAsync creates a container asynchronously.
|
||||||
|
func (c *Client) CreateContainerAsync(ctx context.Context, d *CreateRequestBody) (*string, error) {
|
||||||
|
resBody := &CreateResponseBody{}
|
||||||
|
|
||||||
|
err := c.DoRequest(ctx, http.MethodPost, c.basePath(), d, resBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating container: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resBody.Data == nil {
|
||||||
|
return nil, api.ErrNoDataObjectInResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteContainer deletes a container.
|
// DeleteContainer deletes a container.
|
||||||
func (c *Client) DeleteContainer(ctx context.Context) error {
|
func (c *Client) DeleteContainer(ctx context.Context) error {
|
||||||
err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath(""), nil, nil)
|
err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath(""), nil, nil)
|
||||||
@ -99,15 +120,36 @@ func (c *Client) ShutdownContainer(ctx context.Context, d *ShutdownRequestBody)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StartContainer starts a container.
|
// StartContainer starts a container.
|
||||||
func (c *Client) StartContainer(ctx context.Context) error {
|
func (c *Client) StartContainer(ctx context.Context, timeout int) error {
|
||||||
err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/start"), nil, nil)
|
taskID, err := c.StartContainerAsync(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting container: %w", err)
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error waiting for container start: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartContainerAsync starts a container asynchronously.
|
||||||
|
func (c *Client) StartContainerAsync(ctx context.Context) (*string, error) {
|
||||||
|
resBody := &StartResponseBody{}
|
||||||
|
|
||||||
|
err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/start"), nil, resBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error starting container: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resBody.Data == nil {
|
||||||
|
return nil, api.ErrNoDataObjectInResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return resBody.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StopContainer stops a container immediately.
|
// StopContainer stops a container immediately.
|
||||||
func (c *Client) StopContainer(ctx context.Context) error {
|
func (c *Client) StopContainer(ctx context.Context) error {
|
||||||
err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/stop"), nil, nil)
|
err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/stop"), nil, nil)
|
||||||
|
@ -84,7 +84,7 @@ type CustomFeatures struct {
|
|||||||
type CustomMountPoint struct {
|
type CustomMountPoint struct {
|
||||||
ACL *types.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"`
|
ACL *types.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"`
|
||||||
Backup *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"`
|
Backup *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"`
|
||||||
DiskSize *string `json:"size,omitempty" url:"size,omitempty"`
|
DiskSize *string `json:"size,omitempty" url:"size,omitempty"` // read-only
|
||||||
Enabled bool `json:"-" url:"-"`
|
Enabled bool `json:"-" url:"-"`
|
||||||
MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"`
|
MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"`
|
||||||
MountPoint string `json:"mp" url:"mp"`
|
MountPoint string `json:"mp" url:"mp"`
|
||||||
@ -141,6 +141,11 @@ type CustomStartupBehavior struct {
|
|||||||
Up *int `json:"up,omitempty" url:"up,omitempty"`
|
Up *int `json:"up,omitempty" url:"up,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateResponseBody contains the body from a container create response.
|
||||||
|
type CreateResponseBody struct {
|
||||||
|
Data *string `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// GetResponseBody contains the body from a user get response.
|
// GetResponseBody contains the body from a user get response.
|
||||||
type GetResponseBody struct {
|
type GetResponseBody struct {
|
||||||
Data *GetResponseData `json:"data,omitempty"`
|
Data *GetResponseData `json:"data,omitempty"`
|
||||||
@ -164,10 +169,14 @@ type GetResponseData struct {
|
|||||||
Hostname *string `json:"hostname,omitempty"`
|
Hostname *string `json:"hostname,omitempty"`
|
||||||
Lock *types.CustomBool `json:"lock,omitempty"`
|
Lock *types.CustomBool `json:"lock,omitempty"`
|
||||||
LXCConfiguration *[][2]string `json:"lxc,omitempty"`
|
LXCConfiguration *[][2]string `json:"lxc,omitempty"`
|
||||||
MountPoint0 CustomMountPoint `json:"mp0,omitempty"`
|
MountPoint0 *CustomMountPoint `json:"mp0,omitempty"`
|
||||||
MountPoint1 CustomMountPoint `json:"mp1,omitempty"`
|
MountPoint1 *CustomMountPoint `json:"mp1,omitempty"`
|
||||||
MountPoint2 CustomMountPoint `json:"mp2,omitempty"`
|
MountPoint2 *CustomMountPoint `json:"mp2,omitempty"`
|
||||||
MountPoint3 CustomMountPoint `json:"mp3,omitempty"`
|
MountPoint3 *CustomMountPoint `json:"mp3,omitempty"`
|
||||||
|
MountPoint4 *CustomMountPoint `json:"mp4,omitempty"`
|
||||||
|
MountPoint5 *CustomMountPoint `json:"mp5,omitempty"`
|
||||||
|
MountPoint6 *CustomMountPoint `json:"mp6,omitempty"`
|
||||||
|
MountPoint7 *CustomMountPoint `json:"mp7,omitempty"`
|
||||||
NetworkInterface0 *CustomNetworkInterface `json:"net0,omitempty"`
|
NetworkInterface0 *CustomNetworkInterface `json:"net0,omitempty"`
|
||||||
NetworkInterface1 *CustomNetworkInterface `json:"net1,omitempty"`
|
NetworkInterface1 *CustomNetworkInterface `json:"net1,omitempty"`
|
||||||
NetworkInterface2 *CustomNetworkInterface `json:"net2,omitempty"`
|
NetworkInterface2 *CustomNetworkInterface `json:"net2,omitempty"`
|
||||||
@ -207,6 +216,11 @@ type GetStatusResponseData struct {
|
|||||||
VMID *int `json:"vmid,omitempty"`
|
VMID *int `json:"vmid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartResponseBody contains the body from a container start response.
|
||||||
|
type StartResponseBody struct {
|
||||||
|
Data *string `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// RebootRequestBody contains the body for a container reboot request.
|
// RebootRequestBody contains the body for a container reboot request.
|
||||||
type RebootRequestBody struct {
|
type RebootRequestBody struct {
|
||||||
Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"`
|
Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"`
|
||||||
@ -288,7 +302,7 @@ func (r *CustomMountPoint) EncodeValues(key string, v *url.Values) error {
|
|||||||
|
|
||||||
if r.MountOptions != nil {
|
if r.MountOptions != nil {
|
||||||
if len(*r.MountOptions) > 0 {
|
if len(*r.MountOptions) > 0 {
|
||||||
values = append(values, fmt.Sprintf("mount=%s", strings.Join(*r.MountOptions, ";")))
|
values = append(values, fmt.Sprintf("mountoptions=%s", strings.Join(*r.MountOptions, ";")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +325,7 @@ func (r *CustomMountPoint) EncodeValues(key string, v *url.Values) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r.Replicate != nil {
|
if r.Replicate != nil {
|
||||||
if *r.ReadOnly {
|
if *r.Replicate {
|
||||||
values = append(values, "replicate=1")
|
values = append(values, "replicate=1")
|
||||||
} else {
|
} else {
|
||||||
values = append(values, "replicate=0")
|
values = append(values, "replicate=0")
|
||||||
|
@ -46,6 +46,13 @@ const (
|
|||||||
dvResourceVirtualEnvironmentContainerFeaturesNesting = false
|
dvResourceVirtualEnvironmentContainerFeaturesNesting = false
|
||||||
dvResourceVirtualEnvironmentContainerMemoryDedicated = 512
|
dvResourceVirtualEnvironmentContainerMemoryDedicated = 512
|
||||||
dvResourceVirtualEnvironmentContainerMemorySwap = 0
|
dvResourceVirtualEnvironmentContainerMemorySwap = 0
|
||||||
|
dvResourceVirtualEnvironmentContainerMountPointACL = false
|
||||||
|
dvResourceVirtualEnvironmentContainerMountPointBackup = true
|
||||||
|
dvResourceVirtualEnvironmentContainerMountPointQuota = false
|
||||||
|
dvResourceVirtualEnvironmentContainerMountPointReadOnly = false
|
||||||
|
dvResourceVirtualEnvironmentContainerMountPointReplicate = true
|
||||||
|
dvResourceVirtualEnvironmentContainerMountPointShared = false
|
||||||
|
dvResourceVirtualEnvironmentContainerMountPointSize = ""
|
||||||
dvResourceVirtualEnvironmentContainerNetworkInterfaceBridge = "vmbr0"
|
dvResourceVirtualEnvironmentContainerNetworkInterfaceBridge = "vmbr0"
|
||||||
dvResourceVirtualEnvironmentContainerNetworkInterfaceEnabled = true
|
dvResourceVirtualEnvironmentContainerNetworkInterfaceEnabled = true
|
||||||
dvResourceVirtualEnvironmentContainerNetworkInterfaceFirewall = false
|
dvResourceVirtualEnvironmentContainerNetworkInterfaceFirewall = false
|
||||||
@ -97,6 +104,17 @@ const (
|
|||||||
mkResourceVirtualEnvironmentContainerMemory = "memory"
|
mkResourceVirtualEnvironmentContainerMemory = "memory"
|
||||||
mkResourceVirtualEnvironmentContainerMemoryDedicated = "dedicated"
|
mkResourceVirtualEnvironmentContainerMemoryDedicated = "dedicated"
|
||||||
mkResourceVirtualEnvironmentContainerMemorySwap = "swap"
|
mkResourceVirtualEnvironmentContainerMemorySwap = "swap"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPoint = "mount_point"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointACL = "acl"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointBackup = "backup"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointMountOptions = "mount_options"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointPath = "path"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointQuota = "quota"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReadOnly = "read_only"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReplicate = "replicate"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointShared = "shared"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointSize = "size"
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointVolume = "volume"
|
||||||
mkResourceVirtualEnvironmentContainerNetworkInterface = "network_interface"
|
mkResourceVirtualEnvironmentContainerNetworkInterface = "network_interface"
|
||||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceBridge = "bridge"
|
mkResourceVirtualEnvironmentContainerNetworkInterfaceBridge = "bridge"
|
||||||
mkResourceVirtualEnvironmentContainerNetworkInterfaceEnabled = "enabled"
|
mkResourceVirtualEnvironmentContainerNetworkInterfaceEnabled = "enabled"
|
||||||
@ -493,6 +511,78 @@ func Container() *schema.Resource {
|
|||||||
MaxItems: 1,
|
MaxItems: 1,
|
||||||
MinItems: 0,
|
MinItems: 0,
|
||||||
},
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPoint: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "A mount point",
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointACL: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Explicitly enable or disable ACL support",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentContainerMountPointACL,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointBackup: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Whether to include the mount point in backups (only used for volume mount points)",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentContainerMountPointBackup,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointMountOptions: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "Extra mount options.",
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointPath: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "Path to the mount point as seen from inside the container",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointQuota: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Enable user quotas inside the container (not supported with zfs subvolumes)",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentContainerMountPointQuota,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReadOnly: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Read-only mount point",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentContainerMountPointReadOnly,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReplicate: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Will include this volume to a storage replica job",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentContainerMountPointReplicate,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointShared: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Mark this non-volume mount point as available on all nodes",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentContainerMountPointShared,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointSize: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "Volume size (only used for volume mount points)",
|
||||||
|
Optional: true,
|
||||||
|
Default: dvResourceVirtualEnvironmentContainerMountPointSize,
|
||||||
|
ValidateDiagFunc: getFileSizeValidator(),
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointVolume: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "Volume, device or directory to mount into the container",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaxItems: 8,
|
||||||
|
MinItems: 0,
|
||||||
|
},
|
||||||
mkResourceVirtualEnvironmentContainerNetworkInterface: {
|
mkResourceVirtualEnvironmentContainerNetworkInterface: {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Description: "The network interfaces",
|
Description: "The network interfaces",
|
||||||
@ -1206,11 +1296,60 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
|
|||||||
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentContainerMemoryDedicated].(int)
|
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentContainerMemoryDedicated].(int)
|
||||||
memorySwap := memoryBlock[mkResourceVirtualEnvironmentContainerMemorySwap].(int)
|
memorySwap := memoryBlock[mkResourceVirtualEnvironmentContainerMemorySwap].(int)
|
||||||
|
|
||||||
|
mountPoint := d.Get(mkResourceVirtualEnvironmentContainerMountPoint).([]interface{})
|
||||||
|
mountPointArray := make(containers.CustomMountPointArray, 0, len(mountPoint))
|
||||||
|
|
||||||
|
for _, mp := range mountPoint {
|
||||||
|
mountPointMap := mp.(map[string]interface{})
|
||||||
|
mountPointObject := containers.CustomMountPoint{}
|
||||||
|
|
||||||
|
acl := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointACL].(bool))
|
||||||
|
backup := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointBackup].(bool))
|
||||||
|
mountOptions := mountPointMap[mkResourceVirtualEnvironmentContainerMountPointMountOptions].([]interface{})
|
||||||
|
path := mountPointMap[mkResourceVirtualEnvironmentContainerMountPointPath].(string)
|
||||||
|
quota := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointQuota].(bool))
|
||||||
|
readOnly := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointReadOnly].(bool))
|
||||||
|
replicate := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointReplicate].(bool))
|
||||||
|
shared := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointShared].(bool))
|
||||||
|
size := mountPointMap[mkResourceVirtualEnvironmentContainerMountPointSize].(string)
|
||||||
|
volume := mountPointMap[mkResourceVirtualEnvironmentContainerMountPointVolume].(string)
|
||||||
|
|
||||||
|
mountPointObject.ACL = &acl
|
||||||
|
mountPointObject.Backup = &backup
|
||||||
|
mountPointObject.MountPoint = path
|
||||||
|
mountPointObject.Quota = "a
|
||||||
|
mountPointObject.ReadOnly = &readOnly
|
||||||
|
mountPointObject.Replicate = &replicate
|
||||||
|
mountPointObject.Shared = &shared
|
||||||
|
|
||||||
|
if len(size) > 0 {
|
||||||
|
var ds types.DiskSize
|
||||||
|
|
||||||
|
ds, err = types.ParseDiskSize(size)
|
||||||
|
if err != nil {
|
||||||
|
return diag.Errorf("invalid disk size: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPointObject.Volume = fmt.Sprintf("%s:%d", volume, ds.InGigabytes())
|
||||||
|
} else {
|
||||||
|
mountPointObject.Volume = volume
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mountOptions) > 0 {
|
||||||
|
mountOptionsArray := make([]string, 0, len(mountPoint))
|
||||||
|
|
||||||
|
for _, option := range mountOptions {
|
||||||
|
mountOptionsArray = append(mountOptionsArray, option.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPointObject.MountOptions = &mountOptionsArray
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPointArray = append(mountPointArray, mountPointObject)
|
||||||
|
}
|
||||||
|
|
||||||
networkInterface := d.Get(mkResourceVirtualEnvironmentContainerNetworkInterface).([]interface{})
|
networkInterface := d.Get(mkResourceVirtualEnvironmentContainerNetworkInterface).([]interface{})
|
||||||
networkInterfaceArray := make(
|
networkInterfaceArray := make(containers.CustomNetworkInterfaceArray, len(networkInterface))
|
||||||
containers.CustomNetworkInterfaceArray,
|
|
||||||
len(networkInterface),
|
|
||||||
)
|
|
||||||
|
|
||||||
for ni, nv := range networkInterface {
|
for ni, nv := range networkInterface {
|
||||||
networkInterfaceMap := nv.(map[string]interface{})
|
networkInterfaceMap := nv.(map[string]interface{})
|
||||||
@ -1307,6 +1446,7 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
|
|||||||
DatastoreID: &diskDatastoreID,
|
DatastoreID: &diskDatastoreID,
|
||||||
DedicatedMemory: &memoryDedicated,
|
DedicatedMemory: &memoryDedicated,
|
||||||
Features: &features,
|
Features: &features,
|
||||||
|
MountPoints: mountPointArray,
|
||||||
NetworkInterfaces: networkInterfaceArray,
|
NetworkInterfaces: networkInterfaceArray,
|
||||||
OSTemplateFileVolume: &operatingSystemTemplateFileID,
|
OSTemplateFileVolume: &operatingSystemTemplateFileID,
|
||||||
OSType: &operatingSystemType,
|
OSType: &operatingSystemType,
|
||||||
@ -1352,7 +1492,7 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
|
|||||||
createBody.Tags = &tagsString
|
createBody.Tags = &tagsString
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.Node(nodeName).Container(0).CreateContainer(ctx, &createBody)
|
err = api.Node(nodeName).Container(0).CreateContainer(ctx, &createBody, 60)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
@ -1391,7 +1531,7 @@ func containerCreateStart(ctx context.Context, d *schema.ResourceData, m interfa
|
|||||||
containerAPI := api.Node(nodeName).Container(vmID)
|
containerAPI := api.Node(nodeName).Container(vmID)
|
||||||
|
|
||||||
// Start the container and wait for it to reach a running state before continuing.
|
// Start the container and wait for it to reach a running state before continuing.
|
||||||
err = containerAPI.StartContainer(ctx)
|
err = containerAPI.StartContainer(ctx, 60)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
@ -1745,6 +1885,93 @@ func containerRead(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
initialization[mkResourceVirtualEnvironmentContainerInitializationHostname] = ""
|
initialization[mkResourceVirtualEnvironmentContainerInitializationHostname] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mountPointArray := []*containers.CustomMountPoint{
|
||||||
|
containerConfig.MountPoint0,
|
||||||
|
containerConfig.MountPoint1,
|
||||||
|
containerConfig.MountPoint2,
|
||||||
|
containerConfig.MountPoint3,
|
||||||
|
containerConfig.MountPoint4,
|
||||||
|
containerConfig.MountPoint5,
|
||||||
|
containerConfig.MountPoint6,
|
||||||
|
containerConfig.MountPoint7,
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPointList := make([]interface{}, 0, len(mountPointArray))
|
||||||
|
|
||||||
|
for _, mp := range mountPointArray {
|
||||||
|
if mp == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPoint := map[string]interface{}{}
|
||||||
|
|
||||||
|
if mp.ACL != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointACL] = *mp.ACL
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointACL] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.Backup != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointBackup] = *mp.Backup
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointBackup] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.MountOptions != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointMountOptions] = *mp.MountOptions
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointMountOptions] = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointPath] = mp.MountPoint
|
||||||
|
|
||||||
|
if mp.Quota != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointQuota] = *mp.Quota
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointQuota] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.ReadOnly != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointReadOnly] = *mp.ReadOnly
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointReadOnly] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.Replicate != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointReplicate] = *mp.Replicate
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointReplicate] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.Shared != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointShared] = *mp.Shared
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointShared] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.DiskSize != nil {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointSize] = *mp.DiskSize
|
||||||
|
} else {
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointSize] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPoint[mkResourceVirtualEnvironmentContainerMountPointVolume] = mp.Volume
|
||||||
|
|
||||||
|
mountPointList = append(mountPointList, mountPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMountPoint := d.Get(mkResourceVirtualEnvironmentContainerMountPoint).([]interface{})
|
||||||
|
|
||||||
|
if len(clone) > 0 {
|
||||||
|
if len(currentMountPoint) > 0 {
|
||||||
|
err := d.Set(mkResourceVirtualEnvironmentContainerMountPoint, mountPointList)
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
}
|
||||||
|
} else if len(mountPointList) > 0 {
|
||||||
|
err := d.Set(mkResourceVirtualEnvironmentContainerMountPoint, mountPointList)
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
}
|
||||||
|
|
||||||
var ipConfigList []interface{}
|
var ipConfigList []interface{}
|
||||||
|
|
||||||
networkInterfaceArray := []*containers.CustomNetworkInterface{
|
networkInterfaceArray := []*containers.CustomNetworkInterface{
|
||||||
@ -1945,7 +2172,7 @@ func containerRead(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(clone) > 0 {
|
if len(clone) > 0 {
|
||||||
if len(currentMemory) > 0 {
|
if len(currentOperatingSystem) > 0 {
|
||||||
err := d.Set(
|
err := d.Set(
|
||||||
mkResourceVirtualEnvironmentContainerOperatingSystem,
|
mkResourceVirtualEnvironmentContainerOperatingSystem,
|
||||||
[]interface{}{operatingSystem},
|
[]interface{}{operatingSystem},
|
||||||
@ -2189,6 +2416,55 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
rebootRequired = true
|
rebootRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare the new mount point configuration.
|
||||||
|
if d.HasChange(mkResourceVirtualEnvironmentContainerMountPoint) {
|
||||||
|
mountPoint := d.Get(mkResourceVirtualEnvironmentContainerMountPoint).([]interface{})
|
||||||
|
mountPointArray := make(
|
||||||
|
containers.CustomMountPointArray,
|
||||||
|
len(mountPoint),
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, mp := range mountPoint {
|
||||||
|
mountPointMap := mp.(map[string]interface{})
|
||||||
|
mountPointObject := containers.CustomMountPoint{}
|
||||||
|
|
||||||
|
acl := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointACL].(bool))
|
||||||
|
backup := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointBackup].(bool))
|
||||||
|
mountOptions := mountPointMap[mkResourceVirtualEnvironmentContainerMountPointMountOptions].([]interface{})
|
||||||
|
path := mountPointMap[mkResourceVirtualEnvironmentContainerMountPointPath].(string)
|
||||||
|
quota := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointQuota].(bool))
|
||||||
|
readOnly := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointReadOnly].(bool))
|
||||||
|
replicate := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointReplicate].(bool))
|
||||||
|
shared := types.CustomBool(mountPointMap[mkResourceVirtualEnvironmentContainerMountPointShared].(bool))
|
||||||
|
volume := mountPointMap[mkResourceVirtualEnvironmentContainerMountPointVolume].(string)
|
||||||
|
|
||||||
|
mountPointObject.ACL = &acl
|
||||||
|
mountPointObject.Backup = &backup
|
||||||
|
mountPointObject.MountPoint = path
|
||||||
|
mountPointObject.Quota = "a
|
||||||
|
mountPointObject.ReadOnly = &readOnly
|
||||||
|
mountPointObject.Replicate = &replicate
|
||||||
|
mountPointObject.Shared = &shared
|
||||||
|
mountPointObject.Volume = volume
|
||||||
|
|
||||||
|
if len(mountOptions) > 0 {
|
||||||
|
mountOptionsArray := make([]string, 0, len(mountPoint))
|
||||||
|
|
||||||
|
for _, option := range mountOptions {
|
||||||
|
mountOptionsArray = append(mountOptionsArray, option.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPointObject.MountOptions = &mountOptionsArray
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPointArray[i] = mountPointObject
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.MountPoints = mountPointArray
|
||||||
|
|
||||||
|
rebootRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare the new network interface configuration.
|
// Prepare the new network interface configuration.
|
||||||
networkInterface := d.Get(mkResourceVirtualEnvironmentContainerNetworkInterface).([]interface{})
|
networkInterface := d.Get(mkResourceVirtualEnvironmentContainerNetworkInterface).([]interface{})
|
||||||
|
|
||||||
@ -2319,7 +2595,7 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
|
|
||||||
if d.HasChange(mkResourceVirtualEnvironmentContainerStarted) && !bool(template) {
|
if d.HasChange(mkResourceVirtualEnvironmentContainerStarted) && !bool(template) {
|
||||||
if started {
|
if started {
|
||||||
e = containerAPI.StartContainer(ctx)
|
e = containerAPI.StartContainer(ctx, 60)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return diag.FromErr(e)
|
return diag.FromErr(e)
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ func TestContainerSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentContainerDisk,
|
mkResourceVirtualEnvironmentContainerDisk,
|
||||||
mkResourceVirtualEnvironmentContainerInitialization,
|
mkResourceVirtualEnvironmentContainerInitialization,
|
||||||
mkResourceVirtualEnvironmentContainerMemory,
|
mkResourceVirtualEnvironmentContainerMemory,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPoint,
|
||||||
mkResourceVirtualEnvironmentContainerOperatingSystem,
|
mkResourceVirtualEnvironmentContainerOperatingSystem,
|
||||||
mkResourceVirtualEnvironmentContainerPoolID,
|
mkResourceVirtualEnvironmentContainerPoolID,
|
||||||
mkResourceVirtualEnvironmentContainerStarted,
|
mkResourceVirtualEnvironmentContainerStarted,
|
||||||
@ -56,6 +57,7 @@ func TestContainerSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentContainerDisk: schema.TypeList,
|
mkResourceVirtualEnvironmentContainerDisk: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentContainerInitialization: schema.TypeList,
|
mkResourceVirtualEnvironmentContainerInitialization: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentContainerMemory: schema.TypeList,
|
mkResourceVirtualEnvironmentContainerMemory: schema.TypeList,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPoint: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentContainerOperatingSystem: schema.TypeList,
|
mkResourceVirtualEnvironmentContainerOperatingSystem: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentContainerPoolID: schema.TypeString,
|
mkResourceVirtualEnvironmentContainerPoolID: schema.TypeString,
|
||||||
mkResourceVirtualEnvironmentContainerStarted: schema.TypeBool,
|
mkResourceVirtualEnvironmentContainerStarted: schema.TypeBool,
|
||||||
@ -229,6 +231,32 @@ func TestContainerSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentContainerMemorySwap: schema.TypeInt,
|
mkResourceVirtualEnvironmentContainerMemorySwap: schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mountPointSchema := test.AssertNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentContainerMountPoint)
|
||||||
|
|
||||||
|
test.AssertOptionalArguments(t, mountPointSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointACL,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointBackup,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointMountOptions,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointQuota,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReadOnly,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReplicate,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointShared,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointSize,
|
||||||
|
})
|
||||||
|
|
||||||
|
test.AssertValueTypes(t, mountPointSchema, map[string]schema.ValueType{
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointACL: schema.TypeBool,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointBackup: schema.TypeBool,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointMountOptions: schema.TypeList,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointPath: schema.TypeString,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointQuota: schema.TypeBool,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReadOnly: schema.TypeBool,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointReplicate: schema.TypeBool,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointShared: schema.TypeBool,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointSize: schema.TypeString,
|
||||||
|
mkResourceVirtualEnvironmentContainerMountPointVolume: schema.TypeString,
|
||||||
|
})
|
||||||
|
|
||||||
networkInterfaceSchema := test.AssertNestedSchemaExistence(
|
networkInterfaceSchema := test.AssertNestedSchemaExistence(
|
||||||
t,
|
t,
|
||||||
s,
|
s,
|
||||||
|
@ -211,7 +211,6 @@ func getFileIDValidator() schema.SchemaValidateDiagFunc {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unused
|
|
||||||
func getFileSizeValidator() schema.SchemaValidateDiagFunc {
|
func getFileSizeValidator() schema.SchemaValidateDiagFunc {
|
||||||
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
|
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
|
||||||
v, ok := i.(string)
|
v, ok := i.(string)
|
||||||
|
Loading…
Reference in New Issue
Block a user