From 9334ae8e320fb8c8b12f60e03a78a17deac00117 Mon Sep 17 00:00:00 2001 From: Adam Barreiro Date: Mon, 21 Oct 2024 09:27:17 +0200 Subject: [PATCH] Add vcd_tm_region_storage_policy + vcd_tm_content_library resource and data source (#1339) Signed-off-by: abarreiro --- .changes/v4.0.0/1339-features.md | 3 + .github/workflows/check-docs.yml | 3 + GNUmakefile | 5 +- scripts/runtest.sh | 7 +- vcd/config_test.go | 14 +- vcd/datasource_vcd_tm_content_library.go | 98 +++++++ ...datasource_vcd_tm_region_storage_policy.go | 50 ++++ vcd/provider.go | 5 + vcd/provider_test.go | 2 +- vcd/remove_leftovers_test.go | 5 + vcd/resource_vcd_tm_content_library.go | 270 ++++++++++++++++++ vcd/resource_vcd_tm_content_library_test.go | 93 ++++++ vcd/resource_vcd_tm_region_storage_policy.go | 50 ++++ vcd/testcheck_funcs_test.go | 2 +- .../docs/d/tm_content_library.html.markdown | 39 +++ .../d/tm_region_storage_policy.html.markdown | 42 +++ website/docs/r/service_account.html.markdown | 8 +- .../docs/r/tm_content_library.html.markdown | 87 ++++++ website/vcd.erb | 9 + 19 files changed, 782 insertions(+), 10 deletions(-) create mode 100644 .changes/v4.0.0/1339-features.md create mode 100644 vcd/datasource_vcd_tm_content_library.go create mode 100644 vcd/datasource_vcd_tm_region_storage_policy.go create mode 100644 vcd/resource_vcd_tm_content_library.go create mode 100644 vcd/resource_vcd_tm_content_library_test.go create mode 100644 vcd/resource_vcd_tm_region_storage_policy.go create mode 100644 website/docs/d/tm_content_library.html.markdown create mode 100644 website/docs/d/tm_region_storage_policy.html.markdown create mode 100644 website/docs/r/tm_content_library.html.markdown diff --git a/.changes/v4.0.0/1339-features.md b/.changes/v4.0.0/1339-features.md new file mode 100644 index 000000000..cc14ccb53 --- /dev/null +++ b/.changes/v4.0.0/1339-features.md @@ -0,0 +1,3 @@ +* **New Data Source:** `vcd_tm_region_storage_policy` to read Region Storage Policies [GH-1339] +* **New Resource:** `vcd_tm_content_library` to manage Content Libraries [GH-1339] +* **New Data Source:** `vcd_tm_content_library` to read Content Libraries [GH-1339] diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml index 02d989729..075c74ab0 100644 --- a/.github/workflows/check-docs.yml +++ b/.github/workflows/check-docs.yml @@ -21,6 +21,9 @@ jobs: with: fetch-depth: 0 # Required to have tag information available + - name: Install Terraform binary + uses: hashicorp/setup-terraform@v3 + - name: Set up Go 1.x uses: actions/setup-go@v3 with: diff --git a/GNUmakefile b/GNUmakefile index 08aecc93f..dba27551a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -2,7 +2,6 @@ TEST?=$$(go list ./... ) GOFMT_FILES?=$$(find . -name '*.go' ) WEBSITE_REPO=github.com/hashicorp/terraform-website GIT_DESCRIBE=$(shell git describe --tags) - PKG_NAME=vcd default: build @@ -137,6 +136,10 @@ testnetwork: fmtcheck testextnetwork: fmtcheck @sh -c "'$(CURDIR)/scripts/runtest.sh' extnetwork" +# Runs the acceptance test for tm +testtm: fmtcheck + @sh -c "'$(CURDIR)/scripts/runtest.sh' tm" + # vets all .go files vet: @echo "go vet ." diff --git a/scripts/runtest.sh b/scripts/runtest.sh index 332b0432b..7a7ca197c 100755 --- a/scripts/runtest.sh +++ b/scripts/runtest.sh @@ -122,13 +122,13 @@ function acceptance_test { if [ -n "$VERBOSE" ] then echo "# check for config file" - echo "TF_ACC=1 go test -tags '$tags' -v -timeout $timeout" + echo "TF_ACC=1 go test -tags '$tags' -vcd-add-provider -v -timeout $timeout" fi if [ -z "$DRY_RUN" ] then check_for_config_file - TF_ACC=1 go test -tags "$tags" $testoptions -v -timeout $timeout + TF_ACC=1 go test -tags "$tags" $testoptions -vcd-add-provider -v -timeout $timeout check_exit_code fi } @@ -403,6 +403,9 @@ case $wanted in vm) acceptance_test vm ;; + tm) + acceptance_test tm + ;; network) acceptance_test network ;; diff --git a/vcd/config_test.go b/vcd/config_test.go index 82ddc2f4f..fac2ffa1a 100644 --- a/vcd/config_test.go +++ b/vcd/config_test.go @@ -1,4 +1,4 @@ -//go:build api || functional || catalog || vapp || network || extnetwork || org || query || vm || vdc || gateway || disk || binary || lb || lbServiceMonitor || lbServerPool || lbAppProfile || lbAppRule || lbVirtualServer || access_control || user || standaloneVm || search || auth || nsxt || role || alb || certificate || vdcGroup || ldap || rde || uiPlugin || providerVdc || cse || slz || multisite || ALL +//go:build api || functional || catalog || vapp || network || extnetwork || org || query || vm || vdc || gateway || disk || binary || lb || lbServiceMonitor || lbServerPool || lbAppProfile || lbAppRule || lbVirtualServer || access_control || user || standaloneVm || search || auth || nsxt || role || alb || certificate || vdcGroup || ldap || rde || uiPlugin || providerVdc || cse || slz || multisite || tm || ALL package vcd @@ -107,6 +107,12 @@ type TestConfig struct { UseVcdConnectionCache bool `json:"useVcdConnectionCache"` MaxRetryTimeout int `json:"maxRetryTimeout"` } `json:"provider"` + Tm struct { + Org string `json:"org"` // temporary field to make skipIfNotTm work + Region string `json:"region"` + RegionStoragePolicy string `json:"regionStoragePolicy"` + Vdc string `json:"vdc"` + } `json:"tm,omitempty"` VCD struct { Org string `json:"org"` Vdc string `json:"vdc"` @@ -416,6 +422,12 @@ func skipIfNotSysAdmin(t *testing.T) { } } +func skipIfNotTm(t *testing.T) { + if testConfig.Tm.Org == "" { + t.Skip(t.Name() + " requires 'tm' branch filled in") + } +} + // Gets a list of all variables mentioned in a template func GetVarsFromTemplate(tmpl string) []string { var varList []string diff --git a/vcd/datasource_vcd_tm_content_library.go b/vcd/datasource_vcd_tm_content_library.go new file mode 100644 index 000000000..496f05426 --- /dev/null +++ b/vcd/datasource_vcd_tm_content_library.go @@ -0,0 +1,98 @@ +package vcd + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func datasourceVcdTmContentLibrary() *schema.Resource { + return &schema.Resource{ + ReadContext: datasourceVcdTmContentLibraryRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the Content Library", + }, + "storage_policy_ids": { + Type: schema.TypeSet, + Computed: true, + Description: "A set of Region Storage Policy or VDC Storage Policy IDs used by this Content Library", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "auto_attach": { + Type: schema.TypeBool, + Computed: true, + Description: "For Tenant Content Libraries this field represents whether this Content Library should be " + + "automatically attached to all current and future namespaces in the tenant organization", + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + Description: "The ISO-8601 timestamp representing when this Content Library was created", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the Content Library", + }, + "is_shared": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether this Content Library is shared with other Organziations", + }, + "is_subscribed": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether this Content Library is subscribed from an external published library", + }, + "library_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of content library, can be either PROVIDER (Content Library that is scoped to a " + + "provider) or TENANT (Content Library that is scoped to a tenant organization)", + }, + "owner_org_id": { + Type: schema.TypeString, + Computed: true, + Description: "The reference to the Organization that the Content Library belongs to", + }, + "subscription_config": { + Type: schema.TypeList, + Computed: true, + Description: "A block representing subscription settings of a Content Library", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "subscription_url": { + Type: schema.TypeString, + Computed: true, + Description: "Subscription url of this Content Library", + }, + "password": { + Type: schema.TypeString, + Computed: true, + Description: "Password to use to authenticate with the publisher", + }, + "need_local_copy": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether to eagerly download content from publisher and store it locally", + }, + }, + }, + }, + "version_number": { + Type: schema.TypeInt, + Computed: true, + Description: "Version number of this Content library", + }, + }, + } +} + +func datasourceVcdTmContentLibraryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return genericVcdTmContentLibraryRead(ctx, d, meta, "datasource") +} diff --git a/vcd/datasource_vcd_tm_region_storage_policy.go b/vcd/datasource_vcd_tm_region_storage_policy.go new file mode 100644 index 000000000..3bac320ef --- /dev/null +++ b/vcd/datasource_vcd_tm_region_storage_policy.go @@ -0,0 +1,50 @@ +package vcd + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func datasourceVcdTmRegionStoragePolicy() *schema.Resource { + return &schema.Resource{ + ReadContext: datasourceVcdTmRegionStoragePolicyRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Region Storage Policy name", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "Description of the Region Storage Policy", + }, + "region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The Region that this Region Storage Policy belongs to", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The creation status of the Region Storage Policy. Can be [NOT_READY, READY]", + }, + "storage_capacity_mb": { + Type: schema.TypeInt, + Computed: true, + Description: "Storage capacity in megabytes for this Region Storage Policy", + }, + "storage_consumed_mb": { + Type: schema.TypeInt, + Computed: true, + Description: "Consumed storage in megabytes for this Region Storage Policy", + }, + }, + } +} + +func datasourceVcdTmRegionStoragePolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return genericVcdTmRegionStoragePolicyRead(ctx, d, meta, "datasource") +} diff --git a/vcd/provider.go b/vcd/provider.go index a7a272a12..7cb1675f4 100644 --- a/vcd/provider.go +++ b/vcd/provider.go @@ -175,6 +175,9 @@ var globalDataSourceMap = map[string]*schema.Resource{ "vcd_nsxt_alb_virtual_service_http_req_rules": datasourceVcdAlbVirtualServiceReqRules(), // 3.14 "vcd_nsxt_alb_virtual_service_http_resp_rules": datasourceVcdAlbVirtualServiceRespRules(), // 3.14 "vcd_nsxt_alb_virtual_service_http_sec_rules": datasourceVcdAlbVirtualServiceSecRules(), // 3.14 + + "vcd_tm_region_storage_policy": datasourceVcdTmRegionStoragePolicy(), // 4.0 + "vcd_tm_content_library": datasourceVcdTmContentLibrary(), // 4.0 } var globalResourceMap = map[string]*schema.Resource{ @@ -301,6 +304,8 @@ var globalResourceMap = map[string]*schema.Resource{ "vcd_nsxt_alb_virtual_service_http_req_rules": resourceVcdAlbVirtualServiceReqRules(), // 3.14 "vcd_nsxt_alb_virtual_service_http_resp_rules": resourceVcdAlbVirtualServiceRespRules(), // 3.14 "vcd_nsxt_alb_virtual_service_http_sec_rules": resourceVcdAlbVirtualServiceSecRules(), // 3.14 + + "vcd_tm_content_library": resourceVcdTmContentLibrary(), // 4.0 } // Provider returns a terraform.ResourceProvider. diff --git a/vcd/provider_test.go b/vcd/provider_test.go index d889d8b20..a554a33da 100644 --- a/vcd/provider_test.go +++ b/vcd/provider_test.go @@ -1,4 +1,4 @@ -//go:build api || functional || catalog || vapp || network || extnetwork || org || query || vm || vdc || gateway || disk || binary || lb || lbAppProfile || lbAppRule || lbServiceMonitor || lbServerPool || lbVirtualServer || user || access_control || standaloneVm || search || auth || nsxt || role || alb || certificate || vdcGroup || ldap || rde || uiPlugin || providerVdc || cse || slz || multisite || ALL +//go:build api || functional || catalog || vapp || network || extnetwork || org || query || vm || vdc || gateway || disk || binary || lb || lbAppProfile || lbAppRule || lbServiceMonitor || lbServerPool || lbVirtualServer || user || access_control || standaloneVm || search || auth || nsxt || role || alb || certificate || vdcGroup || ldap || rde || uiPlugin || providerVdc || cse || slz || multisite || tm || ALL package vcd diff --git a/vcd/remove_leftovers_test.go b/vcd/remove_leftovers_test.go index d37d1577a..bbddafd72 100644 --- a/vcd/remove_leftovers_test.go +++ b/vcd/remove_leftovers_test.go @@ -114,6 +114,11 @@ func removeLeftovers(govcdClient *govcd.VCDClient, verbose bool) error { fmt.Printf("Start leftovers removal\n") } + if govcdClient.Client.IsTm() { + fmt.Printf("Skipping leftover removal for TM\n") + return nil + } + // NSX-T ALB configuration Hierarchical cleanup is separate from main hierarchy as even if the // Org is going to be deleted - NSX-T ALB configuration must be cleaned up first // Only System user can control ALB resources diff --git a/vcd/resource_vcd_tm_content_library.go b/vcd/resource_vcd_tm_content_library.go new file mode 100644 index 000000000..6e11b6c66 --- /dev/null +++ b/vcd/resource_vcd_tm_content_library.go @@ -0,0 +1,270 @@ +package vcd + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/go-vcloud-director/v3/govcd" + "github.com/vmware/go-vcloud-director/v3/types/v56" +) + +func resourceVcdTmContentLibrary() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceVcdTmContentLibraryCreate, + ReadContext: resourceVcdTmContentLibraryRead, + UpdateContext: resourceVcdTmContentLibraryUpdate, + DeleteContext: resourceVcdTmContentLibraryDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceVcdTmContentLibraryImport, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, // TODO: TM: Update not supported + Description: "The name of the Content Library", + }, + "storage_policy_ids": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, // TODO: TM: Update not supported + Description: "A set of Region Storage Policy IDs or VDC Storage Policy IDs used by this Content Library", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "auto_attach": { + Type: schema.TypeBool, + Optional: true, + Default: true, + ForceNew: true, // Cannot be updated + Description: "For Tenant Content Libraries this field represents whether this Content Library should be " + + "automatically attached to all current and future namespaces in the tenant organization. If no value is " + + "supplied during creation then this field will default to true. If a value of false is supplied, " + + "then this Tenant Content Library will only be attached to namespaces that explicitly request it. " + + "For Provider Content Libraries this field is not needed for creation and will always be returned as true. " + + "This field cannot be updated after Content Library creation", + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + Description: "The ISO-8601 timestamp representing when this Content Library was created", + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, // TODO: TM: Update not supported + Description: "The description of the Content Library", + }, + "is_shared": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether this Content Library is shared with other Organziations", + }, + "is_subscribed": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether this Content Library is subscribed from an external published library", + }, + "library_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of content library, can be either PROVIDER (Content Library that is scoped to a " + + "provider) or TENANT (Content Library that is scoped to a tenant organization)", + }, + "owner_org_id": { + Type: schema.TypeString, + Computed: true, + Description: "The reference to the Organization that the Content Library belongs to", + }, + "subscription_config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "A block representing subscription settings of a Content Library", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "subscription_url": { + Type: schema.TypeString, + Required: true, + ForceNew: true, // TODO: TM: Update not supported + Description: "Subscription url of this Content Library", + }, + "password": { + Type: schema.TypeString, + Optional: true, // Required at Runtime as cannot be Required + Computed in schema. (It is computed as password cannot be recovered) + Computed: true, + ForceNew: true, // TODO: TM: Update not supported + Description: "Password to use to authenticate with the publisher", + }, + "need_local_copy": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, // TODO: TM: Update not supported + Description: "Whether to eagerly download content from publisher and store it locally", + }, + }, + }, + }, + "version_number": { + Type: schema.TypeInt, + Computed: true, + Description: "Version number of this Content library", + }, + }, + } +} + +func resourceVcdTmContentLibraryCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vcdClient := meta.(*VCDClient) + + t, err := getContentLibraryType(d) + if err != nil { + return diag.Errorf("error getting Content Library type: %s", err) + } + + cl, err := vcdClient.CreateContentLibrary(t) + if err != nil { + return diag.Errorf("error creating Content Library: %s", err) + } + + d.SetId(cl.ContentLibrary.Id) + + return resourceVcdTmContentLibraryRead(ctx, d, meta) +} + +func resourceVcdTmContentLibraryUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vcdClient := meta.(*VCDClient) + rsp, err := vcdClient.GetContentLibraryById(d.Id()) + if err != nil { + return diag.Errorf("error retrieving Content Library: %s", err) + } + + t, err := getContentLibraryType(d) + if err != nil { + return diag.Errorf("error getting Content Library type: %s", err) + } + + _, err = rsp.Update(t) + if err != nil { + return diag.Errorf("error updating Content Library Type: %s", err) + } + + return resourceVcdTmContentLibraryRead(ctx, d, meta) +} + +func resourceVcdTmContentLibraryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return genericVcdTmContentLibraryRead(ctx, d, meta, "resource") +} +func genericVcdTmContentLibraryRead(_ context.Context, d *schema.ResourceData, meta interface{}, origin string) diag.Diagnostics { + vcdClient := meta.(*VCDClient) + + var cl *govcd.ContentLibrary + var err error + if d.Id() != "" { + cl, err = vcdClient.GetContentLibraryById(d.Id()) + } else { + cl, err = vcdClient.GetContentLibraryByName(d.Get("name").(string)) + } + if err != nil { + if origin == "resource" && govcd.ContainsNotFound(err) { + d.SetId("") + return nil + } + return diag.Errorf("error retrieving Content Library: %s", err) + } + + err = setTmContentLibraryData(d, cl.ContentLibrary) + if err != nil { + return diag.Errorf("error saving Content Library data into state: %s", err) + } + + d.SetId(cl.ContentLibrary.Id) + return nil +} + +func resourceVcdTmContentLibraryDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vcdClient := meta.(*VCDClient) + cl, err := vcdClient.GetContentLibraryById(d.Id()) + if err != nil { + return diag.Errorf("error retrieving Content Library: %s", err) + } + + err = cl.Delete() + if err != nil { + return diag.Errorf("error deleting Content Library: %s", err) + } + + return nil +} + +func resourceVcdTmContentLibraryImport(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + vcdClient := meta.(*VCDClient) + rsp, err := vcdClient.GetContentLibraryByName(d.Id()) + if err != nil { + return nil, fmt.Errorf("error retrieving Content Library with name '%s': %s", d.Id(), err) + } + + d.SetId(rsp.ContentLibrary.Id) + dSet(d, "name", rsp.ContentLibrary.Name) + return []*schema.ResourceData{d}, nil +} + +func getContentLibraryType(d *schema.ResourceData) (*types.ContentLibrary, error) { + t := &types.ContentLibrary{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + AutoAttach: d.Get("auto_attach").(bool), + StoragePolicies: convertSliceOfStringsToOpenApiReferenceIds(convertTypeListToSliceOfStrings(d.Get("storage_policy_ids").(*schema.Set).List())), + } + if v, ok := d.GetOk("subscription_config"); ok { + subsConfig := v.([]interface{})[0].(map[string]interface{}) + t.SubscriptionConfig = &types.ContentLibrarySubscriptionConfig{ + SubscriptionUrl: subsConfig["subscription_url"].(string), + NeedLocalCopy: subsConfig["need_local_copy"].(bool), + Password: subsConfig["password"].(string), + } + } + return t, nil +} + +func setTmContentLibraryData(d *schema.ResourceData, cl *types.ContentLibrary) error { + dSet(d, "name", cl.Name) + dSet(d, "auto_attach", cl.AutoAttach) + dSet(d, "creation_date", cl.CreationDate) + dSet(d, "description", cl.Description) + dSet(d, "is_shared", cl.IsShared) + dSet(d, "is_subscribed", cl.IsSubscribed) + dSet(d, "library_type", cl.LibraryType) + dSet(d, "version_number", cl.VersionNumber) + if cl.Org != nil { + dSet(d, "owner_org_id", cl.Org.ID) + } + + sps := make([]string, len(cl.StoragePolicies)) + for i, sp := range cl.StoragePolicies { + sps[i] = sp.ID + } + err := d.Set("storage_policy_ids", sps) + if err != nil { + return err + } + + subscriptionConfig := make([]interface{}, 0) + if cl.SubscriptionConfig != nil { + subscriptionConfig = []interface{}{ + map[string]interface{}{ + "subscription_url": cl.SubscriptionConfig.SubscriptionUrl, + "password": cl.SubscriptionConfig.Password, + "need_local_copy": cl.SubscriptionConfig.NeedLocalCopy, + }, + } + } + err = d.Set("subscription_config", subscriptionConfig) + if err != nil { + return err + } + return nil +} diff --git a/vcd/resource_vcd_tm_content_library_test.go b/vcd/resource_vcd_tm_content_library_test.go new file mode 100644 index 000000000..e9cd48b7d --- /dev/null +++ b/vcd/resource_vcd_tm_content_library_test.go @@ -0,0 +1,93 @@ +//go:build tm || ALL || functional + +package vcd + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccVcdTmContentLibrary(t *testing.T) { + preTestChecks(t) + + skipIfNotSysAdmin(t) + skipIfNotTm(t) + + var params = StringMap{ + "Name": t.Name(), + "RegionStoragePolicy": testConfig.Tm.RegionStoragePolicy, + "Tags": "tm", + } + testParamsNotEmpty(t, params) + + configText1 := templateFill(testAccVcdTmContentLibraryStep1, params) + params["FuncName"] = t.Name() + "-step2" + configText2 := templateFill(testAccVcdTmContentLibraryStep2, params) + + debugPrintf("#[DEBUG] CONFIGURATION step1: %s\n", configText1) + debugPrintf("#[DEBUG] CONFIGURATION step2: %s\n", configText2) + if vcdShortTest { + t.Skip(acceptanceTestsSkipped) + return + } + + resourceName := "vcd_tm_content_library.cl" + + resource.Test(t, resource.TestCase{ + ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: configText1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", t.Name()), + resource.TestCheckResourceAttr(resourceName, "description", t.Name()), + resource.TestCheckResourceAttr(resourceName, "storage_policy_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "auto_attach", "true"), // TODO: TM: Test with false + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttr(resourceName, "is_shared", "true"), // TODO: TM: Test with false + resource.TestCheckResourceAttr(resourceName, "is_subscribed", "false"), // TODO: TM: Test with true + resource.TestCheckResourceAttr(resourceName, "library_type", "PROVIDER"), // TODO: TM: Test with tenant catalog + resource.TestMatchResourceAttr(resourceName, "owner_org_id", regexp.MustCompile("urn:vcloud:org:")), + resource.TestCheckResourceAttr(resourceName, "subscription_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "version_number", "1"), + ), + }, + { + Config: configText2, + Check: resource.ComposeTestCheckFunc( + resourceFieldsEqual(resourceName, "data.vcd_tm_content_library.cl_ds", nil), + ), + }, + { + ResourceName: "vcd_tm_content_library.cl", + ImportState: true, + ImportStateVerify: true, + ImportStateId: params["Name"].(string), + }, + }, + }) + + postTestChecks(t) +} + +const testAccVcdTmContentLibraryStep1 = ` +data "vcd_tm_region_storage_policy" "sp" { + name = "{{.RegionStoragePolicy}}" +} + +resource "vcd_tm_content_library" "cl" { + name = "{{.Name}}" + description = "{{.Name}}" + storage_policy_ids = [ + data.vcd_tm_region_storage_policy.sp.id + ] +} +` + +const testAccVcdTmContentLibraryStep2 = testAccVcdTmContentLibraryStep1 + ` +data "vcd_tm_content_library" "cl_ds" { + name = vcd_tm_content_library.cl.name +} +` diff --git a/vcd/resource_vcd_tm_region_storage_policy.go b/vcd/resource_vcd_tm_region_storage_policy.go new file mode 100644 index 000000000..dfd59b3ba --- /dev/null +++ b/vcd/resource_vcd_tm_region_storage_policy.go @@ -0,0 +1,50 @@ +package vcd + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/go-vcloud-director/v3/govcd" + "github.com/vmware/go-vcloud-director/v3/types/v56" +) + +func genericVcdTmRegionStoragePolicyRead(_ context.Context, d *schema.ResourceData, meta interface{}, origin string) diag.Diagnostics { + vcdClient := meta.(*VCDClient) + var rsp *govcd.RegionStoragePolicy + var err error + if d.Id() != "" { + rsp, err = vcdClient.GetRegionStoragePolicyById(d.Id()) + } else { + rsp, err = vcdClient.GetRegionStoragePolicyByName(d.Get("name").(string)) + } + if err != nil { + if origin == "resource" && govcd.ContainsNotFound(err) { + d.SetId("") + return nil + } + return diag.Errorf("error retrieving Region Storage Policy: %s", err) + } + + err = setRegionStoragePolicyData(d, rsp.RegionStoragePolicy) + if err != nil { + return diag.Errorf("error saving Region Storage Policy data into state: %s", err) + } + + d.SetId(rsp.RegionStoragePolicy.Id) + return nil +} + +func setRegionStoragePolicyData(d *schema.ResourceData, rsp *types.RegionStoragePolicy) error { + dSet(d, "name", rsp.Name) + dSet(d, "description", rsp.Description) + regionId := "" + if rsp.Region != nil { + regionId = rsp.Region.ID + } + dSet(d, "region_id", regionId) + dSet(d, "storage_capacity_mb", rsp.StorageCapacityMB) + dSet(d, "storage_consumed_mb", rsp.StorageConsumedMB) + dSet(d, "status", rsp.Status) + + return nil +} diff --git a/vcd/testcheck_funcs_test.go b/vcd/testcheck_funcs_test.go index 0f191d8c8..ead1f8c7c 100644 --- a/vcd/testcheck_funcs_test.go +++ b/vcd/testcheck_funcs_test.go @@ -1,4 +1,4 @@ -//go:build api || vapp || vm || user || nsxt || extnetwork || network || gateway || catalog || standaloneVm || alb || vdcGroup || ldap || vdc || access_control || rde || uiPlugin || org || disk || providerVdc || cse || ALL || slz || functional +//go:build api || vapp || vm || user || nsxt || extnetwork || network || gateway || catalog || standaloneVm || alb || vdcGroup || ldap || vdc || access_control || rde || uiPlugin || org || disk || providerVdc || cse || ALL || slz || tm || functional package vcd diff --git a/website/docs/d/tm_content_library.html.markdown b/website/docs/d/tm_content_library.html.markdown new file mode 100644 index 000000000..b2034f292 --- /dev/null +++ b/website/docs/d/tm_content_library.html.markdown @@ -0,0 +1,39 @@ +--- +layout: "vcd" +page_title: "VMware Cloud Foundation Tenant Manager: vcd_tm_content_library" +sidebar_current: "docs-vcd-data-source-tm-content-library" +description: |- + Provides a VMware Cloud Foundation Tenant Manager Content Library data source. This can be used to read Content Libraries. +--- + +# vcd\_content\_library + +Provides a VMware Cloud Foundation Tenant Manager Content Library data source. This can be used to read Content Libraries. + +This data source is exclusive to **VMware Cloud Foundation Tenant Manager**. Supported in provider *v4.0+* + +## Example Usage + +```hcl +data "vcd_tm_content_library" "cl" { + name = "My Library" +} + +output "is_shared" { + value = data.vcd_tm_content_library.cl.is_shared +} +output "owner_org" { + value = data.vcd_tm_content_library.cl.owner_org_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Content Library to read + +## Attribute reference + +All arguments and attributes defined in [the resource](/providers/vmware/vcd/latest/docs/resources/tm_content_library) are supported +as read-only (Computed) values. diff --git a/website/docs/d/tm_region_storage_policy.html.markdown b/website/docs/d/tm_region_storage_policy.html.markdown new file mode 100644 index 000000000..a4a79eebb --- /dev/null +++ b/website/docs/d/tm_region_storage_policy.html.markdown @@ -0,0 +1,42 @@ +--- +layout: "vcd" +page_title: "VMware Cloud Foundation Tenant Manager: vcd_tm_region_storage_policy" +sidebar_current: "docs-vcd-data-source-tm-region-storage-policy" +description: |- + Provides a VMware Cloud Foundation Tenant Manager Region Storage Policy data source. This can be used to read Content Libraries. +--- + +# vcd\_tm\_region\_storage\_policy + +Provides a VMware Cloud Foundation Tenant Manager Region Storage Policy data source. This can be used to read Region Storage Policies. + +This data source is exclusive to **VMware Cloud Foundation Tenant Manager**. Supported in provider *v4.0+* + +## Example Usage + +```hcl +data "vcd_tm_region_storage_policy" "sp" { + name = "vSAN Default Storage Policy" +} + +resource "vcd_tm_content_library" "cl" { + name = "My Library" + description = "A simple library" + storage_policy_ids = [ + data.vcd_tm_region_storage_policy.sp.id + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Region Storage Policy to read + +## Attribute reference + +// TODO: TM (Resource is not implemented yet) + +All arguments and attributes defined in [the resource](/providers/vmware/vcd/latest/docs/resources/tm_region_storage_policy) are supported +as read-only (Computed) values. diff --git a/website/docs/r/service_account.html.markdown b/website/docs/r/service_account.html.markdown index 2dee65388..4e1e62ecb 100644 --- a/website/docs/r/service_account.html.markdown +++ b/website/docs/r/service_account.html.markdown @@ -26,9 +26,9 @@ data "vcd_role" "vapp_author" { } resource "vcd_service_account" "example_service" { - org = "my-org" - name = "example" - role = data.vcd_role.vapp_author.id + org = "my-org" + name = "example" + role_id = data.vcd_role.vapp_author.id software_id = "12345678-1234-1234-1234-1234567890ab" software_version = "1.0.0" @@ -49,7 +49,7 @@ The following arguments are supported: * `org` - (Optional) The name of organization to use, optional if defined at provider level. Useful when connected as sysadmin working across different organisations. * `name` - (Required) A unique name for the Service Account in an organisation. -* `role` - (Required) ID of a Role. +* `role_id` - (Required) ID of a Role. * `software_id` - (Required) UUID of the Service Account. * `software_version` - (Optional) Version of the service using the Service Account * `uri` - (Optional) URI of the service using the Service Account diff --git a/website/docs/r/tm_content_library.html.markdown b/website/docs/r/tm_content_library.html.markdown new file mode 100644 index 000000000..15616a742 --- /dev/null +++ b/website/docs/r/tm_content_library.html.markdown @@ -0,0 +1,87 @@ +--- +layout: "vcd" +page_title: "VMware Cloud Foundation Tenant Manager: vcd_tm_content_library" +sidebar_current: "docs-vcd-resource-tm-content-library" +description: |- + Provides a VMware Cloud Foundation Tenant Manager Content Library resource. This can be used to manage Content Libraries. +--- + +# vcd\_tm\_content\_library + +Provides a VMware Cloud Foundation Tenant Manager Content Library resource. This can be used to manage Content Libraries. + +This resource is exclusive to **VMware Cloud Foundation Tenant Manager**. Supported in provider *v4.0+* + +## Example Usage for a Provider Content Library + +```hcl +data "vcd_tm_region_storage_policy" "sp" { + name = "vSAN Default Storage Policy" +} + +resource "vcd_tm_content_library" "cl" { + name = "My Library" + description = "A simple library" + storage_policy_ids = [ + data.vcd_tm_region_storage_policy.sp.id + ] +} +``` + +## Example Usage for a Tenant Content Library + +// TODO: TM (Tenant support is not implemented yet) + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Content Library +* `storage_policy_ids` - (Required) A set of [Region Storage Policy](/providers/vmware/vcd/latest/docs/resources/tm_region_storage_policy) IDs or VDC Storage Policy IDs used by this Content Library +* `auto_attach` - (Optional) Defaults to `true`. For Tenant Content Libraries this field represents whether this Content Library should be + automatically attached to all current and future namespaces in the tenant organization. If a value of `false` is supplied, then this + Tenant Content Library will only be attached to namespaces that explicitly request it. For Provider Content Libraries this field is not needed + for creation and will always be returned as true. This field cannot be updated after Content Library creation +* `description` - (Optional) The description of the Content Library +* `subscription_config` - (Optional) A block representing subscription settings of a Content Library: + * `subscription_url` - Subscription url of this Content Library + * `password` - Password to use to authenticate with the publisher + * `need_local_copy` - Whether to eagerly download content from publisher and store it locally + +## Attribute Reference + +* `creation_date` - The ISO-8601 timestamp representing when this Content Library was created +* `is_shared` - Whether this Content Library is shared with other Organziations +* `is_subscribed` - Whether this Content Library is subscribed from an external published library +* `library_type` - The type of content library, can be either `PROVIDER` (Content Library that is scoped to a provider) or + `TENANT` (Content Library that is scoped to a tenant organization) +* `owner_org_id` - The reference to the Organization that the Content Library belongs to +* `version_number` - Version number of this Content library + +## Importing + +~> **Note:** The current implementation of Terraform import can only import resources into the state. It does not generate +configuration. However, an experimental feature in Terraform 1.5+ allows also code generation. +See [Importing resources][importing-resources] for more information. + +An existing Content Library can be [imported][docs-import] into this resource via supplying its name. +For example, using this structure, representing an existing Content Library that was **not** created using Terraform: + +```hcl +resource "vcd_tm_content_library" "cl" { + name = "My Already Existing Library" +} +``` + +You can import such Content Library into terraform state using this command + +``` +terraform import vcd_tm_content_library.cl "My Already Existing Library" +``` + +NOTE: the default separator (.) can be changed using Provider.import_separator or variable VCD_IMPORT_SEPARATOR + +After that, you can expand the configuration file and either update or delete the Content Library as needed. Running `terraform plan` +at this stage will show the difference between the minimal configuration file and the Content Library's stored properties. + +[importing-resources]:https://registry.terraform.io/providers/vmware/vcd/latest/docs/guides/importing_resources \ No newline at end of file diff --git a/website/vcd.erb b/website/vcd.erb index 7779d3ac6..e5c702f56 100644 --- a/website/vcd.erb +++ b/website/vcd.erb @@ -481,6 +481,12 @@ > vcd_nsxt_alb_virtual_service_http_sec_rules + > + vcd_tm_region_storage_policy + + > + vcd_tm_content_library + > @@ -846,6 +852,9 @@ > vcd_nsxt_alb_virtual_service_http_sec_rules + > + vcd_tm_content_library +