0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-06-30 18:42:58 +00:00

feat: add support for 'import' content type in Proxmox file resources

Signed-off-by: Marco Attia <54147992+Vaneixus@users.noreply.github.com>
This commit is contained in:
Marco Attia 2025-06-06 15:51:17 +00:00
parent b6bcfe75aa
commit d88f0efd75
No known key found for this signature in database
5 changed files with 46 additions and 18 deletions

View File

@ -92,4 +92,4 @@ Goal is to have a proxmox node in VM using <https://virt-manager.org/> for a job
10. Now you can run `make example`. 10. Now you can run `make example`.
11. If you see error with proxmox_virtual_environment_file: the datastore "local" does not support content type "snippets"; supported content types are: `[backup, iso, vztmpl]`, you need to enable them, see <https://registry.terraform.io/providers/bpg/proxmox/latest/docs/resources/virtual_environment_file#snippets>. 11. If you see error with proxmox_virtual_environment_file: the datastore "local" does not support content type "snippets"; supported content types are: `[backup, iso, vztmpl, import]`, you need to enable them, see <https://registry.terraform.io/providers/bpg/proxmox/latest/docs/resources/virtual_environment_file#snippets>.

View File

@ -126,6 +126,7 @@ resource "proxmox_virtual_environment_file" "ubuntu_container_template" {
- `backup` (allowed extensions: `.vzdump`, `.tar.gz`, `.tar.xz`, `tar.zst`) - `backup` (allowed extensions: `.vzdump`, `.tar.gz`, `.tar.xz`, `tar.zst`)
- `iso` (allowed extensions: `.iso`, `.img`) - `iso` (allowed extensions: `.iso`, `.img`)
- `snippets` (allowed extensions: any) - `snippets` (allowed extensions: any)
- `import` (allowed extensions: `.raw`, `.qcow2`, `.vmdk`)
- `vztmpl` (allowed extensions: `.tar.gz`, `.tar.xz`, `tar.zst`) - `vztmpl` (allowed extensions: `.tar.gz`, `.tar.xz`, `tar.zst`)
- `datastore_id` - (Required) The datastore id. - `datastore_id` - (Required) The datastore id.
- `file_mode` - The file mode in octal format, e.g. `0700` or `600`. Note that the prefixes `0o` and `0x` is not supported! Setting this attribute is also only allowed for `root@pam` authenticated user. - `file_mode` - The file mode in octal format, e.g. `0700` or `600`. Note that the prefixes `0o` and `0x` is not supported! Setting this attribute is also only allowed for `root@pam` authenticated user.

View File

@ -205,23 +205,22 @@ func (r *downloadFileResource) Schema(
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": attribute.ResourceID(), "id": attribute.ResourceID(),
"content_type": schema.StringAttribute{ "content_type": schema.StringAttribute{
Description: "The file content type. Must be `iso` for VM images or `vztmpl` for LXC images.", Description: "The file content type. Must be `iso` or `import` for VM images or `vztmpl` for LXC images.",
Required: true, Required: true,
Validators: []validator.String{stringvalidator.OneOf([]string{ Validators: []validator.String{stringvalidator.OneOf([]string{
"iso", "iso",
"vztmpl", "vztmpl",
"import",
}...)}, }...)},
PlanModifiers: []planmodifier.String{ PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(), stringplanmodifier.RequiresReplace(),
}, },
}, },
"file_name": schema.StringAttribute{ "file_name": schema.StringAttribute{
Description: "The file name. If not provided, it is calculated " + Description: "The file name. If not provided, it is calculated using `url`.",
"using `url`. PVE will raise 'wrong file extension' error for some popular " + Computed: true,
"extensions file `.raw` or `.qcow2`. Workaround is to use e.g. `.img` instead.", Required: false,
Computed: true, Optional: true,
Required: false,
Optional: true,
PlanModifiers: []planmodifier.String{ PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(), stringplanmodifier.RequiresReplace(),
stringplanmodifier.UseStateForUnknown(), stringplanmodifier.UseStateForUnknown(),

View File

@ -20,6 +20,7 @@ import (
"path/filepath" "path/filepath"
"slices" "slices"
"sort" "sort"
"strconv"
"strings" "strings"
"time" "time"
@ -325,9 +326,6 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
var diags diag.Diagnostics var diags diag.Diagnostics
contentType, dg := fileGetContentType(d)
diags = append(diags, dg...)
fileName, err := fileGetSourceFileName(d) fileName, err := fileGetSourceFileName(d)
diags = append(diags, diag.FromErr(err)...) diags = append(diags, diag.FromErr(err)...)
@ -345,6 +343,9 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
return diag.FromErr(err) return diag.FromErr(err)
} }
contentType, dg := fileGetContentType(ctx, d, capi)
diags = append(diags, dg...)
list, err := capi.Node(nodeName).Storage(datastoreID).ListDatastoreFiles(ctx) list, err := capi.Node(nodeName).Storage(datastoreID).ListDatastoreFiles(ctx)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
@ -553,7 +554,7 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
} }
switch *contentType { switch *contentType {
case "iso", "vztmpl": case "iso", "vztmpl", "import":
_, err = capi.Node(nodeName).Storage(datastoreID).APIUpload( _, err = capi.Node(nodeName).Storage(datastoreID).APIUpload(
ctx, request, config.TempDir(), ctx, request, config.TempDir(),
) )
@ -600,7 +601,7 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
} }
volID, di := fileGetVolumeID(d) volID, di := fileGetVolumeID(ctx, d, capi)
diags = append(diags, di...) diags = append(diags, di...)
if diags.HasError() { if diags.HasError() {
return diags return diags
@ -617,11 +618,33 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
return diags return diags
} }
func fileGetContentType(d *schema.ResourceData) (*string, diag.Diagnostics) { func fileGetContentType(ctx context.Context, d *schema.ResourceData, c proxmoxtf.ProviderConfiguration) (*string, diag.Diagnostics) {
contentType := d.Get(mkResourceVirtualEnvironmentFileContentType).(string) contentType := d.Get(mkResourceVirtualEnvironmentFileContentType).(string)
sourceFile := d.Get(mkResourceVirtualEnvironmentFileSourceFile).([]interface{}) sourceFile := d.Get(mkResourceVirtualEnvironmentFileSourceFile).([]interface{})
sourceRaw := d.Get(mkResourceVirtualEnvironmentFileSourceRaw).([]interface{}) sourceRaw := d.Get(mkResourceVirtualEnvironmentFileSourceRaw).([]interface{})
release := 0.0
pc, err := c.GetClient()
if err != nil {
tflog.Warn(ctx, "failed to determine Proxmox Client API version", map[string]interface{}{
"error": err,
})
} else {
version, err := pc.Version().Version(context.Background())
if err != nil {
tflog.Warn(ctx, "failed to determine Proxmox VE version", map[string]interface{}{
"error": err,
})
} else {
release, err = strconv.ParseFloat(version.Release, 32)
if err != nil {
tflog.Warn(ctx, "failed to parse Proxmox VE version", map[string]interface{}{
"error": err,
})
}
}
}
sourceFilePath := "" sourceFilePath := ""
if len(sourceFile) > 0 { if len(sourceFile) > 0 {
@ -638,22 +661,26 @@ func fileGetContentType(d *schema.ResourceData) (*string, diag.Diagnostics) {
mkResourceVirtualEnvironmentFileSourceRaw, mkResourceVirtualEnvironmentFileSourceRaw,
) )
} }
if contentType == "" { if contentType == "" {
if strings.HasSuffix(sourceFilePath, ".tar.gz") || if strings.HasSuffix(sourceFilePath, ".tar.gz") ||
strings.HasSuffix(sourceFilePath, ".tar.xz") { strings.HasSuffix(sourceFilePath, ".tar.xz") {
contentType = "vztmpl" contentType = "vztmpl"
} else if release > 8.4 && (strings.HasSuffix(sourceFilePath, ".qcow2") ||
strings.HasSuffix(sourceFilePath, ".raw") ||
strings.HasSuffix(sourceFilePath, ".vmdk")) {
contentType = "import"
} else { } else {
ext := strings.TrimLeft(strings.ToLower(filepath.Ext(sourceFilePath)), ".") ext := strings.TrimLeft(strings.ToLower(filepath.Ext(sourceFilePath)), ".")
switch ext { switch ext {
case "img", "iso": case "iso":
contentType = "iso" contentType = "iso"
case "yaml", "yml": case "yaml", "yml":
contentType = "snippets" contentType = "snippets"
} }
} }
// We cannot determine, for example, the content type of an .img file, so we require the user to specify it.
if contentType == "" { if contentType == "" {
return nil, diag.Errorf( return nil, diag.Errorf(
"cannot determine the content type of source \"%s\" - Please manually define the \"%s\" argument", "cannot determine the content type of source \"%s\" - Please manually define the \"%s\" argument",
@ -715,14 +742,14 @@ func fileGetSourceFileName(d *schema.ResourceData) (*string, error) {
return &sourceFileFileName, nil return &sourceFileFileName, nil
} }
func fileGetVolumeID(d *schema.ResourceData) (fileVolumeID, diag.Diagnostics) { func fileGetVolumeID(ctx context.Context, d *schema.ResourceData, c proxmoxtf.ProviderConfiguration) (fileVolumeID, diag.Diagnostics) {
fileName, err := fileGetSourceFileName(d) fileName, err := fileGetSourceFileName(d)
if err != nil { if err != nil {
return fileVolumeID{}, diag.FromErr(err) return fileVolumeID{}, diag.FromErr(err)
} }
datastoreID := d.Get(mkResourceVirtualEnvironmentFileDatastoreID).(string) datastoreID := d.Get(mkResourceVirtualEnvironmentFileDatastoreID).(string)
contentType, diags := fileGetContentType(d) contentType, diags := fileGetContentType(ctx, d, c)
return fileVolumeID{ return fileVolumeID{
datastoreID: datastoreID, datastoreID: datastoreID,

View File

@ -24,6 +24,7 @@ func ContentType() schema.SchemaValidateDiagFunc {
"iso", "iso",
"snippets", "snippets",
"vztmpl", "vztmpl",
"import",
}, false)) }, false))
} }