diff --git a/README.md b/README.md index b3a72c80..3178256f 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ This data source doesn't accept arguments. * `comment` - The user comment * `email` - The user's email address * `enabled` - Whether the user account is enabled -* `expiration_date` - The user account's expiration date +* `expiration_date` - The user account's expiration date (RFC 3339) * `first_name` - The user's first name * `groups` - The user's groups * `keys` - The user's keys @@ -161,7 +161,7 @@ This data source doesn't accept arguments. * `comments` - The user comments * `emails` - The users' email addresses * `enabled` - Whether a user account is enabled -* `expiration_dates` - The user accounts' expiration dates +* `expiration_dates` - The user accounts' expiration dates (RFC 3339) * `first_names` - The users' first names * `groups` - The users' groups * `keys` - The users' keys @@ -188,12 +188,16 @@ This data source doesn't accept arguments. ###### Arguments * `datastore_id` - (Required) The datastore id * `node_name` - (Required) The node name -* `override_file_name` - (Optional) The file name to use in the datastore (leave undefined to use source file 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_changed` - (Optional) Whether the source has changed (leave undefined to use built-in detection) * `template` - (Required) Whether this is a container template (`vztmpl` instead of `iso`) ###### Attributes +* `file_modification_date` - The file modification date (RFC 3339) * `file_name` - The datastore file name +* `file_size` - The file size in bytes +* `file_tag` - The file tag ##### Group (proxmox_virtual_environment_group) @@ -241,7 +245,7 @@ This resource doesn't expose any additional attributes. * `comment` - (Optional) The user comment * `email` - (Optional) The user's email address * `enabled` - (Optional) Whether the user account is enabled -* `expiration_date` - (Optional) The user account's expiration date +* `expiration_date` - (Optional) The user account's expiration date (RFC 3339) * `first_name` - (Optional) The user's first name * `groups` - (Optional) The user's groups * `keys` - (Optional) The user's keys diff --git a/example/resource_virtual_environment_file.tf b/example/resource_virtual_environment_file.tf index cbfadcf1..fe31c17d 100644 --- a/example/resource_virtual_environment_file.tf +++ b/example/resource_virtual_environment_file.tf @@ -9,10 +9,22 @@ output "resource_proxmox_virtual_environment_file_alpine_template_datastore_id" value = "${proxmox_virtual_environment_file.alpine_template.datastore_id}" } +output "resource_proxmox_virtual_environment_file_alpine_template_file_modification_date" { + value = "${proxmox_virtual_environment_file.alpine_template.file_modification_date}" +} + output "resource_proxmox_virtual_environment_file_alpine_template_file_name" { value = "${proxmox_virtual_environment_file.alpine_template.file_name}" } +output "resource_proxmox_virtual_environment_file_alpine_template_file_size" { + value = "${proxmox_virtual_environment_file.alpine_template.file_size}" +} + +output "resource_proxmox_virtual_environment_file_alpine_template_file_tag" { + value = "${proxmox_virtual_environment_file.alpine_template.file_tag}" +} + output "resource_proxmox_virtual_environment_file_alpine_template_id" { value = "${proxmox_virtual_environment_file.alpine_template.id}" } diff --git a/go.sum b/go.sum index eee39904..27a7bd92 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= diff --git a/resource_virtual_environment_file.go b/resource_virtual_environment_file.go index c86ef9d3..ca0d3f57 100644 --- a/resource_virtual_environment_file.go +++ b/resource_virtual_environment_file.go @@ -15,18 +15,23 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/danitso/terraform-provider-proxmox/proxmox" "github.com/hashicorp/terraform/helper/schema" ) const ( - mkResourceVirtualEnvironmentFileDatastoreID = "datastore_id" - mkResourceVirtualEnvironmentFileFileName = "file_name" - mkResourceVirtualEnvironmentFileOverrideFileName = "override_file_name" - mkResourceVirtualEnvironmentFileNodeName = "node_name" - mkResourceVirtualEnvironmentFileSource = "source" - mkResourceVirtualEnvironmentFileTemplate = "template" + mkResourceVirtualEnvironmentFileDatastoreID = "datastore_id" + mkResourceVirtualEnvironmentFileFileModificationDate = "file_modification_date" + mkResourceVirtualEnvironmentFileFileName = "file_name" + mkResourceVirtualEnvironmentFileFileSize = "file_size" + mkResourceVirtualEnvironmentFileFileTag = "file_tag" + mkResourceVirtualEnvironmentFileOverrideFileName = "override_file_name" + mkResourceVirtualEnvironmentFileNodeName = "node_name" + mkResourceVirtualEnvironmentFileSource = "source" + mkResourceVirtualEnvironmentFileSourceChanged = "source_changed" + mkResourceVirtualEnvironmentFileTemplate = "template" ) func resourceVirtualEnvironmentFile() *schema.Resource { @@ -38,14 +43,32 @@ func resourceVirtualEnvironmentFile() *schema.Resource { Required: true, ForceNew: true, }, + mkResourceVirtualEnvironmentFileFileModificationDate: &schema.Schema{ + Type: schema.TypeString, + Description: "The file modification date", + Computed: true, + ForceNew: true, + }, mkResourceVirtualEnvironmentFileFileName: &schema.Schema{ Type: schema.TypeString, - Description: "The datastore file name", + Description: "The file name", Computed: true, }, + mkResourceVirtualEnvironmentFileFileSize: &schema.Schema{ + Type: schema.TypeInt, + Description: "The file size in bytes", + Computed: true, + ForceNew: true, + }, + mkResourceVirtualEnvironmentFileFileTag: &schema.Schema{ + Type: schema.TypeString, + Description: "The file tag", + Computed: true, + ForceNew: true, + }, mkResourceVirtualEnvironmentFileOverrideFileName: &schema.Schema{ Type: schema.TypeString, - Description: "The file name to use in the datastore (leave undefined to use source file name)", + Description: "The file name to use instead of the source file name", Optional: true, ForceNew: true, Default: "", @@ -62,6 +85,13 @@ func resourceVirtualEnvironmentFile() *schema.Resource { Required: true, ForceNew: true, }, + mkResourceVirtualEnvironmentFileSourceChanged: &schema.Schema{ + Type: schema.TypeBool, + Description: "Whether the source has changed since the last run", + Optional: true, + ForceNew: true, + Default: false, + }, mkResourceVirtualEnvironmentFileTemplate: &schema.Schema{ Type: schema.TypeBool, Description: "Whether this is a container template", @@ -96,8 +126,8 @@ func resourceVirtualEnvironmentFileCreate(d *schema.ResourceData, m interface{}) var sourceReader io.Reader - if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") { - log.Printf("[DEBUG] Downloading file '%s'", source) + if resourceVirtualEnvironmentFileIsURL(d, m) { + log.Printf("[DEBUG] Downloading file from '%s'", source) res, err := http.Get(source) @@ -184,7 +214,7 @@ func resourceVirtualEnvironmentFileGetFileName(d *schema.ResourceData, m interfa source := d.Get(mkResourceVirtualEnvironmentFileSource).(string) if fileName == "" { - if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") { + if resourceVirtualEnvironmentFileIsURL(d, m) { downloadURL, err := url.ParseRequestURI(source) if err != nil { @@ -225,6 +255,12 @@ func resourceVirtualEnvironmentFileGetVolumeID(d *schema.ResourceData, m interfa return &volumeID, nil } +func resourceVirtualEnvironmentFileIsURL(d *schema.ResourceData, m interface{}) bool { + source := d.Get(mkResourceVirtualEnvironmentFileSource).(string) + + return strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") +} + func resourceVirtualEnvironmentFileRead(d *schema.ResourceData, m interface{}) error { config := m.(providerConfiguration) veClient, err := config.GetVEClient() @@ -245,8 +281,82 @@ func resourceVirtualEnvironmentFileRead(d *schema.ResourceData, m interface{}) e 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 err != nil { + return err + } + + defer res.Body.Close() + + fileSize = res.ContentLength + httpLastModified := res.Header.Get("Last-Modified") + + if httpLastModified != "" { + timeParsed, err := time.Parse(time.RFC1123, httpLastModified) + + if err != nil { + timeParsed, err = time.Parse(time.RFC1123Z, httpLastModified) + + if err != nil { + return err + } + } + + fileModificationDate = timeParsed.UTC().Format(time.RFC3339) + } else { + d.Set(mkResourceVirtualEnvironmentFileFileModificationDate, "") + } + + httpTag := res.Header.Get("ETag") + + if httpTag != "" { + httpTagParts := strings.Split(httpTag, "\"") + + if len(httpTagParts) > 1 { + fileTag = httpTagParts[1] + } else { + fileTag = "" + } + } else { + fileTag = "" + } + } else { + f, err := os.Open(source) + + if err != nil { + return err + } + + defer f.Close() + + fileInfo, err := f.Stat() + + if err != nil { + return err + } + + fileModificationDate = fileInfo.ModTime().UTC().Format(time.RFC3339) + fileSize = fileInfo.Size() + fileTag = fmt.Sprintf("%x-%x", fileInfo.ModTime().UTC().Unix(), fileInfo.Size()) + } + + lastFileModificationDate := d.Get(mkResourceVirtualEnvironmentFileFileModificationDate).(string) + lastFileSize := int64(d.Get(mkResourceVirtualEnvironmentFileFileSize).(int)) + lastFileTag := d.Get(mkResourceVirtualEnvironmentFileFileTag).(string) + + d.Set(mkResourceVirtualEnvironmentFileFileModificationDate, fileModificationDate) d.Set(mkResourceVirtualEnvironmentFileFileName, *fileName) + d.Set(mkResourceVirtualEnvironmentFileFileSize, fileSize) + d.Set(mkResourceVirtualEnvironmentFileFileTag, fileTag) + d.Set(mkResourceVirtualEnvironmentFileSourceChanged, lastFileModificationDate != fileModificationDate || lastFileSize != fileSize || lastFileTag != fileTag) return nil } diff --git a/resource_virtual_environment_file_test.go b/resource_virtual_environment_file_test.go index 4ae8c90f..d9251193 100644 --- a/resource_virtual_environment_file_test.go +++ b/resource_virtual_environment_file_test.go @@ -32,16 +32,24 @@ func TestResourceVirtualEnvironmentFileSchema(t *testing.T) { testOptionalArguments(t, s, []string{ mkResourceVirtualEnvironmentFileOverrideFileName, + mkResourceVirtualEnvironmentFileSourceChanged, }) testComputedAttributes(t, s, []string{ + mkResourceVirtualEnvironmentFileFileModificationDate, mkResourceVirtualEnvironmentFileFileName, + mkResourceVirtualEnvironmentFileFileSize, + mkResourceVirtualEnvironmentFileFileTag, }) testSchemaValueTypes(t, s, []string{ mkResourceVirtualEnvironmentFileDatastoreID, + mkResourceVirtualEnvironmentFileFileModificationDate, mkResourceVirtualEnvironmentFileFileName, + mkResourceVirtualEnvironmentFileFileSize, + mkResourceVirtualEnvironmentFileFileTag, mkResourceVirtualEnvironmentFileOverrideFileName, + mkResourceVirtualEnvironmentFileSourceChanged, mkResourceVirtualEnvironmentFileNodeName, mkResourceVirtualEnvironmentFileSource, mkResourceVirtualEnvironmentFileTemplate, @@ -49,6 +57,10 @@ func TestResourceVirtualEnvironmentFileSchema(t *testing.T) { schema.TypeString, schema.TypeString, schema.TypeString, + schema.TypeInt, + schema.TypeString, + schema.TypeString, + schema.TypeBool, schema.TypeString, schema.TypeString, schema.TypeBool,