mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 02:31:10 +00:00
feat(provider): configure temp directory (#607)
* feat(provider): configure temp directory Resource 'proxmox_virtual_environment_file' often requires lot of disk space in /tmp, which can be space-limited. Instead of requiring to set TMPDIR environment variable before running terraform, make it a provider configuration option. Signed-off-by: Oto Petřík <oto.petrik@gmail.com> * fix: lint error, align names in the `client` struct Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> --------- Signed-off-by: Oto Petřík <oto.petrik@gmail.com> Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
d36cf4eab8
commit
06ad00463c
@ -21,6 +21,7 @@ provider "proxmox" {
|
|||||||
username = "root@pam"
|
username = "root@pam"
|
||||||
password = "the-password-set-during-installation-of-proxmox-ve"
|
password = "the-password-set-during-installation-of-proxmox-ve"
|
||||||
insecure = true
|
insecure = true
|
||||||
|
tmp_dir = "/var/tmp"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -167,6 +168,15 @@ failed (changing feature flags for privileged container is only allowed for root
|
|||||||
> when using API Token authentication, even when `Administrator` role or
|
> when using API Token authentication, even when `Administrator` role or
|
||||||
> the `root@pam` user is used with the token.
|
> the `root@pam` user is used with the token.
|
||||||
|
|
||||||
|
### Temporary directory
|
||||||
|
|
||||||
|
Using `proxmox_virtual_environment_file` with `.iso` files or disk images can require
|
||||||
|
large amount of space in the temporary directory of the computer running terraform.
|
||||||
|
|
||||||
|
Consider pointing `tmp_dir` to a directory with enough space, especially if the default
|
||||||
|
temporary directory is limited by the system memory (e.g. `tmpfs` mounted
|
||||||
|
on `/tmp`).
|
||||||
|
|
||||||
## Argument Reference
|
## Argument Reference
|
||||||
|
|
||||||
In addition
|
In addition
|
||||||
@ -209,3 +219,4 @@ Proxmox `provider` block:
|
|||||||
- `name` - (Required) The name of the node.
|
- `name` - (Required) The name of the node.
|
||||||
- `address` - (Required) The IP address of the node.
|
- `address` - (Required) The IP address of the node.
|
||||||
- `port` - (Optional) SSH port of the node. Defaults to 22.
|
- `port` - (Optional) SSH port of the node. Defaults to 22.
|
||||||
|
- `tmp_dir` - (Optional) Use custom temporary directory. (can also be sourced from `PROXMOX_VE_TMPDIR`)
|
@ -70,6 +70,7 @@ type proxmoxProviderModel struct {
|
|||||||
Port types.Int64 `tfsdk:"port"`
|
Port types.Int64 `tfsdk:"port"`
|
||||||
} `tfsdk:"node"`
|
} `tfsdk:"node"`
|
||||||
} `tfsdk:"ssh"`
|
} `tfsdk:"ssh"`
|
||||||
|
TmpDir types.String `tfsdk:"tmp_dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *proxmoxProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
|
func (p *proxmoxProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
|
||||||
@ -119,6 +120,10 @@ func (p *proxmoxProvider) Schema(_ context.Context, _ provider.SchemaRequest, re
|
|||||||
Description: "The username for the Proxmox VE API.",
|
Description: "The username for the Proxmox VE API.",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
"tmp_dir": schema.StringAttribute{
|
||||||
|
Description: "The alternative temporary directory.",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Blocks: map[string]schema.Block{
|
Blocks: map[string]schema.Block{
|
||||||
// have to define it as a list due to backwards compatibility
|
// have to define it as a list due to backwards compatibility
|
||||||
@ -355,7 +360,14 @@ func (p *proxmoxProvider) Configure(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client := proxmox.NewClient(apiClient, sshClient)
|
// Intentionally use 'PROXMOX_VE_TMPDIR' with 'TMP' instead of 'TEMP', to match os.TempDir's use of $TMPDIR
|
||||||
|
tmpDirOverride := utils.GetAnyStringEnv("PROXMOX_VE_TMPDIR", "PM_VE_TMPDIR")
|
||||||
|
|
||||||
|
if !config.TmpDir.IsNull() {
|
||||||
|
tmpDirOverride = config.TmpDir.ValueString()
|
||||||
|
}
|
||||||
|
|
||||||
|
client := proxmox.NewClient(apiClient, sshClient, tmpDirOverride)
|
||||||
|
|
||||||
resp.ResourceData = client
|
resp.ResourceData = client
|
||||||
resp.DataSourceData = client
|
resp.DataSourceData = client
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
package proxmox
|
package proxmox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/access"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/access"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster"
|
||||||
@ -42,54 +44,67 @@ type Client interface {
|
|||||||
|
|
||||||
// SSH returns a lower-level SSH client.
|
// SSH returns a lower-level SSH client.
|
||||||
SSH() ssh.Client
|
SSH() ssh.Client
|
||||||
|
|
||||||
|
// TempDir returns (possibly overridden) os.TempDir().
|
||||||
|
TempDir() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
a api.Client
|
apiClient api.Client
|
||||||
s ssh.Client
|
sshClient ssh.Client
|
||||||
|
tmpDirOverride string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new API client.
|
// NewClient creates a new API client.
|
||||||
func NewClient(a api.Client, s ssh.Client) Client {
|
func NewClient(apiClient api.Client, sshClient ssh.Client, tmpDirOverride string) Client {
|
||||||
return &client{a: a, s: s}
|
return &client{apiClient: apiClient, sshClient: sshClient, tmpDirOverride: tmpDirOverride}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access returns a client for managing access control.
|
// Access returns a client for managing access control.
|
||||||
func (c *client) Access() *access.Client {
|
func (c *client) Access() *access.Client {
|
||||||
return &access.Client{Client: c.a}
|
return &access.Client{Client: c.apiClient}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cluster returns a client for managing the cluster.
|
// Cluster returns a client for managing the cluster.
|
||||||
func (c *client) Cluster() *cluster.Client {
|
func (c *client) Cluster() *cluster.Client {
|
||||||
return &cluster.Client{Client: c.a}
|
return &cluster.Client{Client: c.apiClient}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node returns a client for managing resources on a specific node.
|
// Node returns a client for managing resources on a specific node.
|
||||||
func (c *client) Node(nodeName string) *nodes.Client {
|
func (c *client) Node(nodeName string) *nodes.Client {
|
||||||
return &nodes.Client{Client: c.a, NodeName: nodeName}
|
return &nodes.Client{Client: c.apiClient, NodeName: nodeName}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pool returns a client for managing resource pools.
|
// Pool returns a client for managing resource pools.
|
||||||
func (c *client) Pool() *pools.Client {
|
func (c *client) Pool() *pools.Client {
|
||||||
return &pools.Client{Client: c.a}
|
return &pools.Client{Client: c.apiClient}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Storage returns a client for managing storage.
|
// Storage returns a client for managing storage.
|
||||||
func (c *client) Storage() *storage.Client {
|
func (c *client) Storage() *storage.Client {
|
||||||
return &storage.Client{Client: c.a}
|
return &storage.Client{Client: c.apiClient}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version returns a client for getting the version of the Proxmox Virtual Environment API.
|
// Version returns a client for getting the version of the Proxmox Virtual Environment API.
|
||||||
func (c *client) Version() *version.Client {
|
func (c *client) Version() *version.Client {
|
||||||
return &version.Client{Client: c.a}
|
return &version.Client{Client: c.apiClient}
|
||||||
}
|
}
|
||||||
|
|
||||||
// API returns a lower-lever REST API client.
|
// API returns a lower-lever REST API client.
|
||||||
func (c *client) API() api.Client {
|
func (c *client) API() api.Client {
|
||||||
return c.a
|
return c.apiClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSH returns a lower-lever SSH client.s.
|
// SSH returns a lower-lever SSH client.
|
||||||
func (c *client) SSH() ssh.Client {
|
func (c *client) SSH() ssh.Client {
|
||||||
return c.s
|
return c.sshClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// TempDir returns (possibly overridden) os.TempDir().
|
||||||
|
func (c *client) TempDir() string {
|
||||||
|
if c.tmpDirOverride != "" {
|
||||||
|
return c.tmpDirOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.TempDir()
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,7 @@ func (c *Client) APIUpload(
|
|||||||
datastoreID string,
|
datastoreID string,
|
||||||
d *api.FileUploadRequest,
|
d *api.FileUploadRequest,
|
||||||
uploadTimeout int,
|
uploadTimeout int,
|
||||||
|
tempDir string,
|
||||||
) (*DatastoreUploadResponseBody, error) {
|
) (*DatastoreUploadResponseBody, error) {
|
||||||
tflog.Debug(ctx, "uploading file to datastore using PVE API", map[string]interface{}{
|
tflog.Debug(ctx, "uploading file to datastore using PVE API", map[string]interface{}{
|
||||||
"file_name": d.FileName,
|
"file_name": d.FileName,
|
||||||
@ -205,7 +206,7 @@ func (c *Client) APIUpload(
|
|||||||
|
|
||||||
// We need to store the multipart content in a temporary file to avoid using high amounts of memory.
|
// We need to store the multipart content in a temporary file to avoid using high amounts of memory.
|
||||||
// This is necessary due to Proxmox VE not supporting chunked transfers in v6.1 and earlier versions.
|
// This is necessary due to Proxmox VE not supporting chunked transfers in v6.1 and earlier versions.
|
||||||
tempMultipartFile, err := os.CreateTemp("", "multipart")
|
tempMultipartFile, err := os.CreateTemp(tempDir, "multipart")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create temporary file: %w", err)
|
return nil, fmt.Errorf("failed to create temporary file: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ package proxmoxtf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox"
|
"github.com/bpg/terraform-provider-proxmox/proxmox"
|
||||||
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
"github.com/bpg/terraform-provider-proxmox/proxmox/api"
|
||||||
@ -16,18 +17,21 @@ import (
|
|||||||
|
|
||||||
// ProviderConfiguration is the configuration for the provider.
|
// ProviderConfiguration is the configuration for the provider.
|
||||||
type ProviderConfiguration struct {
|
type ProviderConfiguration struct {
|
||||||
apiClient api.Client
|
apiClient api.Client
|
||||||
sshClient ssh.Client
|
sshClient ssh.Client
|
||||||
|
tmpDirOverride string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProviderConfiguration creates a new provider configuration.
|
// NewProviderConfiguration creates a new provider configuration.
|
||||||
func NewProviderConfiguration(
|
func NewProviderConfiguration(
|
||||||
apiClient api.Client,
|
apiClient api.Client,
|
||||||
sshClient ssh.Client,
|
sshClient ssh.Client,
|
||||||
|
tmpDirOverride string,
|
||||||
) ProviderConfiguration {
|
) ProviderConfiguration {
|
||||||
return ProviderConfiguration{
|
return ProviderConfiguration{
|
||||||
apiClient: apiClient,
|
apiClient: apiClient,
|
||||||
sshClient: sshClient,
|
sshClient: sshClient,
|
||||||
|
tmpDirOverride: tmpDirOverride,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,5 +49,14 @@ func (c *ProviderConfiguration) GetClient() (proxmox.Client, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return proxmox.NewClient(c.apiClient, c.sshClient), nil
|
return proxmox.NewClient(c.apiClient, c.sshClient, c.tmpDirOverride), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TempDir returns (possibly overridden) os.TempDir().
|
||||||
|
func (c *ProviderConfiguration) TempDir() string {
|
||||||
|
if c.tmpDirOverride != "" {
|
||||||
|
return c.tmpDirOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.TempDir()
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,14 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{},
|
|||||||
return nil, diag.Errorf("error creating SSH client: %s", err)
|
return nil, diag.Errorf("error creating SSH client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := proxmoxtf.NewProviderConfiguration(apiClient, sshClient)
|
// Intentionally use 'PROXMOX_VE_TMPDIR' with 'TMP' instead of 'TEMP', to match os.TempDir's use of $TMPDIR
|
||||||
|
tmpDirOverride := utils.GetAnyStringEnv("PROXMOX_VE_TMPDIR", "PM_VE_TMPDIR")
|
||||||
|
|
||||||
|
if v, ok := d.GetOk(mkProviderTmpDir); ok {
|
||||||
|
tmpDirOverride = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := proxmoxtf.NewProviderConfiguration(apiClient, sshClient, tmpDirOverride)
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ const (
|
|||||||
mkProviderPassword = "password"
|
mkProviderPassword = "password"
|
||||||
mkProviderUsername = "username"
|
mkProviderUsername = "username"
|
||||||
mkProviderAPIToken = "api_token"
|
mkProviderAPIToken = "api_token"
|
||||||
|
mkProviderTmpDir = "tmp_dir"
|
||||||
mkProviderSSH = "ssh"
|
mkProviderSSH = "ssh"
|
||||||
mkProviderSSHUsername = "username"
|
mkProviderSSHUsername = "username"
|
||||||
mkProviderSSHPassword = "password"
|
mkProviderSSHPassword = "password"
|
||||||
@ -169,5 +170,11 @@ func createSchema() map[string]*schema.Schema {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mkProviderTmpDir: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "The alternative temporary directory.",
|
||||||
|
ValidateFunc: validation.StringIsNotEmpty,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,7 +395,7 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
|
|||||||
|
|
||||||
defer utils.CloseOrLogError(ctx)(res.Body)
|
defer utils.CloseOrLogError(ctx)(res.Body)
|
||||||
|
|
||||||
tempDownloadedFile, err := os.CreateTemp("", "download")
|
tempDownloadedFile, err := os.CreateTemp(config.TempDir(), "download")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
@ -471,8 +471,8 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempRawFile, err := os.CreateTemp("", "raw")
|
tempRawFile, e := os.CreateTemp(config.TempDir(), "raw")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,7 +522,7 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
|
|||||||
switch *contentType {
|
switch *contentType {
|
||||||
case "iso", "vztmpl":
|
case "iso", "vztmpl":
|
||||||
uploadTimeout := d.Get(mkResourceVirtualEnvironmentFileTimeoutUpload).(int)
|
uploadTimeout := d.Get(mkResourceVirtualEnvironmentFileTimeoutUpload).(int)
|
||||||
_, err = capi.Node(nodeName).APIUpload(ctx, datastoreID, request, uploadTimeout)
|
_, err = capi.Node(nodeName).APIUpload(ctx, datastoreID, request, uploadTimeout, config.TempDir())
|
||||||
default:
|
default:
|
||||||
// For all other content types, we need to upload the file to the node's
|
// For all other content types, we need to upload the file to the node's
|
||||||
// datastore using SFTP.
|
// datastore using SFTP.
|
||||||
|
Loading…
Reference in New Issue
Block a user