From 1bab78b7bf35d70771bce3997388b549cd9834ac Mon Sep 17 00:00:00 2001 From: Giuseppe Maxia Date: Wed, 18 Oct 2023 17:54:38 +0200 Subject: [PATCH 1/2] Media item upload any file and download (#1124) * Add property upload_any_file to vcd_catalog_media Setting this property to true we can upload any file. The default, without using this property, only allows .ISO files * Add property download_to_file to vcd_catalog_media data source This property allows to download the contents of a media item into a specific file * Add changelog items * Update docs * Update TestAccVcdCatalogAndMediaDatasource * Add changelog items --------- Signed-off-by: Giuseppe Maxia --- .changes/v3.11.0/1123-improvements.md | 1 + .changes/v3.11.0/1124-improvements.md | 1 + go.mod | 2 +- go.sum | 4 +- vcd/datasource_vcd_catalog_media.go | 5 + vcd/datasource_vcd_catalog_media_test.go | 78 +++++++++++++++- vcd/datasource_vcd_resource_list_test.go | 4 +- vcd/resource_vcd_catalog_media.go | 31 ++++++- vcd/resource_vcd_catalog_media_test.go | 103 ++++++++++++++------- website/docs/d/catalog_media.html.markdown | 6 ++ website/docs/r/catalog_media.html.markdown | 1 + 11 files changed, 193 insertions(+), 43 deletions(-) create mode 100644 .changes/v3.11.0/1123-improvements.md create mode 100644 .changes/v3.11.0/1124-improvements.md diff --git a/.changes/v3.11.0/1123-improvements.md b/.changes/v3.11.0/1123-improvements.md new file mode 100644 index 000000000..24f45afe8 --- /dev/null +++ b/.changes/v3.11.0/1123-improvements.md @@ -0,0 +1 @@ +* Add property `upload_any_file` to resource `vcd_catalog_media` to allow uploading any file as catalog media item [GH-1123] diff --git a/.changes/v3.11.0/1124-improvements.md b/.changes/v3.11.0/1124-improvements.md new file mode 100644 index 000000000..9275e81ab --- /dev/null +++ b/.changes/v3.11.0/1124-improvements.md @@ -0,0 +1 @@ +* Add property `download_to_file` to data source `vcd_catalog_media` to allow downloading a catalog media item into a file [GH-1124] diff --git a/go.mod b/go.mod index 9b3ec123d..d5149c824 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0 github.com/kr/pretty v0.2.1 - github.com/vmware/go-vcloud-director/v2 v2.22.0-alpha.6 + github.com/vmware/go-vcloud-director/v2 v2.22.0-alpha.7 ) require ( diff --git a/go.sum b/go.sum index a3953da26..66492b20b 100644 --- a/go.sum +++ b/go.sum @@ -123,8 +123,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/vmware/go-vcloud-director/v2 v2.22.0-alpha.6 h1:jvFqmN8tO39t7aFAvF+q5VL3+oZhfU9F0KeRlm9rxN0= -github.com/vmware/go-vcloud-director/v2 v2.22.0-alpha.6/go.mod h1:QPxGFgrUcSyzy9IlpwDE4UNT3tsOy2047tJOPEJ4nlw= +github.com/vmware/go-vcloud-director/v2 v2.22.0-alpha.7 h1:dEB8QK4Y8fPV3MXb0RiAOEGrTak0Gp2MuWINKY+GwLk= +github.com/vmware/go-vcloud-director/v2 v2.22.0-alpha.7/go.mod h1:QPxGFgrUcSyzy9IlpwDE4UNT3tsOy2047tJOPEJ4nlw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0= github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= diff --git a/vcd/datasource_vcd_catalog_media.go b/vcd/datasource_vcd_catalog_media.go index aec8d0199..48024cd6f 100644 --- a/vcd/datasource_vcd_catalog_media.go +++ b/vcd/datasource_vcd_catalog_media.go @@ -83,6 +83,11 @@ func datasourceVcdCatalogMedia() *schema.Resource { Computed: true, Description: "Storage profile name", }, + "download_to_file": { + Type: schema.TypeString, + Optional: true, + Description: "Will download the contents of the media item into the given file", + }, "filter": { Type: schema.TypeList, MaxItems: 1, diff --git a/vcd/datasource_vcd_catalog_media_test.go b/vcd/datasource_vcd_catalog_media_test.go index a08a317ab..a52021717 100644 --- a/vcd/datasource_vcd_catalog_media_test.go +++ b/vcd/datasource_vcd_catalog_media_test.go @@ -4,7 +4,9 @@ package vcd import ( "fmt" + "os" "regexp" + "runtime" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -21,6 +23,11 @@ func TestAccVcdCatalogAndMediaDatasource(t *testing.T) { var TestAccVcdDataSourceMedia = "TestAccVcdCatalogMediaBasic" var TestAccVcdDataSourceMediaDescription = "TestAccVcdCatalogMediaBasicDescription" + _, sourceFile, _, _ := runtime.Caller(0) + if !fileExists(sourceFile) { + t.Skip("source file for this test was not found") + } + tempFile := "source_file.txt" var params = StringMap{ "Org": testConfig.VCD.Org, "Catalog": testConfig.VCD.Catalog.Name, @@ -30,8 +37,12 @@ func TestAccVcdCatalogAndMediaDatasource(t *testing.T) { "UploadProgress": testConfig.Ova.UploadProgress, "Tags": "catalog", "CatalogMediaName": TestAccVcdDataSourceMedia, + "MediaFileName": "TestMediaFile", + "MediaFilePath": sourceFile, + "DownloadToFile": tempFile, "Description": TestAccVcdDataSourceMediaDescription, "MediaPath": testConfig.Media.MediaPath, + "FuncName": t.Name(), } testParamsNotEmpty(t, params) @@ -43,6 +54,14 @@ func TestAccVcdCatalogAndMediaDatasource(t *testing.T) { debugPrintf("#[DEBUG] CONFIGURATION: %s", configText) + defer func() { + if fileExists(tempFile) { + err := os.Remove(tempFile) + if err != nil { + fmt.Printf("error deleting file '%s': %s", tempFile, err) + } + } + }() resource.Test(t, resource.TestCase{ PreCheck: func() { preRunChecks(t) }, ProviderFactories: testAccProviders, @@ -56,14 +75,37 @@ func TestAccVcdCatalogAndMediaDatasource(t *testing.T) { resource.TestMatchOutput("creation_date", regexp.MustCompile(`^\d{4}-\d{2}-\d{2}.*`)), resource.TestCheckOutput("status", "RESOLVED"), resource.TestMatchOutput("storage_profile_name", regexp.MustCompile(`^\S+`)), + resource.TestCheckResourceAttr("vcd_catalog_media.media_file", "name", "TestMediaFile"), testCheckMediaNonStringOutputs(), ), }, + { + Config: configText, + Check: checkFileContentsAreEqual(sourceFile, tempFile), + }, }, }) postTestChecks(t) } +func checkFileContentsAreEqual(fileName1, fileName2 string) resource.TestCheckFunc { + + return func(s *terraform.State) error { + contents1, err := os.ReadFile(fileName1) // #nosec G304 -- file name under control used for testing + if err != nil { + return err + } + contents2, err := os.ReadFile(fileName2) // #nosec G304 -- file name under control used for testing + if err != nil { + return err + } + if string(contents1) == string(contents2) { + return nil + } + return fmt.Errorf("file %s and %s have different content", fileName1, fileName2) + } +} + func catalogMediaDestroyed(catalog, mediaName string) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*VCDClient) @@ -84,15 +126,21 @@ func catalogMediaDestroyed(catalog, mediaName string) resource.TestCheckFunc { } const testAccCheckVcdCatalogMediaDS = ` + +data "vcd_catalog" "mycat" { + org = "{{.Org}}" + name = "{{.Catalog}}" +} + resource "vcd_catalog_media" "{{.CatalogMediaName}}" { - org = "{{.Org}}" - catalog = "{{.Catalog}}" + org = "{{.Org}}" + catalog_id = data.vcd_catalog.mycat.id name = "{{.CatalogMediaName}}" description = "{{.Description}}" media_path = "{{.MediaPath}}" upload_piece_size = {{.UploadPieceSize}} - show_upload_progress = "{{.UploadProgress}}" + show_upload_progress = {{.UploadProgress}} metadata = { catalogMedia_metadata = "catalogMedia Metadata" @@ -100,13 +148,35 @@ resource "vcd_catalog_media" "{{.CatalogMediaName}}" { } } +# this resource uploads the source file for the current test as a media item +resource "vcd_catalog_media" "media_file" { + org = "{{.Org}}" + catalog_id = data.vcd_catalog.mycat.id + + name = "{{.MediaFileName}}" + description = "{{.Description}}" + media_path = "{{.MediaFilePath}}" + upload_piece_size = {{.UploadPieceSize}} + show_upload_progress = {{.UploadProgress}} + upload_any_file = true +} + data "vcd_catalog_media" "{{.NewCatalogMedia}}" { org = "{{.Org}}" - catalog = "{{.Catalog}}" + catalog_id = data.vcd_catalog.mycat.id name = vcd_catalog_media.{{.CatalogMediaName}}.name depends_on = [vcd_catalog_media.{{.CatalogMediaName}}] } +# This data source downloads the contents of the media item to a local file +data "vcd_catalog_media" "media_file_ds" { + org = "{{.Org}}" + catalog_id = data.vcd_catalog.mycat.id + name = vcd_catalog_media.media_file.name + + download_to_file = "{{.DownloadToFile}}" +} + output "size" { value = data.vcd_catalog_media.{{.NewCatalogMedia}}.size } diff --git a/vcd/datasource_vcd_resource_list_test.go b/vcd/datasource_vcd_resource_list_test.go index f2898b8e2..c5e777535 100644 --- a/vcd/datasource_vcd_resource_list_test.go +++ b/vcd/datasource_vcd_resource_list_test.go @@ -77,12 +77,12 @@ func TestAccVcdDatasourceResourceList(t *testing.T) { } else { fmt.Print("`Networking.ExternalNetwork` value isn't configured, datasource test using this will be skipped\n") } - if testConfig.VCD.ProviderVdc.Name != "" { + if testConfig.VCD.ProviderVdc.Name != "" && usingSysAdmin() { lists = append(lists, listDef{name: "provider-vdc", resourceType: "vcd_provider_vdc", knownItem: testConfig.VCD.ProviderVdc.Name}) } else { fmt.Print("`VCD.ProviderVdc` value isn't configured, datasource test using this will be skipped\n") } - if testConfig.VCD.NsxtProviderVdc.Name != "" { + if testConfig.VCD.NsxtProviderVdc.Name != "" && usingSysAdmin() { lists = append(lists, listDef{name: "nsxt-provider-vdc", resourceType: "vcd_provider_vdc", knownItem: testConfig.VCD.NsxtProviderVdc.Name}) } else { fmt.Print("`VCD.NsxtProviderVdc` value isn't configured, datasource test using this will be skipped\n") diff --git a/vcd/resource_vcd_catalog_media.go b/vcd/resource_vcd_catalog_media.go index 23ee1468a..615578087 100644 --- a/vcd/resource_vcd_catalog_media.go +++ b/vcd/resource_vcd_catalog_media.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "log" + "os" + "path" "strings" "time" @@ -66,6 +68,12 @@ func resourceVcdCatalogMedia() *schema.Resource { ForceNew: true, Description: "absolute or relative path to Media file", }, + "upload_any_file": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "If true, will allow uploading any file type, not only .ISO", + }, "upload_piece_size": { Type: schema.TypeInt, Optional: true, @@ -157,7 +165,14 @@ func resourceVcdMediaCreate(ctx context.Context, d *schema.ResourceData, meta in uploadPieceSize := d.Get("upload_piece_size").(int) mediaName := d.Get("name").(string) - task, err := catalog.UploadMediaImage(mediaName, d.Get("description").(string), mediaPath, int64(uploadPieceSize)*1024*1024) // Convert from megabytes to bytes) + var task govcd.UploadTask + uploadAnyFile := d.Get("upload_any_file").(bool) + + if uploadAnyFile { + task, err = catalog.UploadMediaFile(mediaName, d.Get("description").(string), mediaPath, int64(uploadPieceSize)*1024*1024, false) // Convert from megabytes to bytes) + } else { + task, err = catalog.UploadMediaImage(mediaName, d.Get("description").(string), mediaPath, int64(uploadPieceSize)*1024*1024) // Convert from megabytes to bytes) + } if err != nil { log.Printf("Error uploading new catalog media: %s", err) return diag.Errorf("error uploading new catalog media: %s", err) @@ -290,6 +305,20 @@ func genericVcdMediaRead(d *schema.ResourceData, meta interface{}, origin string dSet(d, "status", mediaRecord.MediaRecord.Status) dSet(d, "storage_profile_name", mediaRecord.MediaRecord.StorageProfileName) + if origin == "datasource" { + downloadToFile := d.Get("download_to_file").(string) + if downloadToFile != "" { + contents, err := media.Download() + if err != nil { + return diag.Errorf("error downloading media contents") + } + downloadToFile = path.Clean(downloadToFile) + err = os.WriteFile(downloadToFile, contents, 0600) + if err != nil { + return diag.Errorf("error writing media contents to file '%s'", downloadToFile) + } + } + } diagErr := updateMetadataInState(d, vcdClient, "vcd_catalog_media", media) if diagErr != nil { log.Printf("[DEBUG] Unable to update media item metadata: %s", err) diff --git a/vcd/resource_vcd_catalog_media_test.go b/vcd/resource_vcd_catalog_media_test.go index 9f92f2f4d..dcca0260c 100644 --- a/vcd/resource_vcd_catalog_media_test.go +++ b/vcd/resource_vcd_catalog_media_test.go @@ -11,12 +11,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -var TestAccVcdCatalogMedia = "TestAccVcdCatalogMediaBasic" -var TestAccVcdCatalogMediaDescription = "TestAccVcdCatalogMediaBasicDescription" - func TestAccVcdCatalogMediaBasic(t *testing.T) { preTestChecks(t) + var TestAccVcdCatalogMedia = "TestAccVcdCatalogMediaBasic" + var TestAccVcdCatalogMediaDescription = "TestAccVcdCatalogMediaBasicDescription" var params = StringMap{ "Org": testConfig.VCD.Org, "Catalog": testConfig.VCD.Catalog.Name, @@ -40,16 +39,25 @@ func TestAccVcdCatalogMediaBasic(t *testing.T) { resource.Test(t, resource.TestCase{ ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckCatalogMediaDestroy, + CheckDestroy: resource.ComposeTestCheckFunc( + testAccCheckCatalogMediaDestroy(TestAccVcdCatalogMedia), + testAccCheckCatalogMediaDestroy(TestAccVcdCatalogMedia+"2"), + testAccCheckCatalogMediaDestroy(TestAccVcdCatalogMedia+"2-update"), + ), Steps: []resource.TestStep{ { Config: configText, Check: resource.ComposeTestCheckFunc( testAccCheckVcdCatalogMediaExists("vcd_catalog_media."+TestAccVcdCatalogMedia), + testAccCheckVcdCatalogMediaExists("vcd_catalog_media.text_file"), resource.TestCheckResourceAttr( "vcd_catalog_media."+TestAccVcdCatalogMedia, "name", TestAccVcdCatalogMedia), + resource.TestCheckResourceAttr( + "vcd_catalog_media.text_file", "name", TestAccVcdCatalogMedia+"-2"), resource.TestCheckResourceAttr( "vcd_catalog_media."+TestAccVcdCatalogMedia, "description", TestAccVcdCatalogMediaDescription), + resource.TestCheckResourceAttr( + "vcd_catalog_media.text_file", "description", TestAccVcdCatalogMediaDescription+" 2"), resource.TestCheckResourceAttr( "vcd_catalog_media."+TestAccVcdCatalogMedia, "metadata.mediaItem_metadata", "mediaItem Metadata"), resource.TestCheckResourceAttr( @@ -67,6 +75,8 @@ func TestAccVcdCatalogMediaBasic(t *testing.T) { testAccCheckVcdCatalogMediaExists("vcd_catalog_media."+TestAccVcdCatalogMedia), resource.TestCheckResourceAttr( "vcd_catalog_media."+TestAccVcdCatalogMedia, "name", TestAccVcdCatalogMedia), + resource.TestCheckResourceAttr( + "vcd_catalog_media.text_file", "name", TestAccVcdCatalogMedia+"-2-update"), resource.TestCheckResourceAttr( "vcd_catalog_media."+TestAccVcdCatalogMedia, "description", TestAccVcdCatalogMediaDescription), resource.TestCheckResourceAttr( @@ -83,7 +93,7 @@ func TestAccVcdCatalogMediaBasic(t *testing.T) { ImportStateVerify: true, ImportStateIdFunc: importStateIdOrgCatalogObject(TestAccVcdCatalogMedia), // These fields can't be retrieved from catalog media data - ImportStateVerifyIgnore: []string{"media_path", "upload_piece_size", "show_upload_progress"}, + ImportStateVerifyIgnore: []string{"media_path", "upload_piece_size", "show_upload_progress", "upload_any_file"}, }, }, }) @@ -142,32 +152,32 @@ func testAccCheckVcdCatalogMediaExists(mediaName string) resource.TestCheckFunc } } -func testAccCheckCatalogMediaDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*VCDClient) - for _, rs := range s.RootModule().Resources { - if rs.Type != "vcd_catalog_media" && rs.Primary.Attributes["name"] != TestAccVcdCatalogMedia { - continue - } - - adminOrg, err := conn.GetAdminOrg(testConfig.VCD.Org) - if err != nil { - return fmt.Errorf(errorRetrievingOrg, testConfig.VCD.Org+" and error: "+err.Error()) - } - - catalog, err := adminOrg.GetCatalogByName(testConfig.VCD.Catalog.Name, false) - if err != nil { - return fmt.Errorf("catalog query %s ended with error: %#v", rs.Primary.ID, err) +func testAccCheckCatalogMediaDestroy(mediaName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*VCDClient) + for _, rs := range s.RootModule().Resources { + if rs.Type != "vcd_catalog_media" && rs.Primary.Attributes["name"] != mediaName { + continue + } + + adminOrg, err := conn.GetAdminOrg(testConfig.VCD.Org) + if err != nil { + return fmt.Errorf(errorRetrievingOrg, testConfig.VCD.Org+" and error: "+err.Error()) + } + + catalog, err := adminOrg.GetCatalogByName(testConfig.VCD.Catalog.Name, false) + if err != nil { + return fmt.Errorf("catalog query %s ended with error: %#v", rs.Primary.ID, err) + } + + _, err = catalog.GetMediaByName(mediaName, false) + if err == nil { + return fmt.Errorf("catalog media %s still exists", mediaName) + } } - mediaName := rs.Primary.Attributes["name"] - _, err = catalog.GetMediaByName(mediaName, false) - - if err == nil { - return fmt.Errorf("catalog media %s still exists", mediaName) - } + return nil } - - return nil } const testAccCheckVcdCatalogMediaBasic = ` @@ -183,7 +193,7 @@ resource "vcd_catalog_media" "{{.CatalogMediaName}}" { description = "{{.Description}}" media_path = "{{.MediaPath}}" upload_piece_size = {{.UploadPieceSize}} - show_upload_progress = "{{.UploadProgress}}" + show_upload_progress = {{.UploadProgress}} metadata = { mediaItem_metadata = "mediaItem Metadata" @@ -191,6 +201,17 @@ resource "vcd_catalog_media" "{{.CatalogMediaName}}" { } } +resource "vcd_catalog_media" "text_file" { + catalog_id = data.vcd_catalog.{{.Catalog}}.id + + name = "{{.CatalogMediaName}}-2" + description = "{{.Description}} 2" + media_path = "resource_vcd_catalog_media.go" + upload_any_file = true + upload_piece_size = {{.UploadPieceSize}} + show_upload_progress = {{.UploadProgress}} +} + output "creation_date" { value = vcd_catalog_media.{{.CatalogMediaName}}.creation_date depends_on = [vcd_catalog_media.{{.CatalogMediaName}}] @@ -221,9 +242,14 @@ output "storage_profile_name" { }` const testAccCheckVcdCatalogMediaUpdate = ` - resource "vcd_catalog_media" "{{.CatalogMediaName}}" { - org = "{{.Org}}" - catalog = "{{.Catalog}}" +data "vcd_catalog" "{{.Catalog}}" { + org = "{{.Org}}" + name = "{{.Catalog}}" +} + +resource "vcd_catalog_media" "{{.CatalogMediaName}}" { + org = "{{.Org}}" + catalog_id = data.vcd_catalog.{{.Catalog}}.id name = "{{.CatalogMediaName}}" description = "{{.Description}}" @@ -232,11 +258,22 @@ const testAccCheckVcdCatalogMediaUpdate = ` show_upload_progress = "{{.UploadProgress}}" metadata = { - mediaItem_metadata = "mediaItem Metadata v2" + mediaItem_metadata = "mediaItem Metadata v2" mediaItem_metadata2 = "mediaItem Metadata2 v2" mediaItem_metadata3 = "mediaItem Metadata3" } } + +resource "vcd_catalog_media" "text_file" { + catalog_id = data.vcd_catalog.{{.Catalog}}.id + + name = "{{.CatalogMediaName}}-2-update" + description = "{{.Description}} 2 update" + media_path = "resource_vcd_catalog_media.go" + upload_any_file = true + upload_piece_size = {{.UploadPieceSize}} + show_upload_progress = {{.UploadProgress}} +} ` // TestAccVcdCatalogMediaMetadata tests metadata CRUD on catalog media diff --git a/website/docs/d/catalog_media.html.markdown b/website/docs/d/catalog_media.html.markdown index 2258ba188..ef328ecaf 100644 --- a/website/docs/d/catalog_media.html.markdown +++ b/website/docs/d/catalog_media.html.markdown @@ -47,6 +47,12 @@ The following arguments are supported: * `catalog_id` - (Optional; *v3.8.2+*) The ID of the catalog to which the media file belongs. It's mandatory if `catalog` field is not used. * `name` - (Required) Media name in catalog (optional when `filter` is used) * `filter` - (Optional; *2.9+*) Retrieves the data source using one or more filter parameters +* `download_to_file` - (Optional; *3.11+*) Will download the contents of the media item into the given file + +-> NOTE: downloading of media items can take unexpectedly long amounts of time for large items. The ability of +downloading media items is supplied here to solve a specific problem, i.e. saving small files in the VCD as help +to other workflows. For example, you could save into a media item an HCL file used to configure the VCD, the file +`terraform.tfstate`, planning documents, an image of the deployment topology, and so on. ## Attribute reference diff --git a/website/docs/r/catalog_media.html.markdown b/website/docs/r/catalog_media.html.markdown index f7ffe9fc6..e9dd98ee2 100644 --- a/website/docs/r/catalog_media.html.markdown +++ b/website/docs/r/catalog_media.html.markdown @@ -53,6 +53,7 @@ The following arguments are supported: * `media_path` - (Required) - Absolute or relative path to file to upload * `upload_piece_size` - (Optional) - size in MB for splitting upload size. It can possibly impact upload performance. Default 1MB. * `show_upload_progress` - (Optional) - Default false. Allows to see upload progress. (See note below) +* `upload_any_file` - (Optional; *v3.11+*) - If `true`, allows uploading any file type. With the default `false`, we can only upload `.ISO` files. * `metadata` - (Deprecated; *v2.5+*) Use `metadata_entry` instead. Key value map of metadata to assign * `metadata_entry` - (Optional; *v3.8+*) A set of metadata entries to assign. See [Metadata](#metadata) section for details. From dadb8eaf77df9a382280695a2990689d9fd69148 Mon Sep 17 00:00:00 2001 From: Giuseppe Maxia Date: Fri, 20 Oct 2023 14:11:38 +0200 Subject: [PATCH 2/2] Fix resource list usage of "parent" with entities depending on Org (#1140) * Move firstNonEmpty from api_test.go to structure.go * Improve retrieval of Org in vcd_resource_list * Add "parent" field into consideration when "org" is empty * Use client.GetAdminOrg, which uses the provider "org" field if the above ones are empty. * client.GetAdminOrg also returns a better error message * Add test for VDC group list --------- Signed-off-by: Giuseppe Maxia --- .changes/v3.11.0/1140-bug-fixes.md | 2 ++ vcd/api_test.go | 11 ---------- vcd/datasource_vcd_resource_list.go | 16 +++++++------- vcd/datasource_vcd_resource_list_test.go | 28 +++++++++++++++++++----- vcd/structure.go | 11 ++++++++++ 5 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 .changes/v3.11.0/1140-bug-fixes.md diff --git a/.changes/v3.11.0/1140-bug-fixes.md b/.changes/v3.11.0/1140-bug-fixes.md new file mode 100644 index 000000000..b100c741d --- /dev/null +++ b/.changes/v3.11.0/1140-bug-fixes.md @@ -0,0 +1,2 @@ +* Fix organization retrieval in `vcd_resource_list` when users fill the `"parent"` field instead of `"org"` [GH-1140] +* Fix organization retrieval in `vcd_resource_list` when field `"org"` from the provider block was not used [GH-1140] \ No newline at end of file diff --git a/vcd/api_test.go b/vcd/api_test.go index c99c255ba..8dde449cd 100644 --- a/vcd/api_test.go +++ b/vcd/api_test.go @@ -151,14 +151,3 @@ func getVersionFromFile(fileName string) (string, error) { return strings.TrimSpace(string(versionText)), nil } - -// firstNonEmpty returns the first non empty string from a list -// If all arguments are empty, returns an empty string -func firstNonEmpty(args ...string) string { - for _, s := range args { - if s != "" { - return s - } - } - return "" -} diff --git a/vcd/datasource_vcd_resource_list.go b/vcd/datasource_vcd_resource_list.go index 57407bb69..164a2fb29 100644 --- a/vcd/datasource_vcd_resource_list.go +++ b/vcd/datasource_vcd_resource_list.go @@ -148,7 +148,7 @@ func getPvdcList(d *schema.ResourceData, meta interface{}) (list []string, err e func getVdcGroups(d *schema.ResourceData, meta interface{}) (list []string, err error) { client := meta.(*VCDClient) - org, err := client.GetAdminOrgByName(d.Get("org").(string)) + org, err := client.GetAdminOrg(firstNonEmpty(d.Get("org").(string), d.Get("parent").(string))) if err != nil { return list, err } @@ -203,7 +203,8 @@ func externalNetworkList(d *schema.ResourceData, meta interface{}) (list []strin func rightsList(d *schema.ResourceData, meta interface{}) (list []string, err error) { client := meta.(*VCDClient) - org, err := client.GetAdminOrg(d.Get("org").(string)) + + org, err := client.GetAdminOrg(firstNonEmpty(d.Get("org").(string), d.Get("parent").(string))) if err != nil { return list, err } @@ -229,7 +230,7 @@ func rightsList(d *schema.ResourceData, meta interface{}) (list []string, err er func rolesList(d *schema.ResourceData, meta interface{}) (list []string, err error) { client := meta.(*VCDClient) - org, err := client.GetAdminOrg(d.Get("org").(string)) + org, err := client.GetAdminOrg(firstNonEmpty(d.Get("org").(string), d.Get("parent").(string))) if err != nil { return list, err } @@ -276,7 +277,7 @@ func globalRolesList(d *schema.ResourceData, meta interface{}) (list []string, e func libraryCertificateList(d *schema.ResourceData, meta interface{}) (list []string, err error) { client := meta.(*VCDClient) - adminOrg, err := client.GetAdminOrg(d.Get("org").(string)) + adminOrg, err := client.GetAdminOrg(firstNonEmpty(d.Get("org").(string), d.Get("parent").(string))) if err != nil { return list, err } @@ -332,8 +333,7 @@ func rightsBundlesList(d *schema.ResourceData, meta interface{}) (list []string, func catalogList(d *schema.ResourceData, meta interface{}, resType string) (list []string, err error) { client := meta.(*VCDClient) - - org, err := client.GetAdminOrg(d.Get("org").(string)) + org, err := client.GetAdminOrg(firstNonEmpty(d.Get("org").(string), d.Get("parent").(string))) if err != nil { return list, err } @@ -449,7 +449,7 @@ func vappTemplateList(d *schema.ResourceData, meta interface{}) (list []string, func vdcList(d *schema.ResourceData, meta interface{}, resType string) (list []string, err error) { client := meta.(*VCDClient) - org, err := client.GetAdminOrg(d.Get("org").(string)) + org, err := client.GetAdminOrg(firstNonEmpty(d.Get("org").(string), d.Get("parent").(string))) if err != nil { return list, err } @@ -469,7 +469,7 @@ func vdcList(d *schema.ResourceData, meta interface{}, resType string) (list []s func orgUserList(d *schema.ResourceData, meta interface{}) (list []string, err error) { client := meta.(*VCDClient) - org, err := client.GetAdminOrg(d.Get("org").(string)) + org, err := client.GetAdminOrg(firstNonEmpty(d.Get("org").(string), d.Get("parent").(string))) if err != nil { return list, err } diff --git a/vcd/datasource_vcd_resource_list_test.go b/vcd/datasource_vcd_resource_list_test.go index c5e777535..a89e9bba9 100644 --- a/vcd/datasource_vcd_resource_list_test.go +++ b/vcd/datasource_vcd_resource_list_test.go @@ -154,13 +154,31 @@ func TestAccVcdDatasourceResourceList(t *testing.T) { "VCD.Vdc` value isn't configured, datasource test using this will be skipped\n") } - if testConfig.Nsxt.VdcGroup != "" && testConfig.Nsxt.VdcGroupEdgeGateway != "" { + if testConfig.Nsxt.VdcGroup != "" { + // Retrieves the list of VDC groups, filling the "parent" field lists = append(lists, listDef{ - name: "VdcGroupEdge", - resourceType: "vcd_nsxt_edgegateway", - parent: testConfig.Nsxt.VdcGroup, - knownItem: testConfig.Nsxt.VdcGroupEdgeGateway, + name: "VdcGroupAsParent", + resourceType: "vcd_vdc_group", + parent: testConfig.VCD.Org, + knownItem: testConfig.Nsxt.VdcGroup, + }) + + // Retrieves the list of VDC groups, filling only the "org" field through the provider + lists = append(lists, listDef{ + name: "VdcGroupAsOrg", + resourceType: "vcd_vdc_group", + knownItem: testConfig.Nsxt.VdcGroup, }) + + // Retrieves the list of NSX-T edge gateway belonging to a VDC group + if testConfig.Nsxt.VdcGroupEdgeGateway != "" { + lists = append(lists, listDef{ + name: "VdcGroupEdge", + resourceType: "vcd_nsxt_edgegateway", + parent: testConfig.Nsxt.VdcGroup, + knownItem: testConfig.Nsxt.VdcGroupEdgeGateway, + }) + } } if testConfig.Nsxt.Vdc != "" { diff --git a/vcd/structure.go b/vcd/structure.go index 09523178a..a54430d91 100644 --- a/vcd/structure.go +++ b/vcd/structure.go @@ -349,3 +349,14 @@ func ObjectMap[Input any, Output any](input []Input, f func(Input) Output) []Out } return result } + +// firstNonEmpty returns the first non empty string from a list +// If all arguments are empty, returns an empty string +func firstNonEmpty(args ...string) string { + for _, s := range args { + if s != "" { + return s + } + } + return "" +}