mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-02 19:43:00 +00:00
feat(lxc): add support for device_passthrough
config option (#1706)
Signed-off-by: Tarasa24 <tarasa24@tarasa24.dev>
This commit is contained in:
parent
c57dc78119
commit
7cbd1b46fa
@ -170,6 +170,13 @@ output "ubuntu_container_public_key" {
|
|||||||
Can be specified with a unit suffix (e.g. `10G`).
|
Can be specified with a unit suffix (e.g. `10G`).
|
||||||
- `volume` (Required) Volume, device or directory to mount into the
|
- `volume` (Required) Volume, device or directory to mount into the
|
||||||
container.
|
container.
|
||||||
|
- `device_passthrough` - (Optional) Device to pass through to the container (multiple blocks supported).
|
||||||
|
- `deny_write` - (Optional) Deny the container to write to the device (defaults to `false`).
|
||||||
|
- `gid` - (Optional) Group ID to be assigned to the device node.
|
||||||
|
- `mode` - (Optional) Access mode to be set on the device node. Must be a
|
||||||
|
4-digit octal number.
|
||||||
|
- `path` - (Required) Device to pass through to the container (e.g. `/dev/sda`).
|
||||||
|
- `uid` - (Optional) User ID to be assigned to the device node.
|
||||||
- `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
|
||||||
|
@ -75,6 +75,9 @@ func TestAccResourceContainer(t *testing.T) {
|
|||||||
size = "4G"
|
size = "4G"
|
||||||
path = "mnt/local"
|
path = "mnt/local"
|
||||||
}
|
}
|
||||||
|
device_passthrough {
|
||||||
|
path = "/dev/zero"
|
||||||
|
}
|
||||||
description = <<-EOT
|
description = <<-EOT
|
||||||
my
|
my
|
||||||
description
|
description
|
||||||
@ -199,6 +202,9 @@ func TestAccResourceContainer(t *testing.T) {
|
|||||||
size = "4G"
|
size = "4G"
|
||||||
path = "mnt/local"
|
path = "mnt/local"
|
||||||
}
|
}
|
||||||
|
device_passthrough {
|
||||||
|
path = "/dev/zero"
|
||||||
|
}
|
||||||
initialization {
|
initialization {
|
||||||
hostname = "test"
|
hostname = "test"
|
||||||
ip_config {
|
ip_config {
|
||||||
|
@ -31,45 +31,46 @@ type CloneRequestBody struct {
|
|||||||
|
|
||||||
// CreateRequestBody contains the data for a user create request.
|
// CreateRequestBody contains the data for a user create request.
|
||||||
type CreateRequestBody struct {
|
type CreateRequestBody struct {
|
||||||
BandwidthLimit *float64 `json:"bwlimit,omitempty" url:"bwlimit,omitempty"`
|
BandwidthLimit *float64 `json:"bwlimit,omitempty" url:"bwlimit,omitempty"`
|
||||||
ConsoleEnabled *types.CustomBool `json:"console,omitempty" url:"console,omitempty,int"`
|
ConsoleEnabled *types.CustomBool `json:"console,omitempty" url:"console,omitempty,int"`
|
||||||
ConsoleMode *string `json:"cmode,omitempty" url:"cmode,omitempty"`
|
ConsoleMode *string `json:"cmode,omitempty" url:"cmode,omitempty"`
|
||||||
CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"`
|
CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"`
|
||||||
CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"`
|
CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"`
|
||||||
CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"`
|
CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"`
|
||||||
CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"`
|
CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"`
|
||||||
DatastoreID *string `json:"storage,omitempty" url:"storage,omitempty"`
|
DatastoreID *string `json:"storage,omitempty" url:"storage,omitempty"`
|
||||||
DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"`
|
DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"`
|
||||||
Delete []string `json:"delete,omitempty" url:"delete,omitempty"`
|
Delete []string `json:"delete,omitempty" url:"delete,omitempty"`
|
||||||
Description *string `json:"description,omitempty" url:"description,omitempty"`
|
Description *string `json:"description,omitempty" url:"description,omitempty"`
|
||||||
DNSDomain *string `json:"searchdomain,omitempty" url:"searchdomain,omitempty"`
|
DNSDomain *string `json:"searchdomain,omitempty" url:"searchdomain,omitempty"`
|
||||||
DNSServer *string `json:"nameserver,omitempty" url:"nameserver,omitempty"`
|
DNSServer *string `json:"nameserver,omitempty" url:"nameserver,omitempty"`
|
||||||
Features *CustomFeatures `json:"features,omitempty" url:"features,omitempty"`
|
Features *CustomFeatures `json:"features,omitempty" url:"features,omitempty"`
|
||||||
Force *types.CustomBool `json:"force,omitempty" url:"force,omitempty,int"`
|
Force *types.CustomBool `json:"force,omitempty" url:"force,omitempty,int"`
|
||||||
HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"`
|
HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"`
|
||||||
Hostname *string `json:"hostname,omitempty" url:"hostname,omitempty"`
|
Hostname *string `json:"hostname,omitempty" url:"hostname,omitempty"`
|
||||||
IgnoreUnpackErrors *types.CustomBool `json:"ignore-unpack-errors,omitempty" url:"force,omitempty,int"`
|
IgnoreUnpackErrors *types.CustomBool `json:"ignore-unpack-errors,omitempty" url:"force,omitempty,int"`
|
||||||
Lock *string `json:"lock,omitempty" url:"lock,omitempty,int"`
|
Lock *string `json:"lock,omitempty" url:"lock,omitempty,int"`
|
||||||
MountPoints CustomMountPointArray `json:"mp,omitempty" url:"mp,omitempty,numbered"`
|
MountPoints CustomMountPointArray `json:"mp,omitempty" url:"mp,omitempty,numbered"`
|
||||||
NetworkInterfaces CustomNetworkInterfaceArray `json:"net,omitempty" url:"net,omitempty,numbered"`
|
DevicePassthrough CustomDevicePassthroughArray `json:"dev,omitempty" url:"dev,omitempty,numbered"`
|
||||||
OSTemplateFileVolume *string `json:"ostemplate,omitempty" url:"ostemplate,omitempty"`
|
NetworkInterfaces CustomNetworkInterfaceArray `json:"net,omitempty" url:"net,omitempty,numbered"`
|
||||||
OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"`
|
OSTemplateFileVolume *string `json:"ostemplate,omitempty" url:"ostemplate,omitempty"`
|
||||||
Password *string `json:"password,omitempty" url:"password,omitempty"`
|
OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"`
|
||||||
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
|
Password *string `json:"password,omitempty" url:"password,omitempty"`
|
||||||
Protection *types.CustomBool `json:"protection,omitempty" url:"protection,omitempty,int"`
|
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
|
||||||
Restore *types.CustomBool `json:"restore,omitempty" url:"restore,omitempty,int"`
|
Protection *types.CustomBool `json:"protection,omitempty" url:"protection,omitempty,int"`
|
||||||
RootFS *CustomRootFS `json:"rootfs,omitempty" url:"rootfs,omitempty"`
|
Restore *types.CustomBool `json:"restore,omitempty" url:"restore,omitempty,int"`
|
||||||
SSHKeys *CustomSSHKeys `json:"ssh-public-keys,omitempty" url:"ssh-public-keys,omitempty"`
|
RootFS *CustomRootFS `json:"rootfs,omitempty" url:"rootfs,omitempty"`
|
||||||
Start *types.CustomBool `json:"start,omitempty" url:"start,omitempty,int"`
|
SSHKeys *CustomSSHKeys `json:"ssh-public-keys,omitempty" url:"ssh-public-keys,omitempty"`
|
||||||
StartOnBoot *types.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"`
|
Start *types.CustomBool `json:"start,omitempty" url:"start,omitempty,int"`
|
||||||
StartupBehavior *CustomStartupBehavior `json:"startup,omitempty" url:"startup,omitempty"`
|
StartOnBoot *types.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"`
|
||||||
Swap *int `json:"swap,omitempty" url:"swap,omitempty"`
|
StartupBehavior *CustomStartupBehavior `json:"startup,omitempty" url:"startup,omitempty"`
|
||||||
Tags *string `json:"tags,omitempty" url:"tags,omitempty"`
|
Swap *int `json:"swap,omitempty" url:"swap,omitempty"`
|
||||||
Template *types.CustomBool `json:"template,omitempty" url:"template,omitempty,int"`
|
Tags *string `json:"tags,omitempty" url:"tags,omitempty"`
|
||||||
TTY *int `json:"tty,omitempty" url:"tty,omitempty"`
|
Template *types.CustomBool `json:"template,omitempty" url:"template,omitempty,int"`
|
||||||
Unique *types.CustomBool `json:"unique,omitempty" url:"unique,omitempty,int"`
|
TTY *int `json:"tty,omitempty" url:"tty,omitempty"`
|
||||||
Unprivileged *types.CustomBool `json:"unprivileged,omitempty" url:"unprivileged,omitempty,int"`
|
Unique *types.CustomBool `json:"unique,omitempty" url:"unique,omitempty,int"`
|
||||||
VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"`
|
Unprivileged *types.CustomBool `json:"unprivileged,omitempty" url:"unprivileged,omitempty,int"`
|
||||||
|
VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomFeatures contains the values for the "features" property.
|
// CustomFeatures contains the values for the "features" property.
|
||||||
@ -116,6 +117,18 @@ type CustomNetworkInterface struct {
|
|||||||
Type *string `json:"type,omitempty" url:"type,omitempty"`
|
Type *string `json:"type,omitempty" url:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CustomDevicePassthroughArray is an array of CustomDevicePassthrough.
|
||||||
|
type CustomDevicePassthroughArray []CustomDevicePassthrough
|
||||||
|
|
||||||
|
// CustomDevicePassthrough contains the values for the "dev[n]" properties.
|
||||||
|
type CustomDevicePassthrough struct {
|
||||||
|
DenyWrite *types.CustomBool `json:"deny-write,omitempty" url:"deny-write,omitempty,int"`
|
||||||
|
Path string `json:"path" url:"path"`
|
||||||
|
UID *int `json:"uid,omitempty" url:"uid,omitempty"`
|
||||||
|
GID *int `json:"gid,omitempty" url:"gid,omitempty"`
|
||||||
|
Mode *string `json:"mode,omitempty" url:"mode,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// CustomNetworkInterfaceArray is an array of CustomNetworkInterface.
|
// CustomNetworkInterfaceArray is an array of CustomNetworkInterface.
|
||||||
type CustomNetworkInterfaceArray []CustomNetworkInterface
|
type CustomNetworkInterfaceArray []CustomNetworkInterface
|
||||||
|
|
||||||
@ -153,48 +166,56 @@ type GetResponseBody struct {
|
|||||||
|
|
||||||
// GetResponseData contains the data from a user get response.
|
// GetResponseData contains the data from a user get response.
|
||||||
type GetResponseData struct {
|
type GetResponseData struct {
|
||||||
ConsoleEnabled *types.CustomBool `json:"console,omitempty"`
|
ConsoleEnabled *types.CustomBool `json:"console,omitempty"`
|
||||||
ConsoleMode *string `json:"cmode,omitempty"`
|
ConsoleMode *string `json:"cmode,omitempty"`
|
||||||
CPUArchitecture *string `json:"arch,omitempty"`
|
CPUArchitecture *string `json:"arch,omitempty"`
|
||||||
CPUCores *int `json:"cores,omitempty"`
|
CPUCores *int `json:"cores,omitempty"`
|
||||||
CPULimit *types.CustomInt `json:"cpulimit,omitempty"`
|
CPULimit *types.CustomInt `json:"cpulimit,omitempty"`
|
||||||
CPUUnits *int `json:"cpuunits,omitempty"`
|
CPUUnits *int `json:"cpuunits,omitempty"`
|
||||||
DedicatedMemory *int `json:"memory,omitempty"`
|
DedicatedMemory *int `json:"memory,omitempty"`
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
Digest string `json:"digest"`
|
Digest string `json:"digest"`
|
||||||
DNSDomain *string `json:"searchdomain,omitempty"`
|
DNSDomain *string `json:"searchdomain,omitempty"`
|
||||||
DNSServer *string `json:"nameserver,omitempty"`
|
DNSServer *string `json:"nameserver,omitempty"`
|
||||||
Features *CustomFeatures `json:"features,omitempty"`
|
Features *CustomFeatures `json:"features,omitempty"`
|
||||||
HookScript *string `json:"hookscript,omitempty"`
|
HookScript *string `json:"hookscript,omitempty"`
|
||||||
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"`
|
DevicePassthrough0 *CustomDevicePassthrough `json:"dev0,omitempty"`
|
||||||
MountPoint1 *CustomMountPoint `json:"mp1,omitempty"`
|
DevicePassthrough1 *CustomDevicePassthrough `json:"dev1,omitempty"`
|
||||||
MountPoint2 *CustomMountPoint `json:"mp2,omitempty"`
|
DevicePassthrough2 *CustomDevicePassthrough `json:"dev2,omitempty"`
|
||||||
MountPoint3 *CustomMountPoint `json:"mp3,omitempty"`
|
DevicePassthrough3 *CustomDevicePassthrough `json:"dev3,omitempty"`
|
||||||
MountPoint4 *CustomMountPoint `json:"mp4,omitempty"`
|
DevicePassthrough4 *CustomDevicePassthrough `json:"dev4,omitempty"`
|
||||||
MountPoint5 *CustomMountPoint `json:"mp5,omitempty"`
|
DevicePassthrough5 *CustomDevicePassthrough `json:"dev5,omitempty"`
|
||||||
MountPoint6 *CustomMountPoint `json:"mp6,omitempty"`
|
DevicePassthrough6 *CustomDevicePassthrough `json:"dev6,omitempty"`
|
||||||
MountPoint7 *CustomMountPoint `json:"mp7,omitempty"`
|
DevicePassthrough7 *CustomDevicePassthrough `json:"dev7,omitempty"`
|
||||||
NetworkInterface0 *CustomNetworkInterface `json:"net0,omitempty"`
|
MountPoint0 *CustomMountPoint `json:"mp0,omitempty"`
|
||||||
NetworkInterface1 *CustomNetworkInterface `json:"net1,omitempty"`
|
MountPoint1 *CustomMountPoint `json:"mp1,omitempty"`
|
||||||
NetworkInterface2 *CustomNetworkInterface `json:"net2,omitempty"`
|
MountPoint2 *CustomMountPoint `json:"mp2,omitempty"`
|
||||||
NetworkInterface3 *CustomNetworkInterface `json:"net3,omitempty"`
|
MountPoint3 *CustomMountPoint `json:"mp3,omitempty"`
|
||||||
NetworkInterface4 *CustomNetworkInterface `json:"net4,omitempty"`
|
MountPoint4 *CustomMountPoint `json:"mp4,omitempty"`
|
||||||
NetworkInterface5 *CustomNetworkInterface `json:"net5,omitempty"`
|
MountPoint5 *CustomMountPoint `json:"mp5,omitempty"`
|
||||||
NetworkInterface6 *CustomNetworkInterface `json:"net6,omitempty"`
|
MountPoint6 *CustomMountPoint `json:"mp6,omitempty"`
|
||||||
NetworkInterface7 *CustomNetworkInterface `json:"net7,omitempty"`
|
MountPoint7 *CustomMountPoint `json:"mp7,omitempty"`
|
||||||
OSType *string `json:"ostype,omitempty"`
|
NetworkInterface0 *CustomNetworkInterface `json:"net0,omitempty"`
|
||||||
Protection *types.CustomBool `json:"protection,omitempty"`
|
NetworkInterface1 *CustomNetworkInterface `json:"net1,omitempty"`
|
||||||
RootFS *CustomRootFS `json:"rootfs,omitempty"`
|
NetworkInterface2 *CustomNetworkInterface `json:"net2,omitempty"`
|
||||||
StartOnBoot *types.CustomBool `json:"onboot,omitempty"`
|
NetworkInterface3 *CustomNetworkInterface `json:"net3,omitempty"`
|
||||||
StartupBehavior *CustomStartupBehavior `json:"startup,omitempty"`
|
NetworkInterface4 *CustomNetworkInterface `json:"net4,omitempty"`
|
||||||
Swap *int `json:"swap,omitempty"`
|
NetworkInterface5 *CustomNetworkInterface `json:"net5,omitempty"`
|
||||||
Tags *string `json:"tags,omitempty"`
|
NetworkInterface6 *CustomNetworkInterface `json:"net6,omitempty"`
|
||||||
Template *types.CustomBool `json:"template,omitempty"`
|
NetworkInterface7 *CustomNetworkInterface `json:"net7,omitempty"`
|
||||||
TTY *int `json:"tty,omitempty"`
|
OSType *string `json:"ostype,omitempty"`
|
||||||
Unprivileged *types.CustomBool `json:"unprivileged,omitempty"`
|
Protection *types.CustomBool `json:"protection,omitempty"`
|
||||||
|
RootFS *CustomRootFS `json:"rootfs,omitempty"`
|
||||||
|
StartOnBoot *types.CustomBool `json:"onboot,omitempty"`
|
||||||
|
StartupBehavior *CustomStartupBehavior `json:"startup,omitempty"`
|
||||||
|
Swap *int `json:"swap,omitempty"`
|
||||||
|
Tags *string `json:"tags,omitempty"`
|
||||||
|
Template *types.CustomBool `json:"template,omitempty"`
|
||||||
|
TTY *int `json:"tty,omitempty"`
|
||||||
|
Unprivileged *types.CustomBool `json:"unprivileged,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatusResponseBody contains the body from a container get status response.
|
// GetStatusResponseBody contains the body from a container get status response.
|
||||||
@ -276,6 +297,55 @@ func (r *CustomFeatures) EncodeValues(key string, v *url.Values) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeValues converts a CustomDevicePassthrough struct to a URL value.
|
||||||
|
func (r *CustomDevicePassthrough) EncodeValues(key string, v *url.Values) error {
|
||||||
|
var values []string
|
||||||
|
|
||||||
|
if r.DenyWrite != nil {
|
||||||
|
if *r.DenyWrite {
|
||||||
|
values = append(values, "deny-write=1")
|
||||||
|
} else {
|
||||||
|
values = append(values, "deny-write=0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Path != "" {
|
||||||
|
values = append(values, fmt.Sprintf("path=%s", r.Path))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.UID != nil {
|
||||||
|
values = append(values, fmt.Sprintf("uid=%d", *r.UID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.GID != nil {
|
||||||
|
values = append(values, fmt.Sprintf("gid=%d", *r.GID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Mode != nil && *r.Mode != "" {
|
||||||
|
values = append(values, fmt.Sprintf("mode=%s", *r.Mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(values) > 0 {
|
||||||
|
v.Add(key, strings.Join(values, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeValues converts a CustomDevicePassthroughArray array to multiple URL values.
|
||||||
|
func (r CustomDevicePassthroughArray) EncodeValues(
|
||||||
|
key string,
|
||||||
|
v *url.Values,
|
||||||
|
) error {
|
||||||
|
for i, d := range r {
|
||||||
|
if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil {
|
||||||
|
return fmt.Errorf("failed to encode CustomDevicePassthroughArray: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeValues converts a CustomMountPoint struct to a URL value.
|
// EncodeValues converts a CustomMountPoint struct to a URL value.
|
||||||
func (r *CustomMountPoint) EncodeValues(key string, v *url.Values) error {
|
func (r *CustomMountPoint) EncodeValues(key string, v *url.Values) error {
|
||||||
var values []string
|
var values []string
|
||||||
@ -587,6 +657,56 @@ func (r *CustomFeatures) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON converts a CustomDevicePassthrough string to an object.
|
||||||
|
func (r *CustomDevicePassthrough) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
|
||||||
|
err := json.Unmarshal(b, &s)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to unmarshal CustomDevicePassthrough: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs := strings.Split(s, ",")
|
||||||
|
|
||||||
|
var path string
|
||||||
|
|
||||||
|
for _, p := range pairs {
|
||||||
|
v := strings.Split(strings.TrimSpace(p), "=")
|
||||||
|
|
||||||
|
if len(v) == 1 {
|
||||||
|
path = v[0]
|
||||||
|
} else if len(v) == 2 {
|
||||||
|
switch v[0] {
|
||||||
|
case "deny-write":
|
||||||
|
bv := types.CustomBool(v[1] == "1")
|
||||||
|
r.DenyWrite = &bv
|
||||||
|
case "path":
|
||||||
|
path = v[1]
|
||||||
|
case "uid":
|
||||||
|
iv, err := strconv.Atoi(v[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to unmarshal 'uid': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.UID = &iv
|
||||||
|
case "gid":
|
||||||
|
iv, err := strconv.Atoi(v[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to unmarshal 'gid': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.GID = &iv
|
||||||
|
case "mode":
|
||||||
|
r.Mode = &v[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Path = path
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalJSON converts a CustomMountPoint string to an object.
|
// UnmarshalJSON converts a CustomMountPoint string to an object.
|
||||||
func (r *CustomMountPoint) UnmarshalJSON(b []byte) error {
|
func (r *CustomMountPoint) UnmarshalJSON(b []byte) error {
|
||||||
var s string
|
var s string
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -140,6 +141,12 @@ const (
|
|||||||
mkMountPointShared = "shared"
|
mkMountPointShared = "shared"
|
||||||
mkMountPointSize = "size"
|
mkMountPointSize = "size"
|
||||||
mkMountPointVolume = "volume"
|
mkMountPointVolume = "volume"
|
||||||
|
mkDevicePassthroughDenyWrite = "deny_write"
|
||||||
|
mkDevicePassthrough = "device_passthrough" // #nosec G101
|
||||||
|
mkDevicePassthroughPath = "path"
|
||||||
|
mkDevicePassthroughUID = "uid"
|
||||||
|
mkDevicePassthroughGID = "gid"
|
||||||
|
mkDevicePassthroughMode = "mode"
|
||||||
mkNetworkInterface = "network_interface"
|
mkNetworkInterface = "network_interface"
|
||||||
mkNetworkInterfaceBridge = "bridge"
|
mkNetworkInterfaceBridge = "bridge"
|
||||||
mkNetworkInterfaceEnabled = "enabled"
|
mkNetworkInterfaceEnabled = "enabled"
|
||||||
@ -680,6 +687,49 @@ func Container() *schema.Resource {
|
|||||||
MaxItems: 8,
|
MaxItems: 8,
|
||||||
MinItems: 0,
|
MinItems: 0,
|
||||||
},
|
},
|
||||||
|
mkDevicePassthrough: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "Device to pass through to the container",
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
mkDevicePassthroughDenyWrite: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Deny the container to write to the device",
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
mkDevicePassthroughGID: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "Group ID to be assigned to the device node",
|
||||||
|
Optional: true,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(0)),
|
||||||
|
},
|
||||||
|
mkDevicePassthroughMode: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "Access mode to be set on the device node (e.g. 0666)",
|
||||||
|
Optional: true,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.StringMatch(
|
||||||
|
regexp.MustCompile(`0[0-7]{3}`), "Octal access mode",
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
mkDevicePassthroughPath: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "Device to pass through to the container",
|
||||||
|
Required: true,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotEmpty),
|
||||||
|
},
|
||||||
|
mkDevicePassthroughUID: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "Device UID in the container",
|
||||||
|
Optional: true,
|
||||||
|
ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(0)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaxItems: 8,
|
||||||
|
MinItems: 0,
|
||||||
|
},
|
||||||
mkNetworkInterface: {
|
mkNetworkInterface: {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Description: "The network interfaces",
|
Description: "The network interfaces",
|
||||||
@ -1209,6 +1259,36 @@ func containerCreateClone(ctx context.Context, d *schema.ResourceData, m interfa
|
|||||||
updateBody.Swap = &memorySwap
|
updateBody.Swap = &memorySwap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
devicePassthrough := d.Get(mkDevicePassthrough).([]interface{})
|
||||||
|
|
||||||
|
devicePassthroughArray := make(
|
||||||
|
containers.CustomDevicePassthroughArray,
|
||||||
|
len(devicePassthrough),
|
||||||
|
)
|
||||||
|
|
||||||
|
for di, dv := range devicePassthrough {
|
||||||
|
devicePassthroughMap := dv.(map[string]interface{})
|
||||||
|
devicePassthroughObject := containers.CustomDevicePassthrough{}
|
||||||
|
|
||||||
|
denyWrite := types.CustomBool(
|
||||||
|
devicePassthroughMap[mkDevicePassthroughDenyWrite].(bool),
|
||||||
|
)
|
||||||
|
gid := devicePassthroughMap[mkDevicePassthroughGID].(int)
|
||||||
|
mode := devicePassthroughMap[mkDevicePassthroughMode].(string)
|
||||||
|
path := devicePassthroughMap[mkDevicePassthroughPath].(string)
|
||||||
|
uid := devicePassthroughMap[mkDevicePassthroughUID].(int)
|
||||||
|
|
||||||
|
devicePassthroughObject.DenyWrite = &denyWrite
|
||||||
|
devicePassthroughObject.GID = &gid
|
||||||
|
devicePassthroughObject.Mode = &mode
|
||||||
|
devicePassthroughObject.Path = path
|
||||||
|
devicePassthroughObject.UID = &uid
|
||||||
|
|
||||||
|
devicePassthroughArray[di] = devicePassthroughObject
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.DevicePassthrough = devicePassthroughArray
|
||||||
|
|
||||||
networkInterface := d.Get(mkNetworkInterface).([]interface{})
|
networkInterface := d.Get(mkNetworkInterface).([]interface{})
|
||||||
|
|
||||||
if len(networkInterface) == 0 {
|
if len(networkInterface) == 0 {
|
||||||
@ -2232,6 +2312,65 @@ func containerRead(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
initialization[mkInitializationHostname] = ""
|
initialization[mkInitializationHostname] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
devicePassthroughArray := []*containers.CustomDevicePassthrough{
|
||||||
|
containerConfig.DevicePassthrough0,
|
||||||
|
containerConfig.DevicePassthrough1,
|
||||||
|
containerConfig.DevicePassthrough2,
|
||||||
|
containerConfig.DevicePassthrough3,
|
||||||
|
containerConfig.DevicePassthrough4,
|
||||||
|
containerConfig.DevicePassthrough5,
|
||||||
|
containerConfig.DevicePassthrough6,
|
||||||
|
containerConfig.DevicePassthrough7,
|
||||||
|
}
|
||||||
|
|
||||||
|
devicePassthroughList := make([]interface{}, 0, len(devicePassthroughArray))
|
||||||
|
|
||||||
|
for _, dp := range devicePassthroughArray {
|
||||||
|
if dp == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
devicePassthrough := map[string]interface{}{}
|
||||||
|
|
||||||
|
if dp.DenyWrite != nil {
|
||||||
|
devicePassthrough[mkDevicePassthroughDenyWrite] = *dp.DenyWrite
|
||||||
|
} else {
|
||||||
|
devicePassthrough[mkDevicePassthroughDenyWrite] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if dp.GID != nil {
|
||||||
|
devicePassthrough[mkDevicePassthroughGID] = *dp.GID
|
||||||
|
} else {
|
||||||
|
devicePassthrough[mkDevicePassthroughGID] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if dp.Mode != nil {
|
||||||
|
devicePassthrough[mkDevicePassthroughMode] = *dp.Mode
|
||||||
|
} else {
|
||||||
|
devicePassthrough[mkDevicePassthroughMode] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
devicePassthrough[mkDevicePassthroughPath] = dp.Path
|
||||||
|
|
||||||
|
if dp.UID != nil {
|
||||||
|
devicePassthrough[mkDevicePassthroughUID] = *dp.UID
|
||||||
|
} else {
|
||||||
|
devicePassthrough[mkDevicePassthroughUID] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
devicePassthroughList = append(devicePassthroughList, devicePassthrough)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(clone) > 0 {
|
||||||
|
if len(devicePassthroughList) > 0 {
|
||||||
|
err := d.Set(mkDevicePassthrough, devicePassthroughList)
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
}
|
||||||
|
} else if len(devicePassthroughList) > 0 {
|
||||||
|
err := d.Set(mkDevicePassthrough, devicePassthroughList)
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
}
|
||||||
|
|
||||||
mountPointArray := []*containers.CustomMountPoint{
|
mountPointArray := []*containers.CustomMountPoint{
|
||||||
containerConfig.MountPoint0,
|
containerConfig.MountPoint0,
|
||||||
containerConfig.MountPoint1,
|
containerConfig.MountPoint1,
|
||||||
@ -2860,6 +2999,40 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
rebootRequired = true
|
rebootRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare the new device passthrough configuration.
|
||||||
|
if d.HasChange(mkDevicePassthrough) {
|
||||||
|
_, newDevicePassthrough := d.GetChange(mkDevicePassthrough)
|
||||||
|
|
||||||
|
devicePassthrough := newDevicePassthrough.([]interface{})
|
||||||
|
devicePassthroughArray := make(
|
||||||
|
containers.CustomDevicePassthroughArray,
|
||||||
|
len(devicePassthrough),
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, dp := range devicePassthrough {
|
||||||
|
devicePassthroughMap := dp.(map[string]interface{})
|
||||||
|
devicePassthroughObject := containers.CustomDevicePassthrough{}
|
||||||
|
|
||||||
|
denyWrite := types.CustomBool(devicePassthroughMap[mkDevicePassthroughDenyWrite].(bool))
|
||||||
|
gid := devicePassthroughMap[mkDevicePassthroughGID].(int)
|
||||||
|
mode := devicePassthroughMap[mkDevicePassthroughMode].(string)
|
||||||
|
path := devicePassthroughMap[mkDevicePassthroughPath].(string)
|
||||||
|
uid := devicePassthroughMap[mkDevicePassthroughUID].(int)
|
||||||
|
|
||||||
|
devicePassthroughObject.DenyWrite = &denyWrite
|
||||||
|
devicePassthroughObject.GID = &gid
|
||||||
|
devicePassthroughObject.Mode = &mode
|
||||||
|
devicePassthroughObject.Path = path
|
||||||
|
devicePassthroughObject.UID = &uid
|
||||||
|
|
||||||
|
devicePassthroughArray[i] = devicePassthroughObject
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody.DevicePassthrough = devicePassthroughArray
|
||||||
|
|
||||||
|
rebootRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare the new mount point configuration.
|
// Prepare the new mount point configuration.
|
||||||
if d.HasChange(mkMountPoint) {
|
if d.HasChange(mkMountPoint) {
|
||||||
_, newMountPoints := d.GetChange(mkMountPoint)
|
_, newMountPoints := d.GetChange(mkMountPoint)
|
||||||
|
@ -41,6 +41,7 @@ func TestContainerSchema(t *testing.T) {
|
|||||||
mkInitialization,
|
mkInitialization,
|
||||||
mkHookScriptFileID,
|
mkHookScriptFileID,
|
||||||
mkMemory,
|
mkMemory,
|
||||||
|
mkDevicePassthrough,
|
||||||
mkMountPoint,
|
mkMountPoint,
|
||||||
mkOperatingSystem,
|
mkOperatingSystem,
|
||||||
mkPoolID,
|
mkPoolID,
|
||||||
@ -55,23 +56,24 @@ func TestContainerSchema(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test.AssertValueTypes(t, s, map[string]schema.ValueType{
|
test.AssertValueTypes(t, s, map[string]schema.ValueType{
|
||||||
mkCPU: schema.TypeList,
|
mkCPU: schema.TypeList,
|
||||||
mkDescription: schema.TypeString,
|
mkDescription: schema.TypeString,
|
||||||
mkDisk: schema.TypeList,
|
mkDisk: schema.TypeList,
|
||||||
mkInitialization: schema.TypeList,
|
mkInitialization: schema.TypeList,
|
||||||
mkHookScriptFileID: schema.TypeString,
|
mkHookScriptFileID: schema.TypeString,
|
||||||
mkMemory: schema.TypeList,
|
mkMemory: schema.TypeList,
|
||||||
mkMountPoint: schema.TypeList,
|
mkDevicePassthrough: schema.TypeList,
|
||||||
mkOperatingSystem: schema.TypeList,
|
mkMountPoint: schema.TypeList,
|
||||||
mkPoolID: schema.TypeString,
|
mkOperatingSystem: schema.TypeList,
|
||||||
mkProtection: schema.TypeBool,
|
mkPoolID: schema.TypeString,
|
||||||
mkStarted: schema.TypeBool,
|
mkProtection: schema.TypeBool,
|
||||||
mkTags: schema.TypeList,
|
mkStarted: schema.TypeBool,
|
||||||
mkTemplate: schema.TypeBool,
|
mkTags: schema.TypeList,
|
||||||
mkUnprivileged: schema.TypeBool,
|
mkTemplate: schema.TypeBool,
|
||||||
mkStartOnBoot: schema.TypeBool,
|
mkUnprivileged: schema.TypeBool,
|
||||||
mkFeatures: schema.TypeList,
|
mkStartOnBoot: schema.TypeBool,
|
||||||
mkVMID: schema.TypeInt,
|
mkFeatures: schema.TypeList,
|
||||||
|
mkVMID: schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
cloneSchema := test.AssertNestedSchemaExistence(t, s, mkClone)
|
cloneSchema := test.AssertNestedSchemaExistence(t, s, mkClone)
|
||||||
@ -243,6 +245,27 @@ func TestContainerSchema(t *testing.T) {
|
|||||||
mkMemorySwap: schema.TypeInt,
|
mkMemorySwap: schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
devicePassthroughSchema := test.AssertNestedSchemaExistence(t, s, mkDevicePassthrough)
|
||||||
|
|
||||||
|
test.AssertRequiredArguments(t, devicePassthroughSchema, []string{
|
||||||
|
mkDevicePassthroughPath,
|
||||||
|
})
|
||||||
|
|
||||||
|
test.AssertOptionalArguments(t, devicePassthroughSchema, []string{
|
||||||
|
mkDevicePassthroughDenyWrite,
|
||||||
|
mkDevicePassthroughGID,
|
||||||
|
mkDevicePassthroughMode,
|
||||||
|
mkDevicePassthroughUID,
|
||||||
|
})
|
||||||
|
|
||||||
|
test.AssertValueTypes(t, devicePassthroughSchema, map[string]schema.ValueType{
|
||||||
|
mkDevicePassthroughDenyWrite: schema.TypeBool,
|
||||||
|
mkDevicePassthroughGID: schema.TypeInt,
|
||||||
|
mkDevicePassthroughMode: schema.TypeString,
|
||||||
|
mkDevicePassthroughPath: schema.TypeString,
|
||||||
|
mkDevicePassthroughUID: schema.TypeInt,
|
||||||
|
})
|
||||||
|
|
||||||
mountPointSchema := test.AssertNestedSchemaExistence(t, s, mkMountPoint)
|
mountPointSchema := test.AssertNestedSchemaExistence(t, s, mkMountPoint)
|
||||||
|
|
||||||
test.AssertOptionalArguments(t, mountPointSchema, []string{
|
test.AssertOptionalArguments(t, mountPointSchema, []string{
|
||||||
|
Loading…
Reference in New Issue
Block a user