diff --git a/README.md b/README.md index 57d0aeb1..1762973c 100644 --- a/README.md +++ b/README.md @@ -240,13 +240,22 @@ This data source doesn't accept arguments. ##### File (proxmox_virtual_environment_file) ###### Arguments -* `content_type` - (Optional) The content type (`backup`, `images`, `iso` or `vztmpl`) +* `content_type` - (Optional) The content type + * `backup` + * `iso` + * `snippets` + * `vztmpl` * `datastore_id` - (Required) The datastore id * `node_name` - (Required) The node name -* `override_file_name` - (Optional) The file name to use instead of the source file name -* `source` - (Required) A path to a local file or a URL -* `source_checksum` - (Optional) The SHA256 checksum of the source file -* `source_insecure` - (Optional) Whether to skip the TLS verification step for HTTPS sources (defaults to `false`) +* `source_file` - (Optional) The source file (conflicts with `source_raw`) + * `checksum` - (Optional) The SHA256 checksum of the source file + * `file_name` - (Optional) The file name to use instead of the source file name + * `insecure` - (Optional) Whether to skip the TLS verification step for HTTPS sources (defaults to `false`) + * `path` - (Required) A path to a local file or a URL +* `source_raw` - (Optional) The raw source (conflicts with `source_file`) + * `data` - (Required) The raw data + * `file_name` - (Required) The file name + * `resize` - (Optional) The number of bytes to resize the file to ###### Attributes * `file_modification_date` - The file modification date (RFC 3339) @@ -334,10 +343,11 @@ This resource doesn't expose any additional attributes. * `ipv6` - (Optional) The IPv4 configuration * `address` - (Optional) The IPv6 address (use `dhcp` for autodiscovery) * `gateway` - (Optional) The IPv6 gateway (must be omitted when `dhcp` is used as the address) - * `user_account` - (Required) The user account configuration + * `user_account` - (Required) The user account configuration (conflicts with `user_data_file_id`) * `keys` - (Required) The SSH keys * `password` - (Optional) The SSH password * `username` - (Required) The SSH username + * `user_data_file_id` - (Optional) The ID of a file containing custom user data (conflicts with `user_account`) * `cpu` - (Optional) The CPU configuration * `cores` - (Optional) The number of CPU cores (defaults to `1`) * `hotplugged` - (Optional) The number of hotplugged vCPUs (defaults to `0`) diff --git a/example/resource_virtual_environment_file.tf b/example/resource_virtual_environment_file.tf index d078bc13..0671eac1 100644 --- a/example/resource_virtual_environment_file.tf +++ b/example/resource_virtual_environment_file.tf @@ -1,8 +1,40 @@ resource "proxmox_virtual_environment_file" "ubuntu_cloud_image" { - content_type = "iso" - datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}" - node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}" - source = "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img" + content_type = "iso" + datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}" + node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}" + + source_file { + path = "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img" + } +} + +resource "proxmox_virtual_environment_file" "cloud_init_config" { + content_type = "snippets" + datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}" + node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}" + + source_raw { + data = < 0 && len(sourceRaw) > 0 { + return fmt.Errorf( + "Please specify \"%s.%s\" or \"%s\" - not both", + mkResourceVirtualEnvironmentFileSourceFile, + mkResourceVirtualEnvironmentFileSourceFilePath, + mkResourceVirtualEnvironmentFileSourceRaw, + ) } - // Calculate the checksum of the source file now that it's available locally. - if sourceChecksum != "" { - file, err := os.Open(sourceFile) + // Determine if we're dealing with raw file data or a reference to a file or URL. + // In case of a URL, we must first download the file before proceeding. + // This is due to lack of support for chunked transfers in the Proxmox VE API. + if len(sourceFile) > 0 { + sourceFileBlock := sourceFile[0].(map[string]interface{}) + sourceFilePath := sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFilePath].(string) + sourceFileChecksum := sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFileChecksum].(string) + sourceFileInsecure := sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFileInsecure].(bool) - if err != nil { - return err + if resourceVirtualEnvironmentFileIsURL(d, m) { + log.Printf("[DEBUG] Downloading file from '%s'", sourceFilePath) + + httpClient := http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: sourceFileInsecure, + }, + }, + } + + res, err := httpClient.Get(sourceFilePath) + + if err != nil { + return err + } + + defer res.Body.Close() + + tempDownloadedFile, err := ioutil.TempFile("", "download") + + if err != nil { + return err + } + + tempDownloadedFileName := tempDownloadedFile.Name() + _, err = io.Copy(tempDownloadedFile, res.Body) + + if err != nil { + tempDownloadedFile.Close() + + return err + } + + tempDownloadedFile.Close() + + defer os.Remove(tempDownloadedFileName) + + sourceFilePathLocal = tempDownloadedFileName + } else { + sourceFilePathLocal = sourceFilePath } - h := sha256.New() - _, err = io.Copy(h, file) + // Calculate the checksum of the source file now that it's available locally. + if sourceFileChecksum != "" { + file, err := os.Open(sourceFilePathLocal) + + if err != nil { + return err + } + + h := sha256.New() + _, err = io.Copy(h, file) + + if err != nil { + file.Close() + + return err + } - if err != nil { file.Close() + calculatedChecksum := fmt.Sprintf("%x", h.Sum(nil)) + + log.Printf("[DEBUG] The calculated SHA256 checksum for source \"%s\" is \"%s\"", sourceFilePath, calculatedChecksum) + + if sourceFileChecksum != calculatedChecksum { + return fmt.Errorf("The calculated SHA256 checksum \"%s\" does not match source checksum \"%s\"", calculatedChecksum, sourceFileChecksum) + } + } + } else if len(sourceRaw) > 0 { + sourceRawBlock := sourceRaw[0].(map[string]interface{}) + sourceRawData := sourceRawBlock[mkResourceVirtualEnvironmentFileSourceRawData].(string) + sourceRawResize := sourceRawBlock[mkResourceVirtualEnvironmentFileSourceRawResize].(int) + + if sourceRawResize > 0 { + if len(sourceRawData) <= sourceRawResize { + sourceRawData = fmt.Sprintf(fmt.Sprintf("%%-%dv", sourceRawResize), sourceRawData) + } else { + return fmt.Errorf("Cannot resize %d bytes to %d bytes", len(sourceRawData), sourceRawResize) + } + } + + tempRawFile, err := ioutil.TempFile("", "raw") + + if err != nil { return err } - file.Close() + tempRawFileName := tempRawFile.Name() + _, err = io.Copy(tempRawFile, bytes.NewBufferString(sourceRawData)) - calculatedChecksum := fmt.Sprintf("%x", h.Sum(nil)) + if err != nil { + tempRawFile.Close() - log.Printf("[DEBUG] The calculated SHA256 checksum for source \"%s\" is \"%s\"", source, calculatedChecksum) - - if sourceChecksum != calculatedChecksum { - return fmt.Errorf("The calculated SHA256 checksum \"%s\" does not match source checksum \"%s\"", calculatedChecksum, sourceChecksum) + return err } + + tempRawFile.Close() + + defer os.Remove(tempRawFileName) + + sourceFilePathLocal = tempRawFileName + } else { + return fmt.Errorf( + "Please specify either \"%s.%s\" or \"%s\"", + mkResourceVirtualEnvironmentFileSourceFile, + mkResourceVirtualEnvironmentFileSourceFilePath, + mkResourceVirtualEnvironmentFileSourceRaw, + ) } // Open the source file for reading in order to upload it. - file, err := os.Open(sourceFile) + file, err := os.Open(sourceFilePathLocal) if err != nil { return err @@ -267,69 +381,101 @@ func resourceVirtualEnvironmentFileCreate(d *schema.ResourceData, m interface{}) func resourceVirtualEnvironmentFileGetContentType(d *schema.ResourceData, m interface{}) (*string, error) { contentType := d.Get(mkResourceVirtualEnvironmentFileContentType).(string) - source := d.Get(mkResourceVirtualEnvironmentFileSource).(string) - supportedTypes := []string{"backup", "images", "iso", "vztmpl"} + sourceFile := d.Get(mkResourceVirtualEnvironmentFileSourceFile).([]interface{}) + sourceRaw := d.Get(mkResourceVirtualEnvironmentFileSourceRaw).([]interface{}) + + sourceFilePath := "" + + if len(sourceFile) > 0 { + sourceFileBlock := sourceFile[0].(map[string]interface{}) + sourceFilePath = sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFilePath].(string) + } else if len(sourceRaw) > 0 { + sourceRawBlock := sourceRaw[0].(map[string]interface{}) + sourceFilePath = sourceRawBlock[mkResourceVirtualEnvironmentFileSourceRawFileName].(string) + } else { + return nil, fmt.Errorf( + "Missing argument \"%s.%s\" or \"%s\"", + mkResourceVirtualEnvironmentFileSourceFile, + mkResourceVirtualEnvironmentFileSourceFilePath, + mkResourceVirtualEnvironmentFileSourceRaw, + ) + } if contentType == "" { - if strings.HasSuffix(source, ".tar.xz") { + if strings.HasSuffix(sourceFilePath, ".tar.xz") { contentType = "vztmpl" } else { - ext := strings.TrimLeft(strings.ToLower(filepath.Ext(source)), ".") + ext := strings.TrimLeft(strings.ToLower(filepath.Ext(sourceFilePath)), ".") switch ext { case "img", "iso": contentType = "iso" + case "yaml", "yml": + contentType = "snippets" } } if contentType == "" { return nil, fmt.Errorf( - "Cannot determine the content type of source \"%s\" - Please manually define the \"%s\" argument (supported: %s)", - source, + "Cannot determine the content type of source \"%s\" - Please manually define the \"%s\" argument", + sourceFilePath, mkResourceVirtualEnvironmentFileContentType, - strings.Join(supportedTypes, " or "), ) } } - for _, v := range supportedTypes { - if v == contentType { - return &contentType, nil - } + ctValidator := getContentTypeValidator() + _, errs := ctValidator(contentType, mkResourceVirtualEnvironmentFileContentType) + + if len(errs) > 0 { + return nil, errs[0] } - return nil, fmt.Errorf( - "Unsupported content type \"%s\" for source \"%s\" (supported: %s)", - contentType, - source, - strings.Join(supportedTypes, " or "), - ) + return &contentType, nil } func resourceVirtualEnvironmentFileGetFileName(d *schema.ResourceData, m interface{}) (*string, error) { - fileName := d.Get(mkResourceVirtualEnvironmentFileOverrideFileName).(string) - source := d.Get(mkResourceVirtualEnvironmentFileSource).(string) + sourceFile := d.Get(mkResourceVirtualEnvironmentFileSourceFile).([]interface{}) + sourceRaw := d.Get(mkResourceVirtualEnvironmentFileSourceRaw).([]interface{}) - if fileName == "" { + sourceFileFileName := "" + sourceFilePath := "" + + if len(sourceFile) > 0 { + sourceFileBlock := sourceFile[0].(map[string]interface{}) + sourceFileFileName = sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFileFileName].(string) + sourceFilePath = sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFilePath].(string) + } else if len(sourceRaw) > 0 { + sourceRawBlock := sourceRaw[0].(map[string]interface{}) + sourceFileFileName = sourceRawBlock[mkResourceVirtualEnvironmentFileSourceRawFileName].(string) + } else { + return nil, fmt.Errorf( + "Missing argument \"%s.%s\"", + mkResourceVirtualEnvironmentFileSourceFile, + mkResourceVirtualEnvironmentFileSourceFilePath, + ) + } + + if sourceFileFileName == "" { if resourceVirtualEnvironmentFileIsURL(d, m) { - downloadURL, err := url.ParseRequestURI(source) + downloadURL, err := url.ParseRequestURI(sourceFilePath) if err != nil { return nil, err } path := strings.Split(downloadURL.Path, "/") - fileName = path[len(path)-1] + sourceFileFileName = path[len(path)-1] - if fileName == "" { - return nil, errors.New("Failed to determine file name from source URL") + if sourceFileFileName == "" { + return nil, fmt.Errorf("Failed to determine file name from the URL \"%s\"", sourceFilePath) } } else { - fileName = filepath.Base(source) + sourceFileFileName = filepath.Base(sourceFilePath) } } - return &fileName, nil + return &sourceFileFileName, nil } func resourceVirtualEnvironmentFileGetVolumeID(d *schema.ResourceData, m interface{}) (*string, error) { @@ -352,9 +498,17 @@ func resourceVirtualEnvironmentFileGetVolumeID(d *schema.ResourceData, m interfa } func resourceVirtualEnvironmentFileIsURL(d *schema.ResourceData, m interface{}) bool { - source := d.Get(mkResourceVirtualEnvironmentFileSource).(string) + sourceFile := d.Get(mkResourceVirtualEnvironmentFileSourceFile).([]interface{}) + sourceFilePath := "" - return strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") + if len(sourceFile) > 0 { + sourceFileBlock := sourceFile[0].(map[string]interface{}) + sourceFilePath = sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFilePath].(string) + } else { + return false + } + + return strings.HasPrefix(sourceFilePath, "http://") || strings.HasPrefix(sourceFilePath, "https://") } func resourceVirtualEnvironmentFileRead(d *schema.ResourceData, m interface{}) error { @@ -367,6 +521,15 @@ func resourceVirtualEnvironmentFileRead(d *schema.ResourceData, m interface{}) e datastoreID := d.Get(mkResourceVirtualEnvironmentFileDatastoreID).(string) nodeName := d.Get(mkResourceVirtualEnvironmentFileNodeName).(string) + sourceFile := d.Get(mkResourceVirtualEnvironmentFileSourceFile).([]interface{}) + sourceFilePath := "" + + if len(sourceFile) == 0 { + return nil + } + + sourceFileBlock := sourceFile[0].(map[string]interface{}) + sourceFilePath = sourceFileBlock[mkResourceVirtualEnvironmentFileSourceFilePath].(string) list, err := veClient.ListDatastoreFiles(nodeName, datastoreID) @@ -374,17 +537,21 @@ func resourceVirtualEnvironmentFileRead(d *schema.ResourceData, m interface{}) e return err } + fileIsURL := resourceVirtualEnvironmentFileIsURL(d, m) + fileName, err := resourceVirtualEnvironmentFileGetFileName(d, m) + + if err != nil { + return err + } + for _, v := range list { if v.VolumeID == d.Id() { - fileName, _ := resourceVirtualEnvironmentFileGetFileName(d, m) - source := d.Get(mkResourceVirtualEnvironmentFileSource).(string) - var fileModificationDate string var fileSize int64 var fileTag string - if resourceVirtualEnvironmentFileIsURL(d, m) { - res, err := http.Head(source) + if fileIsURL { + res, err := http.Head(sourceFilePath) if err != nil { return err @@ -425,7 +592,7 @@ func resourceVirtualEnvironmentFileRead(d *schema.ResourceData, m interface{}) e fileTag = "" } } else { - f, err := os.Open(source) + f, err := os.Open(sourceFilePath) if err != nil { return err @@ -452,7 +619,7 @@ func resourceVirtualEnvironmentFileRead(d *schema.ResourceData, m interface{}) e d.Set(mkResourceVirtualEnvironmentFileFileName, *fileName) d.Set(mkResourceVirtualEnvironmentFileFileSize, fileSize) d.Set(mkResourceVirtualEnvironmentFileFileTag, fileTag) - d.Set(mkResourceVirtualEnvironmentFileSourceChanged, lastFileModificationDate != fileModificationDate || lastFileSize != fileSize || lastFileTag != fileTag) + d.Set(mkResourceVirtualEnvironmentFileSourceFileChanged, lastFileModificationDate != fileModificationDate || lastFileSize != fileSize || lastFileTag != fileTag) return nil } diff --git a/proxmoxtf/resource_virtual_environment_file_test.go b/proxmoxtf/resource_virtual_environment_file_test.go index 415f0836..b4b3e07d 100644 --- a/proxmoxtf/resource_virtual_environment_file_test.go +++ b/proxmoxtf/resource_virtual_environment_file_test.go @@ -26,15 +26,12 @@ func TestResourceVirtualEnvironmentFileSchema(t *testing.T) { testRequiredArguments(t, s, []string{ mkResourceVirtualEnvironmentFileDatastoreID, mkResourceVirtualEnvironmentFileNodeName, - mkResourceVirtualEnvironmentFileSource, }) testOptionalArguments(t, s, []string{ mkResourceVirtualEnvironmentFileContentType, - mkResourceVirtualEnvironmentFileOverrideFileName, - mkResourceVirtualEnvironmentFileSourceChanged, - mkResourceVirtualEnvironmentFileSourceChecksum, - mkResourceVirtualEnvironmentFileSourceInsecure, + mkResourceVirtualEnvironmentFileSourceFile, + mkResourceVirtualEnvironmentFileSourceRaw, }) testComputedAttributes(t, s, []string{ @@ -51,12 +48,9 @@ func TestResourceVirtualEnvironmentFileSchema(t *testing.T) { mkResourceVirtualEnvironmentFileFileName, mkResourceVirtualEnvironmentFileFileSize, mkResourceVirtualEnvironmentFileFileTag, - mkResourceVirtualEnvironmentFileOverrideFileName, - mkResourceVirtualEnvironmentFileSourceChanged, mkResourceVirtualEnvironmentFileNodeName, - mkResourceVirtualEnvironmentFileSource, - mkResourceVirtualEnvironmentFileSourceChecksum, - mkResourceVirtualEnvironmentFileSourceInsecure, + mkResourceVirtualEnvironmentFileSourceFile, + mkResourceVirtualEnvironmentFileSourceRaw, }, []schema.ValueType{ schema.TypeString, schema.TypeString, @@ -65,10 +59,55 @@ func TestResourceVirtualEnvironmentFileSchema(t *testing.T) { schema.TypeInt, schema.TypeString, schema.TypeString, + schema.TypeList, + schema.TypeList, + }) + + sourceFileSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentFileSourceFile) + + testRequiredArguments(t, sourceFileSchema, []string{ + mkResourceVirtualEnvironmentFileSourceFilePath, + }) + + testOptionalArguments(t, sourceFileSchema, []string{ + mkResourceVirtualEnvironmentFileSourceFileChanged, + mkResourceVirtualEnvironmentFileSourceFileChecksum, + mkResourceVirtualEnvironmentFileSourceFileFileName, + mkResourceVirtualEnvironmentFileSourceFileInsecure, + }) + + testSchemaValueTypes(t, sourceFileSchema, []string{ + mkResourceVirtualEnvironmentFileSourceFileChanged, + mkResourceVirtualEnvironmentFileSourceFileChecksum, + mkResourceVirtualEnvironmentFileSourceFileFileName, + mkResourceVirtualEnvironmentFileSourceFileInsecure, + mkResourceVirtualEnvironmentFileSourceFilePath, + }, []schema.ValueType{ schema.TypeBool, schema.TypeString, schema.TypeString, - schema.TypeString, schema.TypeBool, + schema.TypeString, + }) + + sourceRawSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentFileSourceRaw) + + testRequiredArguments(t, sourceRawSchema, []string{ + mkResourceVirtualEnvironmentFileSourceRawData, + mkResourceVirtualEnvironmentFileSourceRawFileName, + }) + + testOptionalArguments(t, sourceRawSchema, []string{ + mkResourceVirtualEnvironmentFileSourceRawResize, + }) + + testSchemaValueTypes(t, sourceRawSchema, []string{ + mkResourceVirtualEnvironmentFileSourceRawData, + mkResourceVirtualEnvironmentFileSourceRawFileName, + mkResourceVirtualEnvironmentFileSourceRawResize, + }, []schema.ValueType{ + schema.TypeString, + schema.TypeString, + schema.TypeInt, }) } diff --git a/proxmoxtf/resource_virtual_environment_vm.go b/proxmoxtf/resource_virtual_environment_vm.go index f26c1c0b..1cd2081d 100644 --- a/proxmoxtf/resource_virtual_environment_vm.go +++ b/proxmoxtf/resource_virtual_environment_vm.go @@ -24,6 +24,7 @@ const ( dvResourceVirtualEnvironmentVMCloudInitDNSDomain = "" dvResourceVirtualEnvironmentVMCloudInitDNSServer = "" dvResourceVirtualEnvironmentVMCloudInitUserAccountPassword = "" + dvResourceVirtualEnvironmentVMCloudInitUserDataFileID = "" dvResourceVirtualEnvironmentVMCPUCores = 1 dvResourceVirtualEnvironmentVMCPUHotplugged = 0 dvResourceVirtualEnvironmentVMCPUSockets = 1 @@ -72,6 +73,7 @@ const ( mkResourceVirtualEnvironmentVMCloudInitUserAccountKeys = "keys" mkResourceVirtualEnvironmentVMCloudInitUserAccountPassword = "password" mkResourceVirtualEnvironmentVMCloudInitUserAccountUsername = "username" + mkResourceVirtualEnvironmentVMCloudInitUserDataFileID = "user_data_file_id" mkResourceVirtualEnvironmentVMCPU = "cpu" mkResourceVirtualEnvironmentVMCPUCores = "cores" mkResourceVirtualEnvironmentVMCPUHotplugged = "hotplugged" @@ -318,6 +320,14 @@ func resourceVirtualEnvironmentVM() *schema.Resource { MaxItems: 1, MinItems: 0, }, + mkResourceVirtualEnvironmentVMCloudInitUserDataFileID: { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "The ID of a file containing custom user data", + Default: dvResourceVirtualEnvironmentVMCloudInitUserDataFileID, + ValidateFunc: getFileIDValidator(), + }, }, }, MaxItems: 1, @@ -770,6 +780,14 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e cloudInitConfig.Username = &username } + + cloudInitUserDataFileID := cloudInitBlock[mkResourceVirtualEnvironmentVMCloudInitUserDataFileID].(string) + + if cloudInitUserDataFileID != "" { + cloudInitConfig.Files = &proxmox.CustomCloudInitFiles{ + UserVolume: &cloudInitUserDataFileID, + } + } } cpu := d.Get(mkResourceVirtualEnvironmentVMCPU).([]interface{}) @@ -1075,7 +1093,9 @@ func resourceVirtualEnvironmentVMCreateImportedDisks(d *schema.ResourceData, m i speedBlock := speed[0].(map[string]interface{}) speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int) + speedLimitReadBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable].(int) speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int) + speedLimitWriteBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable].(int) diskOptions := "" @@ -1083,21 +1103,37 @@ func resourceVirtualEnvironmentVMCreateImportedDisks(d *schema.ResourceData, m i diskOptions += fmt.Sprintf(",mbps_rd=%d", speedLimitRead) } + if speedLimitReadBurstable > 0 { + diskOptions += fmt.Sprintf(",mbps_rd_max=%d", speedLimitReadBurstable) + } + if speedLimitWrite > 0 { diskOptions += fmt.Sprintf(",mbps_wr=%d", speedLimitWrite) } + if speedLimitWriteBurstable > 0 { + diskOptions += fmt.Sprintf(",mbps_wr_max=%d", speedLimitWriteBurstable) + } + fileIDParts := strings.Split(fileID, ":") - filePath := fmt.Sprintf("/var/lib/vz/template/%s", fileIDParts[1]) + filePath := "" + + if strings.HasPrefix(fileIDParts[1], "iso/") { + filePath = fmt.Sprintf("/template/%s", fileIDParts[1]) + } else { + filePath = fmt.Sprintf("/%s", fileIDParts[1]) + } + filePathTmp := fmt.Sprintf("/tmp/vm-%d-disk-%d.%s", vmID, diskCount+importedDiskCount, fileFormat) commands = append( commands, - fmt.Sprintf("cp %s %s", filePath, filePathTmp), - fmt.Sprintf("qemu-img resize %s %dG", filePathTmp, size), - fmt.Sprintf("qm importdisk %d %s %s -format qcow2", vmID, filePathTmp, datastoreID), - fmt.Sprintf("qm set %d -scsi%d %s:vm-%d-disk-%d%s", vmID, i, datastoreID, vmID, diskCount+importedDiskCount, diskOptions), - fmt.Sprintf("rm -f %s", filePathTmp), + `set -e`, + fmt.Sprintf(`cp "$(grep -Pzo ': %s\s+path\s+[^\s]+' /etc/pve/storage.cfg | grep -Pzo '/[^\s]*' | tr -d '\000')%s" %s`, fileIDParts[0], filePath, filePathTmp), + fmt.Sprintf(`qemu-img resize %s %dG`, filePathTmp, size), + fmt.Sprintf(`qm importdisk %d %s %s -format qcow2`, vmID, filePathTmp, datastoreID), + fmt.Sprintf(`qm set %d -scsi%d %s:vm-%d-disk-%d%s`, vmID, i, datastoreID, vmID, diskCount+importedDiskCount, diskOptions), + fmt.Sprintf(`rm -f %s`, filePathTmp), ) importedDiskCount++ diff --git a/proxmoxtf/utils.go b/proxmoxtf/utils.go index b9f363a2..c48a70aa 100644 --- a/proxmoxtf/utils.go +++ b/proxmoxtf/utils.go @@ -13,8 +13,21 @@ import ( "github.com/hashicorp/terraform/helper/validation" ) +func getContentTypeValidator() schema.SchemaValidateFunc { + return validation.StringInSlice([]string{ + "backup", + "iso", + "snippets", + "vztmpl", + }, false) +} + func getFileFormatValidator() schema.SchemaValidateFunc { - return validation.StringInSlice([]string{"qcow2", "raw", "vmdk"}, false) + return validation.StringInSlice([]string{ + "qcow2", + "raw", + "vmdk", + }, false) } func getFileIDValidator() schema.SchemaValidateFunc {