Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mountoptions support to lxc #1235

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions docs/resources/lxc.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ resource "proxmox_lxc" "multiple_mountpoints" {

// Device Mount Point
mountpoint {
key = "2"
slot = 2
storage = "/dev/sdg"
volume = "/dev/sdg"
mp = "/mnt/container/device-mount-point"
size = "32G"
key = "2"
slot = 2
storage = "/dev/sdg"
volume = "/dev/sdg"
mp = "/mnt/container/device-mount-point"
size = "32G"
}

network {
Expand Down Expand Up @@ -127,6 +127,14 @@ resource "proxmox_lxc" "advanced_features" {
storage = "/mnt/host/nfs"
mp = "/mnt/container/nfs"
size = "250G"

// mountoptions can be omitted: all options will be disabled.
// Each option should be set to true as follows:
// Don't set options to false; simply omit them.
mountoptions = {
"lazytime" : true,
"nodev" : true
}
}

network {
Expand Down Expand Up @@ -219,6 +227,7 @@ The following arguments may be optionally defined when using this resource:
* `quota` - A boolean for enabling user quotas inside the container for this mount point. Default is `false`.
* `replicate` - A boolean for including this volume in a storage replica job. Default is `false`.
* `shared` - A boolean for marking the volume as available on all nodes. Default is `false`.
* `mountoptions` - A map for setting the lxc mount options disks. Default is `null` meaning all options disabled.
* `nameserver` - The DNS server IP address used by the container. If neither `nameserver` nor `searchdomain` are
specified, the values of the Proxmox host will be used by default.
* `network` - An object defining a network interface for the container. Can be specified multiple times.
Expand Down
71 changes: 71 additions & 0 deletions proxmox/resource_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,30 @@ func resourceLxc() *schema.Resource {
Optional: true,
Computed: true,
},
"mountoptions": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeBool,
},
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
allowedOptions := map[string]bool{
"lazytime": true,
"noexec": true,
"nosuid": true,
"nodev": true,
"discard": true,
"noatime": true,
}
for k := range val.(map[string]interface{}) {
if _, ok := allowedOptions[k]; !ok {
errs = append(errs, fmt.Errorf("invalid mount option %s", k))
}
}
return warns, errs
},
Description: "Map of mount options. Allowed options: discard, lazytime, noatime, noexec, nosuid, nodev",
},
},
},
},
Expand Down Expand Up @@ -515,6 +539,18 @@ func resourceLxcCreate(ctx context.Context, d *schema.ResourceData, meta interfa
mountpoints := d.Get("mountpoint").([]interface{})
if len(mountpoints) > 0 {
lxcMountpoints := DevicesListToDevices(mountpoints, "slot")
// Then handle `mountoptions` inside each mountpoint
for _, mp := range lxcMountpoints {
if mountoptions, ok := mp["mountoptions"]; ok {
if len(mountoptions.([]interface{})) > 0 {
// Assign first element to mountoptions
mp["mountoptions"] = mountoptions.([]interface{})[0]
} else {
// If empty, delete mountoptions from this mountpoint
delete(mp, "mountoptions")
}
}
}
config.Mountpoints = lxcMountpoints
}

Expand Down Expand Up @@ -707,6 +743,10 @@ func resourceLxcUpdate(ctx context.Context, d *schema.ResourceData, meta interfa
oldSet, newSet := d.GetChange("mountpoint")
oldMounts := DevicesListToMapByKey(oldSet.([]interface{}), "key")
newMounts := DevicesListToMapByKey(newSet.([]interface{}), "key")

// Removing mountoptions if no options provided
newMounts = formatMountOptions(newMounts)

processLxcDiskChanges(ctx, oldMounts, newMounts, pconf, vmr)

lxcMountpoints := DevicesListToDevices(newSet.([]interface{}), "slot")
Expand Down Expand Up @@ -942,6 +982,10 @@ func processLxcDiskChanges(

if ok {
for k, v := range prevDisk {
if k == "mountoptions" {
continue // No override of mountoptions by preDisk, necessary in case newDisk mountoptions null
}

_, ok := newDisk[k]
if !ok {
newDisk[k] = v
Expand Down Expand Up @@ -1079,3 +1123,30 @@ func processLxcNetworkChanges(ctx context.Context, prevNetworks []map[string]int

return nil
}

// Remove options set to false and mountoptions block if empty
func formatMountOptions(mountpoints KeyedDeviceMap) KeyedDeviceMap {
// Initialize the result map to store cleaned mountpoints
cleanedMounts := make(KeyedDeviceMap)

// Iterate over each device in the KeyedDeviceMap
for key, mount := range mountpoints {
// Get mountoptions, and immediately check if it is a valid map with length > 0
if optionsMap, ok := mount["mountoptions"].(map[string]interface{}); ok {

// Remove false options
for optionKey, optionValue := range optionsMap {
if !optionValue.(bool) {
delete(optionsMap, optionKey)
}
}
if len(optionsMap) > 0 {
mount["mountoptions"] = optionsMap
} else {
delete(mount, "mountoptions") // Delete if it's not valid or if it's empty
}
}
cleanedMounts[key] = mount
}
return cleanedMounts
}