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(lxc): add support for lxc mount points #394

Merged
merged 4 commits into from
Jul 17, 2023
Merged
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
25 changes: 24 additions & 1 deletion docs/resources/virtual_environment_container.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ resource "proxmox_virtual_environment_container" "ubuntu_container" {
template_file_id = proxmox_virtual_environment_file.ubuntu_container_template.id
type = "ubuntu"
}

mount_point {
volume = "/mnt/bindmounts/shared"
path = "/shared"
}

}

resource "proxmox_virtual_environment_file" "ubuntu_container_template" {
Expand Down Expand Up @@ -136,14 +142,31 @@ output "ubuntu_container_public_key" {
- `dedicated` - (Optional) The dedicated memory in megabytes (defaults
to `512`).
- `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
supported).
- `bridge` - (Optional) The name of the network bridge (defaults
to `vmbr0`).
- `enabled` - (Optional) Whether to enable the network device (defaults
to `true`).
- `firewall` - (Optional) Whether this interface's firewall rules should be
used (defaults to `false`).
used (defaults to `false`).
- `mac_address` - (Optional) The MAC address.
- `mtu` - (Optional) Maximum transfer unit of the interface. Cannot be
larger than the bridge's MTU.
Expand Down
5 changes: 5 additions & 0 deletions example/resource_virtual_environment_container.tf
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ resource "proxmox_virtual_environment_container" "example" {
hostname = "terraform-provider-proxmox-example-lxc"
}

mount_point {
volume = "/mnt/bindmounts/shared"
path = "/shared"
}

node_name = data.proxmox_virtual_environment_nodes.example.names[0]
pool_id = proxmox_virtual_environment_pool.example.id
vm_id = 2043
Expand Down
8 changes: 8 additions & 0 deletions proxmox/nodes/containers/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
"github.com/bpg/terraform-provider-proxmox/proxmox/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.
Expand All @@ -34,6 +35,13 @@ func (c *Client) ExpandPath(path string) string {
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.
func (c *Client) Firewall() firewall.API {
return &containerfirewall.Client{
Expand Down
54 changes: 48 additions & 6 deletions proxmox/nodes/containers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,36 @@ func (c *Client) CloneContainer(ctx context.Context, d *CloneRequestBody) error
}

// CreateContainer creates a container.
func (c *Client) CreateContainer(ctx context.Context, d *CreateRequestBody) error {
err := c.DoRequest(ctx, http.MethodPost, c.basePath(), d, nil)
func (c *Client) CreateContainer(ctx context.Context, d *CreateRequestBody, timeout int) error {
taskID, err := c.CreateContainerAsync(ctx, d)
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
}

// 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.
func (c *Client) DeleteContainer(ctx context.Context) error {
err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath(""), nil, nil)
Expand Down Expand Up @@ -99,15 +120,36 @@ func (c *Client) ShutdownContainer(ctx context.Context, d *ShutdownRequestBody)
}

// StartContainer starts a container.
func (c *Client) StartContainer(ctx context.Context) error {
err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/start"), nil, nil)
func (c *Client) StartContainer(ctx context.Context, timeout int) error {
taskID, err := c.StartContainerAsync(ctx)
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
}

// 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.
func (c *Client) StopContainer(ctx context.Context) error {
err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/stop"), nil, nil)
Expand Down
28 changes: 21 additions & 7 deletions proxmox/nodes/containers/containers_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ type CustomFeatures struct {
type CustomMountPoint struct {
ACL *types.CustomBool `json:"acl,omitempty" url:"acl,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:"-"`
MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"`
MountPoint string `json:"mp" url:"mp"`
Expand Down Expand Up @@ -141,6 +141,11 @@ type CustomStartupBehavior struct {
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.
type GetResponseBody struct {
Data *GetResponseData `json:"data,omitempty"`
Expand All @@ -164,10 +169,14 @@ type GetResponseData struct {
Hostname *string `json:"hostname,omitempty"`
Lock *types.CustomBool `json:"lock,omitempty"`
LXCConfiguration *[][2]string `json:"lxc,omitempty"`
MountPoint0 CustomMountPoint `json:"mp0,omitempty"`
MountPoint1 CustomMountPoint `json:"mp1,omitempty"`
MountPoint2 CustomMountPoint `json:"mp2,omitempty"`
MountPoint3 CustomMountPoint `json:"mp3,omitempty"`
MountPoint0 *CustomMountPoint `json:"mp0,omitempty"`
MountPoint1 *CustomMountPoint `json:"mp1,omitempty"`
MountPoint2 *CustomMountPoint `json:"mp2,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"`
NetworkInterface1 *CustomNetworkInterface `json:"net1,omitempty"`
NetworkInterface2 *CustomNetworkInterface `json:"net2,omitempty"`
Expand Down Expand Up @@ -207,6 +216,11 @@ type GetStatusResponseData struct {
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.
type RebootRequestBody struct {
Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"`
Expand Down Expand Up @@ -288,7 +302,7 @@ func (r *CustomMountPoint) EncodeValues(key string, v *url.Values) error {

if r.MountOptions != nil {
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, ";")))
}
}

Expand All @@ -311,7 +325,7 @@ func (r *CustomMountPoint) EncodeValues(key string, v *url.Values) error {
}

if r.Replicate != nil {
if *r.ReadOnly {
if *r.Replicate {
values = append(values, "replicate=1")
} else {
values = append(values, "replicate=0")
Expand Down
Loading