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

Add vcfa_vcenter resource + data source #2

Merged
merged 11 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
2 changes: 2 additions & 0 deletions .changes/v1.0.0/2-feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* **New Resource:** `vcfa_vcenter` to manage TM vCenter servers [GH-2]
adambarreiro marked this conversation as resolved.
Show resolved Hide resolved
* **New Data Source:** `vcfa_vcenter` to read TM vCenter servers [GH-2]
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22.3
require (
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0
github.com/vmware/go-vcloud-director/v3 v3.0.0-alpha.14
github.com/vmware/go-vcloud-director/v3 v3.0.0-alpha.15
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/vmware/go-vcloud-director/v3 v3.0.0-alpha.14 h1:zY0f4JXNsK2z04DuoQ9VLeRw0FZq1sRCRrmSeKog/ps=
github.com/vmware/go-vcloud-director/v3 v3.0.0-alpha.14/go.mod h1:68KHsVns52dsq/w5JQYzauaU/+NAi1FmCxhBrFc/VoQ=
github.com/vmware/go-vcloud-director/v3 v3.0.0-alpha.15 h1:wJG8XElzjkFqMMgD0M2lRlNpzyotOjX5HQJvQ5vIvMs=
github.com/vmware/go-vcloud-director/v3 v3.0.0-alpha.15/go.mod h1:68KHsVns52dsq/w5JQYzauaU/+NAi1FmCxhBrFc/VoQ=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand Down
6 changes: 6 additions & 0 deletions vcfa/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ func (c *Config) Client() (*VCDClient, error) {
if err != nil {
return nil, fmt.Errorf("something went wrong during authentication: %s", err)
}

// Require API V40 (TM starting API version) to be present
if vcdClient.Client.APIVCDMaxVersionIs("< 40") {
return nil, fmt.Errorf("unsupported API version, at least v40 required")
}

cachedVCDClients.Lock()
cachedVCDClients.conMap[checksum] = cachedConnection{initTime: time.Now(), connection: vcdClient}
cachedVCDClients.Unlock()
Expand Down
101 changes: 101 additions & 0 deletions vcfa/datasource_vcfa_vcenter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package vcfa

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 datasourceVcfaVcenter() *schema.Resource {
return &schema.Resource{
ReadContext: datasourceVcfaVcenterRead,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: fmt.Sprintf("Name of %s", labelVcfaVirtualCenter),
},
"url": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("URL of %s", labelVcfaVirtualCenter),
},
"username": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Username of %s", labelVcfaVirtualCenter),
},
"is_enabled": {
Type: schema.TypeBool,
Computed: true,
Description: fmt.Sprintf("Should the %s be enabled", labelVcfaVirtualCenter),
},
"description": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Description of %s", labelVcfaVirtualCenter),
},
"has_proxy": {
Type: schema.TypeBool,
Computed: true,
Description: fmt.Sprintf("A flag that shows if %s has proxy defined", labelVcfaVirtualCenter),
},
"is_connected": {
Type: schema.TypeBool,
Computed: true,
Description: fmt.Sprintf("A flag that shows if %s is connected", labelVcfaVirtualCenter),
},
"mode": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Mode of %s", labelVcfaVirtualCenter),
},
"connection_status": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Listener state of %s", labelVcfaVirtualCenter),
},
"cluster_health_status": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Mode of %s", labelVcfaVirtualCenter),
},
"vcenter_version": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("Version of %s", labelVcfaVirtualCenter),
},
"uuid": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("%s UUID", labelVcfaVirtualCenter),
},
"vcenter_host": {
Type: schema.TypeString,
Computed: true,
Description: fmt.Sprintf("%s hostname", labelVcfaVirtualCenter),
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "vCenter status",
},
},
}
}

func datasourceVcfaVcenterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
vcdClient := meta.(*VCDClient)

c := dsReadConfig[*govcd.VCenter, types.VSphereVirtualCenter]{
entityLabel: labelVcfaVirtualCenter,
getEntityFunc: vcdClient.GetVCenterByName,
stateStoreFunc: setVcenterData,
}
return readDatasource(ctx, d, meta, c)
}
3 changes: 2 additions & 1 deletion vcfa/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ func Resources(nameRegexp string, includeDeprecated bool) (map[string]*schema.Re

var globalDataSourceMap = map[string]*schema.Resource{
"vcfa_tm_version": datasourceVcfaTmVersion(), // 1.0
"vcfa_vcenter": datasourceVcfaVcenter(), // 1.0
}

var globalResourceMap = map[string]*schema.Resource{
"vcfa_deleteme": resourceVcfaDeleteme(), // TODO: VCFA: Delete this (and associated doc) once there's a resource ready
"vcfa_vcenter": resourceVcfaVcenter(), // 1.0
}

// Provider returns a terraform.ResourceProvider.
Expand Down
4 changes: 2 additions & 2 deletions vcfa/provider_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ func TestVcfaResources(t *testing.T) {
},
{
name: "MatchExactResourceName",
args: args{nameRegexp: "vcfa_deleteme", includeDeprecated: false}, // TODO: VCFA: Change this to correct resource once there's one
wantLen: 1, // should return only one because exact name was given
args: args{nameRegexp: "vcfa_vcenter", includeDeprecated: false},
wantLen: 1, // should return only one because exact name was given
lenOnly: true,
wantErr: false,
},
Expand Down
112 changes: 112 additions & 0 deletions vcfa/remove_leftovers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,127 @@ package vcfa

import (
"fmt"
"regexp"
"strings"

"github.com/vmware/go-vcloud-director/v3/govcd"
)

// This file contains routines that clean up the test suite after failed tests

// entityDef is the definition of an entity (to be either deleted or kept)
// with an optional comment
type entityDef struct {
Type string `json:"type"`
Name string `json:"name"`
Comment string `json:"comment,omitempty"`
NameRegexp *regexp.Regexp
}

// entityList is a collection of entityDef
type entityList []entityDef

// doNotDelete contains a list of entities that should not be deleted,
// despite having a name that starts with `Test` or `test`
var doNotDelete = entityList{
// {Type: "vcfa_xxx", Name: "test_entity", Comment: "loaded with provisioning"},
}

// alsoDelete contains a list of entities that should be removed , in addition to the ones
// found by name matching
// Add to this list if you ever get an entity left behind by a test
var alsoDelete = entityList{
// {Type: "vcfa_xxx", Name: "custom-name", Comment: "manually created"},
}

// isTest is a regular expression that tells if an entity needs to be deleted
var isTest = regexp.MustCompile(`^[Tt]est`)

// alwaysShow lists the resources that will always be shown
var alwaysShow = []string{
"vcfa_vcenter",
}

func removeLeftovers(govcdClient *govcd.VCDClient, verbose bool) error {
if verbose {
fmt.Printf("Start leftovers removal\n")
}

// --------------------------------------------------------------
// vCenters
// --------------------------------------------------------------
if govcdClient.Client.IsSysAdmin {
allVcs, err := govcdClient.GetAllVCenters(nil)
if err != nil {
return fmt.Errorf("error retrieving provider vCenters: %s", err)
}
for _, vc := range allVcs {
toBeDeleted := shouldDeleteEntity(alsoDelete, doNotDelete, vc.VSphereVCenter.Name, "vcfa_vcenter", 0, verbose)
if toBeDeleted {
fmt.Printf("\t REMOVING vCenter %s\n", vc.VSphereVCenter.Name)
err = vc.Disable()
if err != nil {
return fmt.Errorf("error disabling %s '%s': %s", labelVcfaVirtualCenter, vc.VSphereVCenter.Name, err)
}
err := vc.Delete()
if err != nil {
return fmt.Errorf("error deleting %s '%s': %s", labelVcfaVirtualCenter, vc.VSphereVCenter.Name, err)
}
}
}
}

return nil
}

// shouldDeleteEntity checks whether a given entity is to be deleted, either by its name
// or by its inclusion in one of the entity lists
func shouldDeleteEntity(alsoDelete, doNotDelete entityList, name, entityType string, level int, verbose bool) bool {
inclusion := ""
exclusion := ""
// 1. First requirement to be deleted: the entity name starts with 'Test' or 'test'
toBeDeleted := isTest.MatchString(name)
if inList(alsoDelete, name, entityType) {
toBeDeleted = true
// 2. If the entity was in the additional deletion list, regardless of the name,
// it is marked for deletion, with a "+", indicating that it was selected for deletion because of the
// deletion list
inclusion = " +"
}
if inList(doNotDelete, name, entityType) {
toBeDeleted = false
// 3. If a file, normally marked for deletion, is found in the keep list,
// its deletion status is revoked, and it is marked with a "-", indicating that it was excluded
// for deletion because of the keep list
exclusion = " -"
}
tabs := strings.Repeat("\t", level)
format := tabs + "[%s] %s (%s%s%s)\n"
deletionText := "DELETE"
if !toBeDeleted {
deletionText = "keep"
}

// 4. Show the entity. If it is to be deleted, it will always be shown
if toBeDeleted || contains(alwaysShow, entityType) {
if verbose {
fmt.Printf(format, entityType, name, deletionText, inclusion, exclusion)
}
}
return toBeDeleted
}

// inList shows whether a given entity is included in an entityList
func inList(list entityList, name, entityType string) bool {
for _, element := range list {
// Compare by names
if element.Name == name && element.Type == entityType {
return true
}
// Compare by possible regexp values
if element.NameRegexp != nil && element.NameRegexp.MatchString(name) {
return true
}
}
return false
}
Loading
Loading