Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce 'vcd_external_network_v2' and dependencies with NSX-T support #560

Merged
merged 28 commits into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f65b8d2
Introduce 'vcd_external_network_v2' and dependencies
Didainius Sep 24, 2020
ebb55e0
Fix static checks
Didainius Sep 24, 2020
a81bc94
Fix vet
Didainius Sep 24, 2020
d087259
Self review
Didainius Sep 24, 2020
0b21d70
restructure
Didainius Sep 25, 2020
d1881f5
Address comments, test VRF Tier-0 router, pull in newer govcd
Didainius Sep 29, 2020
7679cc4
Add extra fields to vcd_vcenter datasource
Didainius Sep 30, 2020
34b35d1
[skip-ci] Add explicit TODO comment about Tier-0 VTF router support
Didainius Sep 30, 2020
500afe9
Bump govcd
Didainius Oct 5, 2020
e7449e4
merge master
Didainius Oct 5, 2020
3d64fad
Add missing parameter
Didainius Oct 5, 2020
95d48ab
[skip ci] Improve docs
Didainius Oct 6, 2020
7c96b20
Merge master
Didainius Oct 7, 2020
c7d0838
[skip ci] Add NSX-T example and note to resource docs
Didainius Oct 7, 2020
fc57e45
Pull in latest govcd
Didainius Oct 7, 2020
0f57f18
Rebuild vendor
Didainius Oct 7, 2020
e892182
Update docs
Didainius Oct 7, 2020
c4b1cf6
Merge branch 'master' into nsxt-external-network3
Didainius Oct 8, 2020
ff62fb7
Add 'nsxt' tag to params
Didainius Oct 8, 2020
2131edf
Split ExternalNetworkV2 datasource test
Didainius Oct 9, 2020
1832c8a
Fix make vet
Didainius Oct 9, 2020
813cfed
Merge branch 'master' into nsxt-external-network3
Didainius Oct 13, 2020
d1b8b6d
Merge branch 'master' into nsxt-external-network3
Didainius Oct 13, 2020
113c8d9
Pull in govcd v2.9.0-rc.1
Didainius Oct 13, 2020
1abb2d8
Add more details to not found error for vcd_nsxt_tier0_router
Didainius Oct 13, 2020
74665bf
Fix example
Didainius Oct 13, 2020
986ec40
Fix comment
Didainius Oct 13, 2020
ed17c66
TypeString -> TypeBool
Didainius Oct 13, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/v3.0.0/560-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
* **New Resource**: `vcd_external_network_v2` with NSX-T support [GH-560]
* **New Data Source**: `vcd_external_network_v2` with NSX-T support [GH-560]
* **New Data Source**: `vcd_vcenter` [GH-560]
* **New Data Source**: `vcd_portgroup` [GH-560]
* **New Data Source**: `vcd_nsxt_manager` [GH-560]
* **New Data Source**: `vcd_nsxt_tier0_router` [GH-560]
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ require (
github.com/vmware/go-vcloud-director/v2 v2.9.0-beta.2
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 // indirect
)

replace github.com/vmware/go-vcloud-director/v2 => github.com/Didainius/go-vcloud-director/v2 v2.9.0-beta.1.0.20201007103319-a64b3faf72d6
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Didainius/go-vcloud-director/v2 v2.9.0-beta.1.0.20201007103319-a64b3faf72d6 h1:+7JMXR4jv2kjG4gYRtRi9riNyb+LvD9BF7Mykg53VQQ=
github.com/Didainius/go-vcloud-director/v2 v2.9.0-beta.1.0.20201007103319-a64b3faf72d6/go.mod h1:czvTQZlB4/WsOsL7rMVCb+SYAPJhx/dYoS/Sk7rc/O0=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
Expand Down Expand Up @@ -213,8 +215,6 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvc
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.9.0-beta.2 h1:4cU0YFLkdW7u2rDn9abOJe9wWinyI6t2szVciOAURZk=
github.com/vmware/go-vcloud-director/v2 v2.9.0-beta.2/go.mod h1:czvTQZlB4/WsOsL7rMVCb+SYAPJhx/dYoS/Sk7rc/O0=
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
Expand Down
15 changes: 15 additions & 0 deletions vcd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ type TestConfig struct {
PeerSubnetGateway string `json:"peerSubnetGw"`
} `json:"peer"`
} `json:"networking"`
Nsxt struct {
Manager string `json:"manager"`
Tier0router string `json:"tier0router"`
Tier0routerVrf string `json:"tier0routervrf"`
} `json:"nsxt"`
Logging struct {
Enabled bool `json:"enabled,omitempty"`
LogFileName string `json:"logFileName,omitempty"`
Expand Down Expand Up @@ -1117,4 +1122,14 @@ func skipNoNsxtConfiguration(t *testing.T) {
if testConfig.VCD.NsxtProviderVdc.StorageProfile == "" {
t.Skip(generalMessage + "No storage profile specified")
}

if testConfig.Nsxt.Manager == "" {
t.Skip(generalMessage + "No NSX-T manager specified")
}
if testConfig.Nsxt.Tier0router == "" {
t.Skip(generalMessage + "No NSX-T Tier-0 specified")
}
if testConfig.Nsxt.Tier0routerVrf == "" {
t.Skip(generalMessage + "No VRF NSX-T Tier-0 specified")
}
}
56 changes: 56 additions & 0 deletions vcd/datasource_nsxt_manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// +build ALL nsxt functional

package vcd

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccVcdDatasourceNsxtManager(t *testing.T) {

if !usingSysAdmin() {
t.Skip(t.Name() + " requires system admin privileges")
return
}

skipNoNsxtConfiguration(t)

var params = StringMap{
"FuncName": t.Name(),
"NsxtManager": testConfig.Nsxt.Manager,
"Tags": "nsxt",
}

configText := templateFill(testAccCheckVcdNsxtManager, params)

if vcdShortTest {
t.Skip(acceptanceTestsSkipped)
return
}
debugPrintf("#[DEBUG] CONFIGURATION: %s", configText)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: configText,
Check: resource.ComposeTestCheckFunc(
// ID must match URN 'urn:vcloud:nsxtmanager:09722307-aee0-4623-af95-7f8e577c9ebc'
resource.TestMatchResourceAttr("data.vcd_nsxt_manager.nsxt", "id",
regexp.MustCompile(`urn:vcloud:nsxtmanager:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$`)),
resource.TestCheckResourceAttr("data.vcd_nsxt_manager.nsxt", "name", params["NsxtManager"].(string)),
),
},
},
})
}

const testAccCheckVcdNsxtManager = `
data "vcd_nsxt_manager" "nsxt" {
name = "{{.NsxtManager}}"
}
`
84 changes: 84 additions & 0 deletions vcd/datasource_nsxt_tier0_router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package vcd

import (
"fmt"

"github.com/vmware/go-vcloud-director/v2/types/v56"

"github.com/vmware/go-vcloud-director/v2/govcd"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func datasourceVcdNsxtTier0Router() *schema.Resource {
return &schema.Resource{
Read: datasourceNsxtTier0RouterRead,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "Name of NSX-T Tier-0 router.",
},
"nsxt_manager_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "ID of NSX-T manager.",
},
"is_assigned": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assigned or used in comments explaining the complexity you are using used.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but used is somewhat "informal" for an attribute to me. We could probably discuss consumed if that sounds better.
cc @dataclouder, @lvirbalas

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dataclouder @lvirbalas your input still would be valuable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assigned is good

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_assigned is fine for me in this context. BTW, it's TypeString ;)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be TypeBool

Type: schema.TypeString,
Computed: true,
Description: "Defines if Tier-0 router is already assigned to external network.",
},
},
}
}

// datasourceNsxtTier0RouterRead has special behavior. By default `GetImportableNsxtTier0RouterByName` which uses API
// endpoint `1.0.0/nsxTResources/importableTier0Routers` does not return Tier-0 routers when they are used in external
// networks. This causes a problem in regular Terraform flow - when user uses this datasource to reference Tier-0 router
// for external network creation - next "apply" would fail with "Tier 0 router not found error". If original endpoint
// does not find Tier-0 router - then this datasource queries all defined external networks and looks for Tier-0 router
// backing by name.
func datasourceNsxtTier0RouterRead(d *schema.ResourceData, meta interface{}) error {
vcdClient := meta.(*VCDClient)
nsxtManagerId := d.Get("nsxt_manager_id").(string)
tier0RouterName := d.Get("name").(string)

tier0Router, err := vcdClient.GetImportableNsxtTier0RouterByName(tier0RouterName, nsxtManagerId)
if err != nil && !govcd.ContainsNotFound(err) {
return fmt.Errorf("could not find NSX-T Tier-0 router by name '%s' in NSX-T manager %s: %s",
tier0RouterName, nsxtManagerId, err)
}

// If unused Tier-0 router is found - set the ID and return
if err == nil {
d.Set("is_assigned", false)
d.SetId(tier0Router.NsxtTier0Router.ID)
return nil
}

// API endpoint for Tier-0 routers does not return Tier-0 routers which are already used in external networks
// therefore we are searching for used Tier-0 router name in external networks. This should not cause any risks as
// required permissions should be of the same level.
if govcd.ContainsNotFound(err) {
// Filtering by network backing is unsupported therefore queryParameters are nil
extNets, err := govcd.GetAllExternalNetworksV2(vcdClient.VCDClient, nil)
if err != nil {
return fmt.Errorf("could not find external networks: %s", err)
}

for _, extNetwork := range extNets {
for _, v := range extNetwork.ExternalNetwork.NetworkBackings.Values {
// Very odd but when VRF Tier-0 router is used - BackingType can be UNKNOWN
if v.Name == tier0RouterName &&
(v.BackingType == types.ExternalNetworkBackingTypeNsxtTier0Router || v.BackingType == "UNKNOWN") {
d.Set("is_assigned", true)
d.SetId(v.BackingID)
return nil
}
}
}
}

return govcd.ErrorEntityNotFound
}
73 changes: 73 additions & 0 deletions vcd/datasource_nsxt_tier0_router_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// +build ALL nsxt functional

package vcd

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

// TestAccVcdDatasourceNsxtTier0Router checks if datasource can find existing regular Tier-0 router
// provided it is specified in configuration
func TestAccVcdDatasourceNsxtTier0Router(t *testing.T) {
testAccVcdDatasourceNsxtTier0Router(t, testConfig.Nsxt.Tier0router)
}

// TestAccVcdDatasourceNsxtTier0Router checks if datasource can find existing VRF Tier-0 router
// provided it is specified in configuration
func TestAccVcdDatasourceNsxtTier0RouterVrf(t *testing.T) {
testAccVcdDatasourceNsxtTier0Router(t, testConfig.Nsxt.Tier0routerVrf)
}

func testAccVcdDatasourceNsxtTier0Router(t *testing.T, tier0RouterName string) {

if !usingSysAdmin() {
t.Skip(t.Name() + " requires system admin privileges")
return
}

skipNoNsxtConfiguration(t)

var params = StringMap{
"FuncName": t.Name(),
"NsxtManager": testConfig.Nsxt.Manager,
"NsxtTier0Router": tier0RouterName,
"Tags": "nsxt",
}

configText := templateFill(testAccCheckVcdNsxtTier0Router, params)

if vcdShortTest {
t.Skip(acceptanceTestsSkipped)
return
}
debugPrintf("#[DEBUG] CONFIGURATION: %s", configText)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: configText,
Check: resource.ComposeTestCheckFunc(
// ID must match URN 'urn:vcloud:nsxtmanager:09722307-aee0-4623-af95-7f8e577c9ebc'
resource.TestMatchResourceAttr("data.vcd_nsxt_manager.nsxt", "id",
regexp.MustCompile(`urn:vcloud:nsxtmanager:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$`)),
resource.TestCheckResourceAttr("data.vcd_nsxt_tier0_router.router", "name", params["NsxtTier0Router"].(string)),
),
},
},
})
}

const testAccCheckVcdNsxtTier0Router = `
data "vcd_nsxt_manager" "nsxt" {
name = "{{.NsxtManager}}"
}
data "vcd_nsxt_tier0_router" "router" {
name = "{{.NsxtTier0Router}}"
nsxt_manager_id = data.vcd_nsxt_manager.nsxt.id
}
`
39 changes: 33 additions & 6 deletions vcd/datasource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,35 @@ func TestAccDataSourceNotFound(t *testing.T) {
return
}

// Setup temporary client to evaluate versions and conditionally skip tests
vcdClient := createTemporaryVCDConnection()

// Run a sub-test for each of data source defined in provider
for _, dataSource := range Provider().DataSources() {
t.Run(dataSource.Name, testSpecificDataSourceNotFound(t, dataSource.Name))
t.Run(dataSource.Name, testSpecificDataSourceNotFound(t, dataSource.Name, vcdClient))
}
}

func testSpecificDataSourceNotFound(t *testing.T, dataSourceName string) func(*testing.T) {
func testSpecificDataSourceNotFound(t *testing.T, dataSourceName string, vcdClient *VCDClient) func(*testing.T) {
return func(t *testing.T) {

// Skip sub-test if conditions are not met
if dataSourceName == "vcd_external_network" && !usingSysAdmin() {
switch {
case dataSourceName == "vcd_external_network" && !usingSysAdmin():
t.Skip(`Works only with system admin privileges`)
case dataSourceName == "vcd_external_network_v2" && vcdClient.Client.APIVCDMaxVersionIs("< 33") &&
!usingSysAdmin():
t.Skip("External network V2 requires at least API version 33 (VCD 10.0+)")
case (dataSourceName == "vcd_nsxt_tier0_router" || dataSourceName == "vcd_external_network_v2" || dataSourceName == "vcd_nsxt_manager") &&
(testConfig.Nsxt.Manager == "" || testConfig.Nsxt.Tier0router == "") || !usingSysAdmin():
t.Skip(`No NSX-T configuration detected`)
}

// Get list of mandatory fields in schema for a particular data source
mandatoryFields := getMandatoryDataSourceSchemaFields(dataSourceName)
mandatoryRuntimeFields := getMandatoryDataSourceRuntimeFields(dataSourceName)
mandatoryFields = append(mandatoryFields, mandatoryRuntimeFields...)
addedParams := addMandatoryParams(dataSourceName, mandatoryFields, t)
addedParams := addMandatoryParams(dataSourceName, mandatoryFields, t, vcdClient)

var params = StringMap{
"DataSourceName": dataSourceName,
Expand Down Expand Up @@ -93,7 +103,7 @@ func getMandatoryDataSourceRuntimeFields(dataSourceName string) []string {
return []string{}
}

func addMandatoryParams(dataSourceName string, mandatoryFields []string, t *testing.T) string {
func addMandatoryParams(dataSourceName string, mandatoryFields []string, t *testing.T, vcdClient *VCDClient) string {
var templateFields string
for fieldIndex := range mandatoryFields {

Expand All @@ -103,6 +113,12 @@ func addMandatoryParams(dataSourceName string, mandatoryFields []string, t *test
return templateFields
}

// vcd_portgroup requires portgroup type
if dataSourceName == "vcd_portgroup" && mandatoryFields[fieldIndex] == "type" {
templateFields = templateFields + `type = "` + testConfig.Networking.ExternalNetworkPortGroupType + `"` + "\n"
return templateFields
}

switch mandatoryFields[fieldIndex] {
// Fields, which must be valid to satisfy a data source
case "org": // Some data sources require org - fill it from testConfig
Expand All @@ -118,7 +134,18 @@ func addMandatoryParams(dataSourceName string, mandatoryFields []string, t *test
return ""
}
templateFields = templateFields + `vapp_name = "` + vapp.VApp.Name + `"` + "\n"

case "nsxt_manager_id":
// This test needs a valid nsxt_manager_id
nsxtManager, err := vcdClient.QueryNsxtManagerByName(testConfig.Nsxt.Manager)
if err != nil {
t.Skipf("No suitable NSX-T manager found for this test: %s", err)
return ""
}
nsxtManagerUrn, err := govcd.BuildUrnWithUuid("urn:vcloud:nsxtmanager:", extractUuid(nsxtManager[0].HREF))
if err != nil {
t.Errorf("error building URN for NSX-T manager")
}
templateFields = templateFields + `nsxt_manager_id = "` + nsxtManagerUrn + `"` + "\n"
// Invalid fields which are required for some resources for search (usually they are used instead of `name`)
case "rule_id":
templateFields = templateFields + `rule_id = "347928347234"` + "\n"
Expand Down
Loading