diff --git a/CHANGELOG.md b/CHANGELOG.md index ba91290e8..baa60fdf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ FEATURES: * **New Resource:** `vcd_edgegateway` creates and deletes edge gateways - [GH-262] * **New Resource:** `vcd_lb_server_pool` Load Balancer Server Pool - [GH-268] * **New Resource:** `vcd_lb_app_profile` Load Balancer Application profile - [GH-274] +* **New Resource:** `vcd_lb_app_rule` Load Balancer Application rule - [GH-278] * **New Resource:** `vcd_org_user` Organization User - [GH-279] * **New Data Source:** `vcd_lb_service_monitor` Load Balancer Service Monitor - [GH-256] * **New Data Source:** `vcd_lb_server_pool` Load Balancer Server Pool - [GH-268] * **New Data Source:** `vcd_lb_app_profile` Load Balancer Application profile - [GH-274] +* **New Data Source:** `vcd_lb_app_rule` Load Balancer Application rule - [GH-278] * **New build commands** `make test-env-init` and `make test-env-apply` can configure an empty vCD to run the test suite. See `TESTING.md` for details. * `resource/vcd_org_vdc` added Org VDC update and full state read - [GH-275] * `resource/vcd_org_vdc` added Org VDC metadata support - [GH-276] diff --git a/go.mod b/go.mod index 143c362ef..2d3eae24d 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.12 require ( github.com/hashicorp/terraform v0.12.0 - github.com/vmware/go-vcloud-director/v2 v2.3.0-alpha.9 + github.com/vmware/go-vcloud-director/v2 v2.3.0-beta.2 ) diff --git a/go.sum b/go.sum index 6f250566c..754d24b79 100644 --- a/go.sum +++ b/go.sum @@ -301,8 +301,8 @@ github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmware/go-vcloud-director/v2 v2.3.0-alpha.9 h1:ZZ9R6sS0NU6d4ZxsImujn4US1/osQtJoAp9SqhRNOzI= -github.com/vmware/go-vcloud-director/v2 v2.3.0-alpha.9/go.mod h1:HonlGxbjJ1NAibWh99eE4/S2l6ZOZ5KJzKK1rh2a9vc= +github.com/vmware/go-vcloud-director/v2 v2.3.0-beta.2 h1:3lB0heHFS+0SKcuWsQAx2YZXoxDeZXeHsEme5sYaTHw= +github.com/vmware/go-vcloud-director/v2 v2.3.0-beta.2/go.mod h1:HonlGxbjJ1NAibWh99eE4/S2l6ZOZ5KJzKK1rh2a9vc= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= diff --git a/vcd/datasource_vcd_lb_app_profile.go b/vcd/datasource_vcd_lb_app_profile.go index 6fbd8520a..63be4f0df 100644 --- a/vcd/datasource_vcd_lb_app_profile.go +++ b/vcd/datasource_vcd_lb_app_profile.go @@ -14,24 +14,24 @@ func datasourceVcdLBAppProfile() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD organization in which the App Profile is located", + Description: "vCD organization in which the LB Application Profile is located", }, "vdc": { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD virtual datacenter in which the App Profile is located", + Description: "vCD virtual datacenter in which the LB Application Profile is located", }, "edge_gateway": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Edge gateway name in which the App Profile is located", + Description: "Edge gateway name in which the LB Application Profile is located", }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, - Description: "App Profile name for lookup", + Description: "LB Application Profile name for lookup", }, "type": &schema.Schema{ Type: schema.TypeString, @@ -104,7 +104,7 @@ func datasourceVcdLBAppProfileRead(d *schema.ResourceData, meta interface{}) err readLBAppProfile, err := edgeGateway.ReadLBAppProfileByName(d.Get("name").(string)) if err != nil { - return fmt.Errorf("unable to find load balancer app profile with Name %s: %s", + return fmt.Errorf("unable to find load balancer application profile with Name %s: %s", d.Get("name").(string), err) } diff --git a/vcd/datasource_vcd_lb_app_rule.go b/vcd/datasource_vcd_lb_app_rule.go new file mode 100644 index 000000000..d1144a0d6 --- /dev/null +++ b/vcd/datasource_vcd_lb_app_rule.go @@ -0,0 +1,61 @@ +package vcd + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" +) + +func datasourceVcdLBAppRule() *schema.Resource { + return &schema.Resource{ + Read: datasourceVcdLBAppRuleRead, + Schema: map[string]*schema.Schema{ + "org": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "vCD organization in which the LB Application Rule is located", + }, + "vdc": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "vCD virtual datacenter in which the LB Application Rule is located", + }, + "edge_gateway": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Edge gateway name in which the LB Application Rule is located", + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Description: "LB Application Rule name for lookup", + }, + "script": { + Computed: true, + Type: schema.TypeString, + Description: "The script for the LB Application Rule. Each line will be " + + "terminated by newlines (\n)", + }, + }, + } +} + +func datasourceVcdLBAppRuleRead(d *schema.ResourceData, meta interface{}) error { + vcdClient := meta.(*VCDClient) + edgeGateway, err := vcdClient.GetEdgeGatewayFromResource(d, "edge_gateway") + if err != nil { + return fmt.Errorf(errorUnableToFindEdgeGateway, err) + } + + readLBAppRule, err := edgeGateway.ReadLBAppRuleByName(d.Get("name").(string)) + if err != nil { + return fmt.Errorf("unable to find load balancer application rule with Name %s: %s", + d.Get("name").(string), err) + } + + d.SetId(readLBAppRule.ID) + return setLBAppRuleData(d, readLBAppRule) +} diff --git a/vcd/datasource_vcd_lb_server_pool.go b/vcd/datasource_vcd_lb_server_pool.go index 4d1b591db..03d094627 100644 --- a/vcd/datasource_vcd_lb_server_pool.go +++ b/vcd/datasource_vcd_lb_server_pool.go @@ -15,19 +15,19 @@ func datasourceVcdLbServerPool() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD organization in which the Service Monitor is located", + Description: "vCD organization in which the LB Server Pool is located", }, "vdc": { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD virtual datacenter in which the Service Monitor is located", + Description: "vCD virtual datacenter in which the LB Server Pool is located", }, "edge_gateway": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Edge gateway name in which the Server Pool is located", + Description: "Edge gateway name in which the LB Server Pool is located", }, "name": &schema.Schema{ Type: schema.TypeString, diff --git a/vcd/datasource_vcd_lb_service_monitor.go b/vcd/datasource_vcd_lb_service_monitor.go index 6e19690f8..7bfb58b9b 100644 --- a/vcd/datasource_vcd_lb_service_monitor.go +++ b/vcd/datasource_vcd_lb_service_monitor.go @@ -14,22 +14,22 @@ func datasourceVcdLbServiceMonitor() *schema.Resource { "org": { Type: schema.TypeString, Optional: true, - Description: "vCD organization in which the Service Monitor is located", + Description: "vCD organization in which the LB Service Monitor is located", }, "vdc": { Type: schema.TypeString, Optional: true, - Description: "vCD virtual datacenter in which the Service Monitor is located", + Description: "vCD virtual datacenter in which the LB Service Monitor is located", }, "edge_gateway": &schema.Schema{ Type: schema.TypeString, Required: true, - Description: "Edge gateway name in which the Service Monitor is located", + Description: "Edge gateway name in which the LB Service Monitor is located", }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, - Description: "Service Monitor name", + Description: "LB Service Monitor name", }, "interval": &schema.Schema{ Type: schema.TypeInt, diff --git a/vcd/provider.go b/vcd/provider.go index 8f15d418c..876c54be2 100644 --- a/vcd/provider.go +++ b/vcd/provider.go @@ -107,12 +107,14 @@ func Provider() terraform.ResourceProvider { "vcd_lb_service_monitor": resourceVcdLbServiceMonitor(), // 2.4 "vcd_lb_server_pool": resourceVcdLBServerPool(), // 2.4 "vcd_lb_app_profile": resourceVcdLBAppProfile(), // 2.4 + "vcd_lb_app_rule": resourceVcdLBAppRule(), // 2.4 }, DataSourcesMap: map[string]*schema.Resource{ "vcd_lb_service_monitor": datasourceVcdLbServiceMonitor(), // 2.4 "vcd_lb_server_pool": datasourceVcdLbServerPool(), // 2.4 "vcd_lb_app_profile": datasourceVcdLBAppProfile(), // 2.4 + "vcd_lb_app_rule": datasourceVcdLBAppRule(), // 2.4 }, ConfigureFunc: providerConfigure, diff --git a/vcd/provider_test.go b/vcd/provider_test.go index 5a5fbba15..56d976403 100644 --- a/vcd/provider_test.go +++ b/vcd/provider_test.go @@ -1,4 +1,4 @@ -// +build api functional catalog vapp network extnetwork org query vm vdc gateway disk binary lb lbAppProfile lbServiceMonitor lbServerPool user ALL +// +build api functional catalog vapp network extnetwork org query vm vdc gateway disk binary lb lbAppProfile lbAppRule lbServiceMonitor lbServerPool user ALL package vcd diff --git a/vcd/resource_vcd_lb_app_profile.go b/vcd/resource_vcd_lb_app_profile.go index 8514c099d..2493d7895 100644 --- a/vcd/resource_vcd_lb_app_profile.go +++ b/vcd/resource_vcd_lb_app_profile.go @@ -23,25 +23,25 @@ func resourceVcdLBAppProfile() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD organization in which the Application Profile is located", + Description: "vCD organization in which the LB Application Profile is located", }, "vdc": { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD virtual datacenter in which the Application Profile is located", + Description: "vCD virtual datacenter in which the LB Application Profile is located", }, "edge_gateway": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Edge gateway name in which the Application Profile is located", + Description: "Edge gateway name in which the LB Application Profile is located", }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Unique Application Profile name", + Description: "Unique LB Application Profile name", }, "type": &schema.Schema{ Type: schema.TypeString, @@ -123,12 +123,12 @@ func resourceVcdLBAppProfileCreate(d *schema.ResourceData, meta interface{}) err LBProfile, err := getLBAppProfileType(d) if err != nil { - return fmt.Errorf("unable to expand load balancer app profile: %s", err) + return fmt.Errorf("unable to create load balancer application profile type: %s", err) } createdPool, err := edgeGateway.CreateLBAppProfile(LBProfile) if err != nil { - return fmt.Errorf("error creating new load balancer app profile: %s", err) + return fmt.Errorf("error creating new load balancer application profile: %s", err) } // We store the values once again because response include pool member IDs @@ -151,7 +151,7 @@ func resourceVcdLBAppProfileRead(d *schema.ResourceData, meta interface{}) error readLBProfile, err := edgeGateway.ReadLBAppProfileByID(d.Id()) if err != nil { d.SetId("") - return fmt.Errorf("unable to find load balancer app profile with ID %s: %s", d.Id(), err) + return fmt.Errorf("unable to find load balancer application profile with ID %s: %s", d.Id(), err) } return setLBAppProfileData(d, readLBProfile) @@ -169,12 +169,12 @@ func resourceVcdLBAppProfileUpdate(d *schema.ResourceData, meta interface{}) err updateLBProfileConfig, err := getLBAppProfileType(d) if err != nil { - return fmt.Errorf("could not expand load balancer app profile for update: %s", err) + return fmt.Errorf("unable to create load balancer application profile type for update: %s", err) } updatedLBProfile, err := edgeGateway.UpdateLBAppProfile(updateLBProfileConfig) if err != nil { - return fmt.Errorf("unable to update load balancer app profile with ID %s: %s", d.Id(), err) + return fmt.Errorf("unable to update load balancer application profile with ID %s: %s", d.Id(), err) } if err := setLBAppProfileData(d, updatedLBProfile); err != nil { @@ -196,7 +196,7 @@ func resourceVcdLBAppProfileDelete(d *schema.ResourceData, meta interface{}) err err = edgeGateway.DeleteLBAppProfileByID(d.Id()) if err != nil { - return fmt.Errorf("error deleting load balancer app profile: %s", err) + return fmt.Errorf("error deleting load balancer application profile: %s", err) } d.SetId("") @@ -224,7 +224,7 @@ func resourceVcdLBAppProfileImport(d *schema.ResourceData, meta interface{}) ([] readLBProfile, err := edgeGateway.ReadLBAppProfileByName(appProfileName) if err != nil { - return []*schema.ResourceData{}, fmt.Errorf("unable to find load balancer app profile with name %s: %s", + return []*schema.ResourceData{}, fmt.Errorf("unable to find load balancer application profile with name %s: %s", d.Id(), err) } diff --git a/vcd/resource_vcd_lb_app_profile_test.go b/vcd/resource_vcd_lb_app_profile_test.go index f2d8feee4..4ffb6e304 100644 --- a/vcd/resource_vcd_lb_app_profile_test.go +++ b/vcd/resource_vcd_lb_app_profile_test.go @@ -8,6 +8,8 @@ import ( "strings" "testing" + "github.com/vmware/go-vcloud-director/v2/govcd" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/vmware/go-vcloud-director/v2/types/v56" @@ -174,7 +176,7 @@ func testAccCheckVcdLBAppProfileDestroy(appProfileName string) resource.TestChec } monitor, err := edgeGateway.ReadLBAppProfile(&types.LBAppProfile{Name: appProfileName}) - if !strings.Contains(err.Error(), "could not find load balancer application profile") || + if !strings.Contains(err.Error(), govcd.ErrorEntityNotFound.Error()) || monitor != nil { return fmt.Errorf("load balancer application profile was not deleted: %s", err) } diff --git a/vcd/resource_vcd_lb_app_rule.go b/vcd/resource_vcd_lb_app_rule.go new file mode 100644 index 000000000..0afeb7512 --- /dev/null +++ b/vcd/resource_vcd_lb_app_rule.go @@ -0,0 +1,212 @@ +package vcd + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +func resourceVcdLBAppRule() *schema.Resource { + return &schema.Resource{ + Create: resourceVcdLBAppRuleCreate, + Read: resourceVcdLBAppRuleRead, + Update: resourceVcdLBAppRuleUpdate, + Delete: resourceVcdLBAppRuleDelete, + Importer: &schema.ResourceImporter{ + State: resourceVcdLBAppRuleImport, + }, + + Schema: map[string]*schema.Schema{ + "org": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "vCD organization in which the LB Application Rule is located", + }, + "vdc": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "vCD virtual datacenter in which the LB Application Rule is located", + }, + "edge_gateway": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Edge gateway name in which the LB Application Rule is located", + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Unique LB Application Rule name", + }, + "script": { + Type: schema.TypeString, + Required: true, + Description: "The script for the application rule. Note - you may find HEREDOC " + + "useful to pass multiline strings", + }, + }, + } +} + +func resourceVcdLBAppRuleCreate(d *schema.ResourceData, meta interface{}) error { + vcdClient := meta.(*VCDClient) + vcdClient.lockParentEdgeGtw(d) + defer vcdClient.unLockParentEdgeGtw(d) + + edgeGateway, err := vcdClient.GetEdgeGatewayFromResource(d, "edge_gateway") + if err != nil { + return fmt.Errorf(errorUnableToFindEdgeGateway, err) + } + + LBRule, err := getLBAppRuleType(d) + if err != nil { + return fmt.Errorf("unable to create load balancer application rule type: %s", err) + } + + createdPool, err := edgeGateway.CreateLBAppRule(LBRule) + if err != nil { + return fmt.Errorf("error creating new load balancer application rule: %s", err) + } + + err = setLBAppRuleData(d, createdPool) + if err != nil { + return err + } + d.SetId(createdPool.ID) + return nil +} + +func resourceVcdLBAppRuleRead(d *schema.ResourceData, meta interface{}) error { + vcdClient := meta.(*VCDClient) + + edgeGateway, err := vcdClient.GetEdgeGatewayFromResource(d, "edge_gateway") + if err != nil { + return fmt.Errorf(errorUnableToFindEdgeGateway, err) + } + + readLBRule, err := edgeGateway.ReadLBAppRuleByID(d.Id()) + if err != nil { + d.SetId("") + return fmt.Errorf("unable to find load balancer application rule with ID %s: %s", d.Id(), err) + } + + return setLBAppRuleData(d, readLBRule) +} + +func resourceVcdLBAppRuleUpdate(d *schema.ResourceData, meta interface{}) error { + vcdClient := meta.(*VCDClient) + vcdClient.lockParentEdgeGtw(d) + defer vcdClient.unLockParentEdgeGtw(d) + + edgeGateway, err := vcdClient.GetEdgeGatewayFromResource(d, "edge_gateway") + if err != nil { + return fmt.Errorf(errorUnableToFindEdgeGateway, err) + } + + updateLBRuleConfig, err := getLBAppRuleType(d) + if err != nil { + return fmt.Errorf("could not create load balancer application rule type for update: %s", err) + } + + updatedLBRule, err := edgeGateway.UpdateLBAppRule(updateLBRuleConfig) + if err != nil { + return fmt.Errorf("unable to update load balancer application rule with ID %s: %s", d.Id(), err) + } + + if err := setLBAppRuleData(d, updatedLBRule); err != nil { + return err + } + + return nil +} + +func resourceVcdLBAppRuleDelete(d *schema.ResourceData, meta interface{}) error { + vcdClient := meta.(*VCDClient) + vcdClient.lockParentEdgeGtw(d) + defer vcdClient.unLockParentEdgeGtw(d) + + edgeGateway, err := vcdClient.GetEdgeGatewayFromResource(d, "edge_gateway") + if err != nil { + return fmt.Errorf(errorUnableToFindEdgeGateway, err) + } + + err = edgeGateway.DeleteLBAppRuleByID(d.Id()) + if err != nil { + return fmt.Errorf("error deleting load balancer application rule: %s", err) + } + + d.SetId("") + return nil +} + +// resourceVcdLBAppRuleImport is responsible for importing the resource. +// The following steps happen as part of import +// 1. The user supplies `terraform import _resource_name_ _the_id_string_` command +// 2. `_the_id_string_` contains a dot formatted path to resource as in the example below +// 3. The functions splits the dot-formatted path and tries to lookup the object +// 4. If the lookup succeeds it sets the ID field for `_resource_name_` resource in statefile +// (the resource must be already defined in .tf config otherwise `terraform import` will complain) +// 5. `terraform refresh` is being implicitly launched. The Read method looks up all other fields +// based on the known ID of object. +// +// Example resource name (_resource_name_): vcd_lb_app_rule.my-test-app-rule +// Example import path (_the_id_string_): org.vdc.edge-gw.existing-app-rule +func resourceVcdLBAppRuleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + resourceURI := strings.Split(d.Id(), ".") + if len(resourceURI) != 4 { + return nil, fmt.Errorf("resource name must be specified in such way org.vdc.edge-gw.existing-app-rule") + } + orgName, vdcName, edgeName, appRuleName := resourceURI[0], resourceURI[1], resourceURI[2], resourceURI[3] + + vcdClient := meta.(*VCDClient) + edgeGateway, err := vcdClient.GetEdgeGateway(orgName, vdcName, edgeName) + if err != nil { + return nil, fmt.Errorf(errorUnableToFindEdgeGateway, err) + } + + readLBRule, err := edgeGateway.ReadLBAppRuleByName(appRuleName) + if err != nil { + return []*schema.ResourceData{}, fmt.Errorf("unable to find load balancer application rule with name %s: %s", + d.Id(), err) + } + + d.Set("org", orgName) + d.Set("vdc", vdcName) + d.Set("edge_gateway", edgeName) + d.Set("name", appRuleName) + + d.SetId(readLBRule.ID) + return []*schema.ResourceData{d}, nil +} + +// getLBAppRuleType converts Terraform resource data into types.LBAppRule type for API request. +func getLBAppRuleType(d *schema.ResourceData) (*types.LBAppRule, error) { + lbAppRule := &types.LBAppRule{ + Name: d.Get("name").(string), + Script: d.Get("script").(string), + } + + return lbAppRule, nil +} + +// setLBAppRuleData sets name and script API fields. API output returns a single string terminated +// by newline ("\n") for each line of script. If a user wants to set multiline script Terraform's +// HEREDOC syntax is helpful. +// This terraform configuration +// script = <<-EOT +// acl vmware_page url_beg / vmware redirect location https://www.vmware.com/ ifvmware_page +// acl other_page2 url_beg / other2 redirect location https://www.other2.com/ ifother_page2 +// acl hello payload(0,6) -m bin 48656c6c6f0a +// EOT +// is rendered as such API call +// +func setLBAppRuleData(d *schema.ResourceData, LBRule *types.LBAppRule) error { + d.Set("script", LBRule.Script) + d.Set("name", LBRule.Name) + return nil +} diff --git a/vcd/resource_vcd_lb_app_rule_test.go b/vcd/resource_vcd_lb_app_rule_test.go new file mode 100644 index 000000000..fdaafbd53 --- /dev/null +++ b/vcd/resource_vcd_lb_app_rule_test.go @@ -0,0 +1,194 @@ +// +build gateway lb lbAppRule ALL functional + +package vcd + +import ( + "fmt" + "html/template" + "regexp" + "strings" + "testing" + + "github.com/vmware/go-vcloud-director/v2/govcd" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccVcdLBAppRule(t *testing.T) { + // The Script parameter must be sent as multiline string separated by newline (\n) characters. + // Terraform has a native HEREDOC format for sending raw strings (with newline characters). + // This variable is established for easier test comparison and is wrapped into HEREDOC syntax + // in the `params` map using type `template.HTML` so that template engine does not + // escape HEREDOC syntax <<- characters. + MultiLineScript := `acl vmware_page url_beg / vmware redirect location https://www.vmware.com/ ifvmware_page +acl other_page2 url_beg / other2 redirect location https://www.other2.com/ ifother_page2 +` + + // String map to fill the template + var params = StringMap{ + "Org": testConfig.VCD.Org, + "Vdc": testConfig.VCD.Vdc, + "EdgeGateway": testConfig.Networking.EdgeGateway, + "AppRuleName": t.Name(), + "SingleLineScript": "acl vmware_page url_beg / vmware redirect location https://www.vmware.com/ ifvmware_page", + "MultilineScript": template.HTML(`<<-EOT +` + MultiLineScript + `EOT`), + "MultilineFailScript": template.HTML(`<<-EOT + acl vmware_page url_beg / vmware redirect location https://www.vmware.com/ ifvmware_page + acl other_page2 url_beg / other2 redirect location https://www.other2.com/ ifother_page2 + acl en req.fhdr(accept-language),language(es;fr;en) -m str en + use_backend english if en + EOT`), + "Tags": "lb lbAppRule", + "SkipTest": "", + } + + configText := templateFill(testAccVcdLBAppRule_OneLine, params) + debugPrintf("#[DEBUG] CONFIGURATION for step 0: %s", configText) + + params["FuncName"] = t.Name() + "-step1" + configText1 := templateFill(testAccVcdLBAppRule_MultiLine, params) + debugPrintf("#[DEBUG] CONFIGURATION for step 1: %s", configText1) + + params["FuncName"] = t.Name() + "-step3" + // This test must fail with invalid rule script so we avoid running it in `make test-binary` + params["SkipTest"] = "# skip-test: it will fail on purpose" + configText3 := templateFill(testAccVcdLBAppRule_FailMultiLine, params) + debugPrintf("#[DEBUG] CONFIGURATION for step 3: %s", configText3) + + if vcdShortTest { + t.Skip(acceptanceTestsSkipped) + return + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + CheckDestroy: testAccCheckVcdLBAppRuleDestroy(params["AppRuleName"].(string)), + Steps: []resource.TestStep{ + resource.TestStep{ // Single Line Script + Config: configText, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestMatchResourceAttr("vcd_lb_app_rule.test", "id", regexp.MustCompile(`^applicationRule-\d*$`)), + resource.TestCheckResourceAttr("vcd_lb_app_rule.test", "name", params["AppRuleName"].(string)), + resource.TestCheckResourceAttr("vcd_lb_app_rule.test", "script", params["SingleLineScript"].(string)), + + // Data source testing - it must expose all fields which resource has + resource.TestMatchResourceAttr("data.vcd_lb_app_rule.test", "id", regexp.MustCompile(`^applicationRule-\d*$`)), + resource.TestCheckResourceAttr("data.vcd_lb_app_rule.test", "name", params["AppRuleName"].(string)), + resource.TestCheckResourceAttr("data.vcd_lb_app_rule.test", "script", params["SingleLineScript"].(string)), + ), + }, + + resource.TestStep{ // Multi Line Script + Config: configText1, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestMatchResourceAttr("vcd_lb_app_rule.test", "id", regexp.MustCompile(`^applicationRule-\d*$`)), + resource.TestCheckResourceAttr("vcd_lb_app_rule.test", "name", params["AppRuleName"].(string)), + resource.TestCheckResourceAttr("vcd_lb_app_rule.test", "script", MultiLineScript), + + // Data source testing - it must expose all fields which resource has + resource.TestMatchResourceAttr("data.vcd_lb_app_rule.test", "id", regexp.MustCompile(`^applicationRule-\d*$`)), + resource.TestCheckResourceAttr("data.vcd_lb_app_rule.test", "name", params["AppRuleName"].(string)), + resource.TestCheckResourceAttr("data.vcd_lb_app_rule.test", "script", MultiLineScript), + ), + }, + + resource.TestStep{ + ResourceName: "vcd_lb_app_rule.imported", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: importStateIdByOrgVdcEdge(testConfig, params["AppRuleName"].(string)), + }, + + resource.TestStep{ // Multi Line Script with invalid rule + Config: configText3, + ExpectError: regexp.MustCompile(`.*vShield Edge .* Not found pool name .* in rules.*`), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckNoResourceAttr("vcd_lb_app_rule.test", "id"), + resource.TestCheckNoResourceAttr("vcd_lb_app_rule.test", "name"), + + // Data source testing - it must expose all fields which resource has + resource.TestCheckNoResourceAttr("data.vcd_lb_app_rule.test", "id"), + resource.TestCheckNoResourceAttr("data.vcd_lb_app_rule.test", "name"), + ), + }, + }, + }) +} + +func testAccCheckVcdLBAppRuleDestroy(appRuleName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*VCDClient) + + edgeGateway, err := conn.GetEdgeGateway(testConfig.VCD.Org, testConfig.VCD.Vdc, testConfig.Networking.EdgeGateway) + if err != nil { + return fmt.Errorf(errorUnableToFindEdgeGateway, err) + } + + monitor, err := edgeGateway.ReadLBAppRuleByName(appRuleName) + if !strings.Contains(err.Error(), govcd.ErrorEntityNotFound.Error()) || + monitor != nil { + return fmt.Errorf("load balancer application rule was not deleted: %s", err) + } + return nil + } +} + +const testAccVcdLBAppRule_OneLine = ` +resource "vcd_lb_app_rule" "test" { + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + + name = "{{.AppRuleName}}" + script = "{{.SingleLineScript}}" +} + +data "vcd_lb_app_rule" "test" { + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + name = "${vcd_lb_app_rule.test.name}" +} +` + +const testAccVcdLBAppRule_MultiLine = ` +resource "vcd_lb_app_rule" "test" { + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + + name = "{{.AppRuleName}}" + script = {{.MultilineScript}} +} + +data "vcd_lb_app_rule" "test" { + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + name = "${vcd_lb_app_rule.test.name}" +} +` + +const testAccVcdLBAppRule_FailMultiLine = ` +{{.SkipTest}} + +resource "vcd_lb_app_rule" "test" { + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + + name = "{{.AppRuleName}}" + script = {{.MultilineFailScript}} +} + +data "vcd_lb_app_rule" "test" { + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + name = "${vcd_lb_app_rule.test.name}" +} + +` diff --git a/vcd/resource_vcd_lb_server_pool.go b/vcd/resource_vcd_lb_server_pool.go index 964c2c104..e4ef14ee1 100644 --- a/vcd/resource_vcd_lb_server_pool.go +++ b/vcd/resource_vcd_lb_server_pool.go @@ -23,25 +23,25 @@ func resourceVcdLBServerPool() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD organization in which the Service Monitor is located", + Description: "vCD organization in which the LB Server Pool is located", }, "vdc": { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD virtual datacenter in which the Service Monitor is located", + Description: "vCD virtual datacenter in which the LB Server Pool is located", }, "edge_gateway": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Edge gateway name in which the Server Pool is located", + Description: "Edge gateway name in which the LB Server Pool is located", }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Unique Server Pool name", + Description: "Unique LB Server Pool name", }, "description": &schema.Schema{ Type: schema.TypeString, @@ -151,7 +151,7 @@ func resourceVcdLBServerPoolCreate(d *schema.ResourceData, meta interface{}) err LBPool, err := getLBPoolType(d) if err != nil { - return fmt.Errorf("unable to expand load balancer server pool: %s", err) + return fmt.Errorf("unable to create load balancer server pool type: %s", err) } createdPool, err := edgeGateway.CreateLBServerPool(LBPool) @@ -196,7 +196,7 @@ func resourceVcdLBServerPoolUpdate(d *schema.ResourceData, meta interface{}) err updateLBPoolConfig, err := getLBPoolType(d) if err != nil { - return fmt.Errorf("could not expand load balancer server pool for update: %s", err) + return fmt.Errorf("could not create load balancer server pool type for update: %s", err) } updatedLBPool, err := edgeGateway.UpdateLBServerPool(updateLBPoolConfig) diff --git a/vcd/resource_vcd_lb_server_pool_test.go b/vcd/resource_vcd_lb_server_pool_test.go index fb5dcb983..eff237022 100644 --- a/vcd/resource_vcd_lb_server_pool_test.go +++ b/vcd/resource_vcd_lb_server_pool_test.go @@ -8,9 +8,11 @@ import ( "strings" "testing" + "github.com/vmware/go-vcloud-director/v2/govcd" + "github.com/vmware/go-vcloud-director/v2/types/v56" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/vmware/go-vcloud-director/v2/types/v56" ) func TestAccVcdLbServerPool(t *testing.T) { @@ -225,7 +227,7 @@ func testAccCheckVcdLbServerPoolDestroy(serverPoolName string) resource.TestChec } monitor, err := edgeGateway.ReadLBServerPool(&types.LBPool{Name: serverPoolName}) - if !strings.Contains(err.Error(), "could not find load balancer server pool") || monitor != nil { + if !strings.Contains(err.Error(), govcd.ErrorEntityNotFound.Error()) || monitor != nil { return fmt.Errorf("load balancer server pool was not deleted: %s", err) } return nil diff --git a/vcd/resource_vcd_lb_service_monitor.go b/vcd/resource_vcd_lb_service_monitor.go index 92ecfb04a..5c83c4c40 100644 --- a/vcd/resource_vcd_lb_service_monitor.go +++ b/vcd/resource_vcd_lb_service_monitor.go @@ -23,25 +23,25 @@ func resourceVcdLbServiceMonitor() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD organization in which the Service Monitor is located", + Description: "vCD organization in which the LB Service Monitor is located", }, "vdc": { Type: schema.TypeString, Optional: true, ForceNew: true, - Description: "vCD virtual datacenter in which the Service Monitor is located", + Description: "vCD virtual datacenter in which the LB Service Monitor is located", }, "edge_gateway": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Edge gateway name in which the Service Monitor is located", + Description: "Edge gateway name in which the LB Service Monitor is located", }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, - Description: "Unique Service Monitor name", + Description: "Unique LB Service Monitor name", }, "interval": &schema.Schema{ Type: schema.TypeInt, @@ -141,7 +141,7 @@ func resourceVcdLbServiceMonitorCreate(d *schema.ResourceData, meta interface{}) lbMonitor, err := getLBMonitorType(d) if err != nil { - return fmt.Errorf("unable to expand load balancer service monitor: %s", err) + return fmt.Errorf("unable to create load balancer service monitor type: %s", err) } createdMonitor, err := edgeGateway.CreateLBServiceMonitor(lbMonitor) @@ -182,7 +182,7 @@ func resourceVcdLbServiceMonitorUpdate(d *schema.ResourceData, meta interface{}) updateLBMonitorConfig, err := getLBMonitorType(d) if err != nil { - return fmt.Errorf("could not expand monitor for update: %s", err) + return fmt.Errorf("could not create service monitor type for update: %s", err) } updatedLBMonitor, err := edgeGateway.UpdateLBServiceMonitor(updateLBMonitorConfig) diff --git a/vcd/resource_vcd_lb_service_monitor_test.go b/vcd/resource_vcd_lb_service_monitor_test.go index a561768ef..191d32473 100644 --- a/vcd/resource_vcd_lb_service_monitor_test.go +++ b/vcd/resource_vcd_lb_service_monitor_test.go @@ -9,11 +9,11 @@ import ( "strings" "testing" + "github.com/vmware/go-vcloud-director/v2/govcd" "github.com/vmware/go-vcloud-director/v2/types/v56" - "github.com/hashicorp/terraform/terraform" - "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" ) func TestAccVcdLbServiceMonitor(t *testing.T) { @@ -104,7 +104,7 @@ func testAccCheckVcdLbServiceMonitorDestroy(serviceMonitorName string) resource. } monitor, err := edgeGateway.ReadLBServiceMonitor(&types.LBMonitor{Name: serviceMonitorName}) - if !strings.Contains(err.Error(), "could not find load balancer") || monitor != nil { + if !strings.Contains(err.Error(), govcd.ErrorEntityNotFound.Error()) || monitor != nil { return fmt.Errorf("load balancer service monitor was not deleted: %s", err) } return nil diff --git a/vcd/test-templates/load-balancer1.tf b/vcd/test-templates/load-balancer1.tf index af7c99f10..e633c5903 100644 --- a/vcd/test-templates/load-balancer1.tf +++ b/vcd/test-templates/load-balancer1.tf @@ -131,3 +131,23 @@ data "vcd_lb_app_profile" "test" { edge_gateway = "{{.EdgeGateway}}" name = "${vcd_lb_app_profile.test[count.index].name}" } + +resource "vcd_lb_app_rule" "test" { + count = "${var.component_count}" + + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + + name = "test-app-profile-${count.index}" + script = ["acl hello payload(0,6) -m bin 48656c6c6f0a"] +} + +data "vcd_lb_app_rule" "test" { + count = "${var.component_count}" + + org = "{{.Org}}" + vdc = "{{.Vdc}}" + edge_gateway = "{{.EdgeGateway}}" + name = "${vcd_lb_app_rule.test[count.index].name}" +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go index ace1efc3b..beee5a493 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/edgegateway.go @@ -10,6 +10,7 @@ import ( "fmt" "net/http" "net/url" + "path" "regexp" "strconv" "strings" @@ -39,6 +40,19 @@ func NewEdgeGateway(cli *Client) *EdgeGateway { } } +// Struct which covers NAT rule fields +type NatRule struct { + natType string + networkHref string + externalIP string + externalPort string + internalIP string + internalPort string + protocol string + icmpSubType string + description string +} + func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []interface{}) (Task, error) { newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration util.Logger.Printf("[DEBUG] EDGE GATEWAY: %#v", newEdgeConfig) @@ -131,11 +145,85 @@ func (eGW *EdgeGateway) AddDhcpPool(network *types.OrgVDCNetwork, dhcppool []int } +// Temporary fix for existing Remove functions as it doesn't support network interfaces +// TODO: remove this function when ruleId is available +func (eGW *EdgeGateway) RemoveNATMappingRule(networkHref, natType, externalIP, internalIP, port string) (Task, error) { + return eGW.RemoveNATPortRule(networkHref, natType, externalIP, port, internalIP, port) +} + +// Temporary fix for existing Remove functions as it doesn't support network interfaces +// TODO: remove this function when ruleId is available +func (eGW *EdgeGateway) RemoveNATPortRule(networkHref, natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { + newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration + + // Take care of the NAT service + newNatService := &types.NatService{} + + newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled + newNatService.NatType = newEdgeConfig.NatService.NatType + newNatService.Policy = newEdgeConfig.NatService.Policy + newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP + + for _, natRule := range newEdgeConfig.NatService.NatRule { + + // Kludgy IF to avoid deleting DNAT rules not created by us. + // If matches, let's skip it and continue the loop + interfaceId, err := extractObjectIDfromPath(natRule.GatewayNatRule.Interface.HREF) + networkHrefId, err := extractObjectIDfromPath(networkHref) + if err != nil { + return Task{}, nil + } + if natRule.RuleType == natType && + natRule.GatewayNatRule.OriginalIP == externalIP && + natRule.GatewayNatRule.OriginalPort == externalPort && + interfaceId == networkHrefId { + util.Logger.Printf("[DEBUG] REMOVING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) + continue + } + util.Logger.Printf("[DEBUG] KEEPING %s Rule: %#v", natRule.RuleType, natRule.GatewayNatRule) + newNatService.NatRule = append(newNatService.NatRule, natRule) + } + + newEdgeConfig.NatService = newNatService + + newRules := &types.EdgeGatewayServiceConfiguration{ + Xmlns: types.XMLNamespaceVCloud, + NatService: newNatService, + } + + apiEndpoint, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) + apiEndpoint.Path += "/action/configureServices" + + // Return the task + return eGW.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, + "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) + +} + +// Temporary fix to support parsing network Id from HREF +// TODO: remove this function when ruleId is available +func extractObjectIDfromPath(locationPath string) (string, error) { + if locationPath == "" { + return "", fmt.Errorf("unable to get ID from empty path") + } + + cleanPath := path.Clean(locationPath) // Removes trailing slash if there is one + splitPath := strings.Split(cleanPath, "/") + + if len(splitPath) < 2 { + return "", fmt.Errorf("path does not contain url path: %s", splitPath) + } + + objectID := splitPath[len(splitPath)-1] + + return objectID, nil +} + func (eGW *EdgeGateway) RemoveNATMapping(natType, externalIP, internalIP, port string) (Task, error) { return eGW.RemoveNATPortMapping(natType, externalIP, port, internalIP, port) } -func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort string, internalIP, internalPort string) (Task, error) { +func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort, internalIP, internalPort string) (Task, error) { // Find uplink interface var uplink types.Reference for _, gi := range eGW.EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface { @@ -157,8 +245,6 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort s for _, natRule := range newEdgeConfig.NatService.NatRule { - // Kludgy IF to avoid deleting DNAT rules not created by us. - // If matches, let's skip it and continue the loop if natRule.RuleType == natType && natRule.GatewayNatRule.OriginalIP == externalIP && natRule.GatewayNatRule.OriginalPort == externalPort && @@ -186,6 +272,130 @@ func (eGW *EdgeGateway) RemoveNATPortMapping(natType, externalIP, externalPort s } +// AddDNATRule creates DNAT rule and return refreshed existing NAT rules array or error +// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and +// AddNATMapping assigns rule to first external network +func (eGW *EdgeGateway) AddDNATRule(ruleDetails NatRule) ([]*types.NatRule, error) { + + ruleDetails.natType = "DNAT" + task, err := eGW.AddNATRuleAsync(ruleDetails) + if err != nil { + return nil, fmt.Errorf("error creating DNAT rule: %#v", err) + } + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) + } + + err = eGW.Refresh() + if err != nil { + return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) + } + + return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil +} + +// AddSNATRule creates SNAT rule and returns refreshed existing NAT rules array or error +// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and +// AddNATMapping assigns rule to first external network +func (eGW *EdgeGateway) AddSNATRule(networkHref, externalIP, internalIP, description string) ([]*types.NatRule, error) { + + task, err := eGW.AddNATRuleAsync(NatRule{networkHref: networkHref, natType: "SNAT", externalIP: externalIP, + externalPort: "any", internalIP: internalIP, internalPort: "any", + icmpSubType: "", protocol: "any", description: description}) + if err != nil { + return nil, fmt.Errorf("error creating SNAT rule: %#v", err) + } + err = task.WaitTaskCompletion() + if err != nil { + return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) + } + + err = eGW.Refresh() + if err != nil { + return nil, fmt.Errorf("error refreshing edge gateway: %#v", err) + } + + return eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration.NatService.NatRule, nil +} + +// AddNATRuleAsync creates NAT rule and return task or err +// Allows to assign specific network Org VDC or external. Old function AddNATPortMapping and +// AddNATMapping assigns rule to first external network +func (eGW *EdgeGateway) AddNATRuleAsync(ruleDetails NatRule) (Task, error) { + if !isValidProtocol(ruleDetails.protocol) { + return Task{}, fmt.Errorf("provided protocol is not one of TCP, UDP, TCPUDP, ICMP, ANY") + } + + if strings.ToUpper(ruleDetails.protocol) == "ICMP" && !isValidIcmpSubType(ruleDetails.icmpSubType) { + return Task{}, fmt.Errorf("provided icmp sub type is not correct") + } + + newEdgeConfig := eGW.EdgeGateway.Configuration.EdgeGatewayServiceConfiguration + + // Take care of the NAT service + newNatService := &types.NatService{} + + if newEdgeConfig.NatService == nil { + newNatService.IsEnabled = true + } else { + newNatService.IsEnabled = newEdgeConfig.NatService.IsEnabled + newNatService.NatType = newEdgeConfig.NatService.NatType + newNatService.Policy = newEdgeConfig.NatService.Policy + newNatService.ExternalIP = newEdgeConfig.NatService.ExternalIP + + for _, natRule := range newEdgeConfig.NatService.NatRule { + + // Kludgy IF to avoid deleting DNAT rules not created by us. + // If matches, let's skip it and continue the loop + if natRule.RuleType == ruleDetails.natType && + natRule.GatewayNatRule.OriginalIP == ruleDetails.externalIP && + natRule.GatewayNatRule.OriginalPort == ruleDetails.externalPort && + natRule.GatewayNatRule.TranslatedIP == ruleDetails.internalIP && + natRule.GatewayNatRule.TranslatedPort == ruleDetails.internalPort && + natRule.GatewayNatRule.Interface.HREF == ruleDetails.networkHref { + continue + } + + newNatService.NatRule = append(newNatService.NatRule, natRule) + } + } + + //add rule + natRule := &types.NatRule{ + RuleType: ruleDetails.natType, + IsEnabled: true, + Description: ruleDetails.description, + GatewayNatRule: &types.GatewayNatRule{ + Interface: &types.Reference{ + HREF: ruleDetails.networkHref, + }, + OriginalIP: ruleDetails.externalIP, + OriginalPort: ruleDetails.externalPort, + TranslatedIP: ruleDetails.internalIP, + TranslatedPort: ruleDetails.internalPort, + Protocol: ruleDetails.protocol, + IcmpSubType: ruleDetails.icmpSubType, + }, + } + newNatService.NatRule = append(newNatService.NatRule, natRule) + + newEdgeConfig.NatService = newNatService + + newRules := &types.EdgeGatewayServiceConfiguration{ + Xmlns: types.XMLNamespaceVCloud, + NatService: newNatService, + } + + apiEndpoint, _ := url.ParseRequestURI(eGW.EdgeGateway.HREF) + apiEndpoint.Path += "/action/configureServices" + + // Return the task + return eGW.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPost, + "application/vnd.vmware.admin.edgeGatewayServiceConfiguration+xml", "error reconfiguring Edge Gateway: %s", newRules) +} + +// Deprecated: Use eGW.AddSNATRule() or eGW.AddDNATRule() func (eGW *EdgeGateway) AddNATRule(network *types.OrgVDCNetwork, natType, externalIP, internalIP string) (Task, error) { return eGW.AddNATPortMappingWithUplink(network, natType, externalIP, "any", internalIP, "any", "any", "") } @@ -250,6 +460,7 @@ func isValidIcmpSubType(protocol string) bool { return false } +// Deprecated: Use eGW.AddNATFirewallRule() func (eGW *EdgeGateway) AddNATPortMappingWithUplink(network *types.OrgVDCNetwork, natType, externalIP, externalPort, internalIP, internalPort, protocol, icmpSubType string) (Task, error) { // if a network is provided take it, otherwise find first uplink on the edge gateway var uplinkRef string diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lb.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lb.go index 3f36db390..a27a631cf 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lb.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lb.go @@ -10,14 +10,14 @@ import ( "strings" ) -// extractNSXObjectIDfromPath parses proxied NSX API response Location header and +// extractNSXObjectIDFromPath parses proxied NSX API response Location header and // extracts ID for newly created object from it. The object ID is the last element in path. // It expects the path to have at least one "/" to be a valid path and cleans up the trailing slash // if there is one. // // Sample locationPath from API: /network/edges/edge-3/loadbalancer/config/monitors/monitor-5 // Expected ID to be returned: monitor-5 -func extractNSXObjectIDfromPath(locationPath string) (string, error) { +func extractNSXObjectIDFromPath(locationPath string) (string, error) { if locationPath == "" { return "", fmt.Errorf("unable to get ID from empty path") } diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbappprofile.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbappprofile.go index 8b703f383..3d401aa4a 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbappprofile.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbappprofile.go @@ -31,7 +31,7 @@ func (eGW *EdgeGateway) CreateLBAppProfile(lbAppProfileConfig *types.LBAppProfil // Location header should look similar to: // [/network/edges/edge-3/loadbalancer/config/applicationprofiles/applicationProfile-4] - lbAppProfileID, err := extractNSXObjectIDfromPath(resp.Header.Get("Location")) + lbAppProfileID, err := extractNSXObjectIDFromPath(resp.Header.Get("Location")) if err != nil { return nil, err } @@ -88,8 +88,7 @@ func (eGW *EdgeGateway) ReadLBAppProfile(lbAppProfileConfig *types.LBAppProfile) } } - return nil, fmt.Errorf("could not find load balancer application profile (name: %s, ID: %s)", - lbAppProfileConfig.Name, lbAppProfileConfig.ID) + return nil, ErrorEntityNotFound } // ReadLBAppProfileByID wraps ReadLBAppProfile and needs only an ID for lookup diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbapprule.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbapprule.go new file mode 100644 index 000000000..ca0cafae2 --- /dev/null +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbapprule.go @@ -0,0 +1,216 @@ +/* + * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "net/http" + + "github.com/vmware/go-vcloud-director/v2/types/v56" +) + +// CreateLBAppRule creates a load balancer application rule based on mandatory fields. It is a +// synchronous operation. It returns created object with all fields (including ID) populated or an error. +func (eGW *EdgeGateway) CreateLBAppRule(lbAppRuleConfig *types.LBAppRule) (*types.LBAppRule, error) { + if err := validateCreateLBAppRule(lbAppRuleConfig); err != nil { + return nil, err + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + // We expect to get http.StatusCreated or if not an error of type types.NSXError + resp, err := eGW.client.ExecuteRequestWithCustomError(httpPath, http.MethodPost, types.AnyXMLMime, + "error creating load balancer application rule: %s", lbAppRuleConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + // Location header should look similar to: + // [/network/edges/edge-3/loadbalancer/config/applicationrules/applicationRule-4] + lbAppRuleId, err := extractNSXObjectIDFromPath(resp.Header.Get("Location")) + if err != nil { + return nil, err + } + + readAppRule, err := eGW.ReadLBAppRule(&types.LBAppRule{ID: lbAppRuleId}) + if err != nil { + return nil, fmt.Errorf("unable to retrieve application rule with ID (%s) after creation: %s", + readAppRule.ID, err) + } + return readAppRule, nil +} + +// ReadLBAppRule is able to find the types.LBAppRule type by Name and/or ID. +// If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found +// name do not match. +func (eGW *EdgeGateway) ReadLBAppRule(lbAppRuleConfig *types.LBAppRule) (*types.LBAppRule, error) { + if err := validateReadLBAppRule(lbAppRuleConfig); err != nil { + return nil, err + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Anonymous struct to unwrap response + lbAppRuleResponse := &struct { + LbAppRules []*types.LBAppRule `xml:"applicationRule"` + }{} + + // This query returns all application rules as the API does not have filtering options + _, err = eGW.client.ExecuteRequest(httpPath, http.MethodGet, types.AnyXMLMime, + "unable to read load balancer application rule: %s", nil, lbAppRuleResponse) + if err != nil { + return nil, err + } + + // Search for application rule by ID or by Name + for _, rule := range lbAppRuleResponse.LbAppRules { + // If ID was specified for lookup - look for the same ID + if lbAppRuleConfig.ID != "" && rule.ID == lbAppRuleConfig.ID { + return rule, nil + } + + // If Name was specified for lookup - look for the same Name + if lbAppRuleConfig.Name != "" && rule.Name == lbAppRuleConfig.Name { + // We found it by name. Let's verify if search ID was specified and it matches the lookup object + if lbAppRuleConfig.ID != "" && rule.ID != lbAppRuleConfig.ID { + return nil, fmt.Errorf("load balancer application rule was found by name (%s)"+ + ", but its ID (%s) does not match specified ID (%s)", + rule.Name, rule.ID, lbAppRuleConfig.ID) + } + return rule, nil + } + } + + return nil, ErrorEntityNotFound +} + +// ReadLBAppRuleById wraps ReadLBAppRule and needs only an ID for lookup +func (eGW *EdgeGateway) ReadLBAppRuleByID(id string) (*types.LBAppRule, error) { + return eGW.ReadLBAppRule(&types.LBAppRule{ID: id}) +} + +// ReadLBAppRuleByName wraps ReadLBAppRule and needs only a Name for lookup +func (eGW *EdgeGateway) ReadLBAppRuleByName(name string) (*types.LBAppRule, error) { + return eGW.ReadLBAppRule(&types.LBAppRule{Name: name}) +} + +// UpdateLBAppRule updates types.LBAppRule with all fields. At least name or ID must be specified. +// If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found +// name do not match. +func (eGW *EdgeGateway) UpdateLBAppRule(lbAppRuleConfig *types.LBAppRule) (*types.LBAppRule, error) { + err := validateUpdateLBAppRule(lbAppRuleConfig) + if err != nil { + return nil, err + } + + lbAppRuleConfig.ID, err = eGW.getLBAppRuleIDByNameID(lbAppRuleConfig.Name, lbAppRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("cannot update load balancer application rule: %s", err) + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath + lbAppRuleConfig.ID) + if err != nil { + return nil, fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + // Result should be 204, if not we expect an error of type types.NSXError + _, err = eGW.client.ExecuteRequestWithCustomError(httpPath, http.MethodPut, types.AnyXMLMime, + "error while updating load balancer application rule : %s", lbAppRuleConfig, &types.NSXError{}) + if err != nil { + return nil, err + } + + readAppRule, err := eGW.ReadLBAppRule(&types.LBAppRule{ID: lbAppRuleConfig.ID}) + if err != nil { + return nil, fmt.Errorf("unable to retrieve application rule with ID (%s) after update: %s", + readAppRule.ID, err) + } + return readAppRule, nil +} + +// DeleteLBAppRule is able to delete the types.LBAppRule type by Name and/or ID. +// If both - Name and ID are specified it performs a lookup by ID and returns an error if the specified name and found +// name do not match. +func (eGW *EdgeGateway) DeleteLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + err := validateDeleteLBAppRule(lbAppRuleConfig) + if err != nil { + return err + } + + lbAppRuleConfig.ID, err = eGW.getLBAppRuleIDByNameID(lbAppRuleConfig.Name, lbAppRuleConfig.ID) + if err != nil { + return fmt.Errorf("cannot update load balancer application rule: %s", err) + } + + httpPath, err := eGW.buildProxiedEdgeEndpointURL(types.LBAppRulePath + lbAppRuleConfig.ID) + if err != nil { + return fmt.Errorf("could not get Edge Gateway API endpoint: %s", err) + } + + return eGW.client.ExecuteRequestWithoutResponse(httpPath, http.MethodDelete, "application/xml", + "unable to delete application rule: %s", nil) +} + +// DeleteLBAppRuleById wraps DeleteLBAppRule and requires only ID for deletion +func (eGW *EdgeGateway) DeleteLBAppRuleByID(id string) error { + return eGW.DeleteLBAppRule(&types.LBAppRule{ID: id}) +} + +// DeleteLBAppRuleByName wraps DeleteLBAppRule and requires only Name for deletion +func (eGW *EdgeGateway) DeleteLBAppRuleByName(name string) error { + return eGW.DeleteLBAppRule(&types.LBAppRule{Name: name}) +} + +func validateCreateLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + if lbAppRuleConfig.Name == "" { + return fmt.Errorf("load balancer application rule Name cannot be empty") + } + + return nil +} + +func validateReadLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + if lbAppRuleConfig.ID == "" && lbAppRuleConfig.Name == "" { + return fmt.Errorf("to read load balancer application rule at least one of `ID`, `Name`" + + " fields must be specified") + } + + return nil +} + +func validateUpdateLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + // Update and create have the same requirements for now + return validateCreateLBAppRule(lbAppRuleConfig) +} + +func validateDeleteLBAppRule(lbAppRuleConfig *types.LBAppRule) error { + // Read and delete have the same requirements for now + return validateReadLBAppRule(lbAppRuleConfig) +} + +// getLBAppRuleIDByNameID checks if at least name or ID is set and returns the ID. +// If the ID is specified - it passes through the ID. If only name was specified +// it will lookup the object by name and return the ID. +func (eGW *EdgeGateway) getLBAppRuleIDByNameID(name, id string) (string, error) { + if name == "" && id == "" { + return "", fmt.Errorf("at least Name or ID must be specific to find load balancer "+ + "application rule got name (%s) ID (%s)", name, id) + } + if id != "" { + return id, nil + } + + // if only name was specified, ID must be found, because only ID can be used in request path + readlbAppRule, err := eGW.ReadLBAppRuleByName(name) + if err != nil { + return "", fmt.Errorf("unable to find load balancer application rule by name: %s", err) + } + return readlbAppRule.ID, nil +} diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go index 471165c26..409868b02 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbserverpool.go @@ -32,7 +32,7 @@ func (eGW *EdgeGateway) CreateLBServerPool(lbPoolConfig *types.LBPool) (*types.L // Location header should look similar to: // Location: [/network/edges/edge-3/loadbalancer/config/pools/pool-7] - lbPoolID, err := extractNSXObjectIDfromPath(resp.Header.Get("Location")) + lbPoolID, err := extractNSXObjectIDFromPath(resp.Header.Get("Location")) if err != nil { return nil, err } @@ -87,8 +87,7 @@ func (eGW *EdgeGateway) ReadLBServerPool(lbPoolConfig *types.LBPool) (*types.LBP } } - return nil, fmt.Errorf("could not find load balancer server pool (name: %s, ID: %s)", - lbPoolConfig.Name, lbPoolConfig.ID) + return nil, ErrorEntityNotFound } // ReadLBServerPoolByName wraps ReadLBServerPool and needs only an ID for lookup diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go index 4084239d1..21e59c1bb 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/govcd/lbservicemonitor.go @@ -35,7 +35,7 @@ func (eGW *EdgeGateway) CreateLBServiceMonitor(lbMonitorConfig *types.LBMonitor) // Location header should look similar to: // Location: [/network/edges/edge-3/loadbalancer/config/monitors/monitor-5] - lbMonitorID, err := extractNSXObjectIDfromPath(resp.Header.Get("Location")) + lbMonitorID, err := extractNSXObjectIDFromPath(resp.Header.Get("Location")) if err != nil { return nil, err } @@ -89,8 +89,7 @@ func (eGW *EdgeGateway) ReadLBServiceMonitor(lbMonitorConfig *types.LBMonitor) ( } } - return nil, fmt.Errorf("could not find load balancer service monitor (name: %s, ID: %s)", - lbMonitorConfig.Name, lbMonitorConfig.ID) + return nil, ErrorEntityNotFound } // ReadLBServiceMonitorByID wraps ReadLBServiceMonitor and needs only an ID for lookup diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go index 7eec812c0..558fe0ae0 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/constants.go @@ -154,4 +154,5 @@ const ( LBMonitorPath = "/loadbalancer/config/monitors/" LBServerPoolPath = "/loadbalancer/config/pools/" LBAppProfilePath = "/loadbalancer/config/applicationprofiles/" + LBAppRulePath = "/loadbalancer/config/applicationrules/" ) diff --git a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go index 4da1254b2..48757267f 100644 --- a/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go +++ b/vendor/github.com/vmware/go-vcloud-director/v2/types/v56/types.go @@ -1728,6 +1728,18 @@ type LBAppProfileHTTPRedirect struct { To string `xml:"to,omitempty"` } +// LBAppRule represents a load balancer application rule as per "vCloud Director API for NSX +// Programming Guide" +// https://code.vmware.com/docs/6900/vcloud-director-api-for-nsx-programming-guide +type LBAppRule struct { + XMLName xml.Name `xml:"applicationRule"` + ID string `xml:"applicationRuleId,omitempty"` + Name string `xml:"name,omitempty"` + Script string `xml:"script,omitempty"` +} + +type LBAppRules []LBAppRule + // LoadBalancerVirtualServer represents a load balancer virtual server. // Type: LoadBalancerVirtualServerType // Namespace: http://www.vmware.com/vcloud/v1.5 diff --git a/vendor/modules.txt b/vendor/modules.txt index d74e4e7a8..7e931d098 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -216,7 +216,7 @@ github.com/ulikunitz/xz/internal/hash # github.com/vmihailenco/msgpack v4.0.1+incompatible github.com/vmihailenco/msgpack github.com/vmihailenco/msgpack/codes -# github.com/vmware/go-vcloud-director/v2 v2.3.0-alpha.9 +# github.com/vmware/go-vcloud-director/v2 v2.3.0-beta.2 github.com/vmware/go-vcloud-director/v2/govcd github.com/vmware/go-vcloud-director/v2/types/v56 github.com/vmware/go-vcloud-director/v2/util diff --git a/website/docs/d/lb_app_profile.html.markdown b/website/docs/d/lb_app_profile.html.markdown index 4d5d4a54d..247cfff9e 100644 --- a/website/docs/d/lb_app_profile.html.markdown +++ b/website/docs/d/lb_app_profile.html.markdown @@ -41,4 +41,4 @@ The following arguments are supported: ## Attribute Reference -All the attributes defined in `vcd_lb_app_profile` resource are be available. +All the attributes defined in `vcd_lb_app_profile` resource are available. diff --git a/website/docs/d/lb_app_rule.html.markdown b/website/docs/d/lb_app_rule.html.markdown new file mode 100644 index 000000000..1938f2ab7 --- /dev/null +++ b/website/docs/d/lb_app_rule.html.markdown @@ -0,0 +1,42 @@ +--- +layout: "vcd" +page_title: "vCloudDirector: vcd_lb_app_rule" +sidebar_current: "docs-vcd-data-source-lb-app-rule" +description: |- + Provides an NSX edge gateway load balancer application rule data source. +--- + +# vcd\_lb\_app\_rule + +Provides a vCloud Director Edge Gateway Load Balancer Application Rule data source. An application +rule allows to directly manipulate and manage IP application traffic with load balancer. + +~> **Note:** See additional support notes in [application rule resource page] +(/docs/providers/vcd/r/lb_app_rule.html). + +Supported in provider *v2.4+* + +## Example Usage + +```hcl +data "vcd_lb_app_rule" "my-rule" { + org = "my-org" + vdc = "my-org-vdc" + edge_gateway = "my-edge-gw" + + name = "not-managed" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `org` - (Optional) The name of organization to use, optional if defined at provider level +* `vdc` - (Optional) The name of VDC to use, optional if defined at provider level +* `edge_gateway` - (Required) The name of the edge gateway on which the service monitor is defined +* `name` - (Required) Application rule name for identifying the exact application rule + +## Attribute Reference + +All the attributes defined in `vcd_lb_app_rule` resource are available. diff --git a/website/docs/d/lb_server_pool.markdown b/website/docs/d/lb_server_pool.markdown index eacbc7828..751947b6f 100644 --- a/website/docs/d/lb_server_pool.markdown +++ b/website/docs/d/lb_server_pool.markdown @@ -39,4 +39,4 @@ The following arguments are supported: ## Attribute Reference -All the attributes defined in `vcd_lb_server_pool` resource are be available. +All the attributes defined in `vcd_lb_server_pool` resource are available. diff --git a/website/docs/d/lb_service_monitor.html.markdown b/website/docs/d/lb_service_monitor.html.markdown index 33f2a0781..c56b9ea02 100644 --- a/website/docs/d/lb_service_monitor.html.markdown +++ b/website/docs/d/lb_service_monitor.html.markdown @@ -40,4 +40,4 @@ The following arguments are supported: ## Attribute Reference -All the attributes defined in `vcd_lb_service_monitor` resource are be available. +All the attributes defined in `vcd_lb_service_monitor` resource are available. diff --git a/website/docs/r/lb_app_rule.html.markdown b/website/docs/r/lb_app_rule.html.markdown new file mode 100644 index 000000000..9531601cf --- /dev/null +++ b/website/docs/r/lb_app_rule.html.markdown @@ -0,0 +1,104 @@ +--- +layout: "vcd" +page_title: "vCloudDirector: vcd_lb_app_rule" +sidebar_current: "docs-vcd-resource-lb-app-rule" +description: |- + Provides an NSX edge gateway load balancer application rule resource. +--- + +# vcd\_lb\_app\_rule + +Provides a vCloud Director Edge Gateway Load Balancer Application Rule resource. An application rule +allows to directly manipulate and manage IP application traffic with load balancer. + +~> **Note:** To make load balancing work one must ensure that load balancing is enabled on edge +gateway (edge gateway must be advanced). +This depends on NSX version to work properly. Please refer to [VMware Product Interoperability +Matrices](https://www.vmware.com/resources/compatibility/sim/interop_matrix.php#interop&29=&93=) +to check supported vCloud director and NSX for vSphere configurations. + +~> **Note:** The vCloud Director API for NSX supports a subset of the operations and objects defined +in the NSX vSphere API Guide. The API supports NSX 6.2, 6.3, and 6.4. + +Supported in provider *v2.4+* + +## Example Usage 1 (Application rule with single line script) + +```hcl +provider "vcd" { + user = "${var.admin_user}" + password = "${var.admin_password}" + org = "System" + url = "https://AcmeVcd/api" +} + +resource "vcd_lb_app_rule" "example-one" { + edge_gateway = "my-edge-gw" + org = "my-org" + vdc = "my-org-vdc" + + name = "script1" + script = "acl vmware_page url_beg / vmware redirect location https://www.vmware.com/ ifvmware_page" +} +``` + +## Example Usage 2 (Application rule with multi line script) + +```hcl +provider "vcd" { + user = "${var.admin_user}" + password = "${var.admin_password}" + org = "System" + url = "https://AcmeVcd/api" +} + +resource "vcd_lb_app_rule" "example-two" { + edge_gateway = "my-edge-gw" + org = "my-org" + vdc = "my-org-vdc" + name = "script1" + script = <<-EOT + acl vmware_page url_beg / vmware redirect location https://www.vmware.com/ ifvmware_page + acl other_page2 url_beg / other2 redirect location https://www.other2.com/ ifother_page2 + acl hello payload(0,6) -m bin 48656c6c6f0a + EOT +} +``` + +## Argument Reference + +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 +* `vdc` - (Optional) The name of VDC to use, optional if defined at provider level +* `edge_gateway` - (Required) The name of the edge gateway on which the application rule is to be created +* `name` - (Required) Application rule name +* `script` - (Required) A multiline application rule script. +Terraform's [HEREDOC syntax](https://www.terraform.io/docs/configuration/expressions.html#string-literals) +may be useful for multiline scripts. **Note:** For information on +the application rule syntax, see more in [vCloud Director documentation] +(https://docs.vmware.com/en/vCloud-Director/9.7/com.vmware.vcloud.tenantportal.doc/GUID-AFF9F70F-85C9-4053-BA69-F2B062F34C7F.html) + +## Attribute Reference + +The following attributes are exported on this resource: + +* `id` - The NSX ID of the load balancer application rule + +## Importing + +~> **Note:** The current implementation of Terraform import can only import resources into the state. +It does not generate configuration. [More information.](https://www.terraform.io/docs/import/) + +An existing load balancer application rule can be [imported][docs-import] into this resource +via supplying the full dot separated path for load balancer application rule. An example is +below: + +[docs-import]: https://www.terraform.io/docs/import/ + +``` +terraform import vcd_lb_app_rule.imported my-org.my-org-vdc.my-edge-gw.my-lb-app-rule +``` + +The above would import the application rule named `my-lb-app-my-lb-app-rule` that is defined on edge +gateway `my-edge-gw` which is configured in organization named `my-org` and vDC named `my-org-vdc`. diff --git a/website/vcd.erb b/website/vcd.erb index 07ee0ed14..1b8ff805c 100644 --- a/website/vcd.erb +++ b/website/vcd.erb @@ -22,6 +22,9 @@