diff --git a/docs/guides/setup-proxmox-for-tests.md b/docs/guides/setup-proxmox-for-tests.md index 2d2f9f72..71ea146f 100644 --- a/docs/guides/setup-proxmox-for-tests.md +++ b/docs/guides/setup-proxmox-for-tests.md @@ -92,4 +92,4 @@ Goal is to have a proxmox node in VM using for a job 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 . +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 . diff --git a/docs/resources/virtual_environment_file.md b/docs/resources/virtual_environment_file.md index 90c36275..6fcd3fba 100644 --- a/docs/resources/virtual_environment_file.md +++ b/docs/resources/virtual_environment_file.md @@ -126,6 +126,7 @@ resource "proxmox_virtual_environment_file" "ubuntu_container_template" { - `backup` (allowed extensions: `.vzdump`, `.tar.gz`, `.tar.xz`, `tar.zst`) - `iso` (allowed extensions: `.iso`, `.img`) - `snippets` (allowed extensions: any) + - `import` (allowed extensions: `.raw`, `.qcow2`, `.vmdk`) - `vztmpl` (allowed extensions: `.tar.gz`, `.tar.xz`, `tar.zst`) - `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. diff --git a/fwprovider/nodes/resource_download_file.go b/fwprovider/nodes/resource_download_file.go index e0421efe..b433fda9 100644 --- a/fwprovider/nodes/resource_download_file.go +++ b/fwprovider/nodes/resource_download_file.go @@ -205,23 +205,22 @@ func (r *downloadFileResource) Schema( Attributes: map[string]schema.Attribute{ "id": attribute.ResourceID(), "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, Validators: []validator.String{stringvalidator.OneOf([]string{ "iso", "vztmpl", + "import", }...)}, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, "file_name": schema.StringAttribute{ - Description: "The file name. If not provided, it is calculated " + - "using `url`. PVE will raise 'wrong file extension' error for some popular " + - "extensions file `.raw` or `.qcow2`. Workaround is to use e.g. `.img` instead.", - Computed: true, - Required: false, - Optional: true, + Description: "The file name. If not provided, it is calculated using `url`.", + Computed: true, + Required: false, + Optional: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), stringplanmodifier.UseStateForUnknown(), diff --git a/proxmoxtf/resource/file.go b/proxmoxtf/resource/file.go index 3e62558b..01ed9a2b 100644 --- a/proxmoxtf/resource/file.go +++ b/proxmoxtf/resource/file.go @@ -20,6 +20,7 @@ import ( "path/filepath" "slices" "sort" + "strconv" "strings" "time" @@ -325,9 +326,6 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag var diags diag.Diagnostics - contentType, dg := fileGetContentType(d) - diags = append(diags, dg...) - fileName, err := fileGetSourceFileName(d) 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) } + contentType, dg := fileGetContentType(ctx, d, capi) + diags = append(diags, dg...) + list, err := capi.Node(nodeName).Storage(datastoreID).ListDatastoreFiles(ctx) if err != nil { return diag.FromErr(err) @@ -553,7 +554,7 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag } switch *contentType { - case "iso", "vztmpl": + case "iso", "vztmpl", "import": _, err = capi.Node(nodeName).Storage(datastoreID).APIUpload( 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...) if diags.HasError() { return diags @@ -617,11 +618,33 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag 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) sourceFile := d.Get(mkResourceVirtualEnvironmentFileSourceFile).([]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 := "" if len(sourceFile) > 0 { @@ -638,22 +661,26 @@ func fileGetContentType(d *schema.ResourceData) (*string, diag.Diagnostics) { mkResourceVirtualEnvironmentFileSourceRaw, ) } - if contentType == "" { if strings.HasSuffix(sourceFilePath, ".tar.gz") || strings.HasSuffix(sourceFilePath, ".tar.xz") { contentType = "vztmpl" + } else if release > 8.4 && (strings.HasSuffix(sourceFilePath, ".qcow2") || + strings.HasSuffix(sourceFilePath, ".raw") || + strings.HasSuffix(sourceFilePath, ".vmdk")) { + contentType = "import" } else { ext := strings.TrimLeft(strings.ToLower(filepath.Ext(sourceFilePath)), ".") switch ext { - case "img", "iso": + case "iso": contentType = "iso" case "yaml", "yml": contentType = "snippets" } } + // We cannot determine, for example, the content type of an .img file, so we require the user to specify it. if contentType == "" { return nil, diag.Errorf( "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 } -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) if err != nil { return fileVolumeID{}, diag.FromErr(err) } datastoreID := d.Get(mkResourceVirtualEnvironmentFileDatastoreID).(string) - contentType, diags := fileGetContentType(d) + contentType, diags := fileGetContentType(ctx, d, c) return fileVolumeID{ datastoreID: datastoreID, diff --git a/proxmoxtf/resource/validators/file.go b/proxmoxtf/resource/validators/file.go index 8cd2e57e..0c8e5908 100644 --- a/proxmoxtf/resource/validators/file.go +++ b/proxmoxtf/resource/validators/file.go @@ -24,6 +24,7 @@ func ContentType() schema.SchemaValidateDiagFunc { "iso", "snippets", "vztmpl", + "import", }, false)) }