Skip to content

Commit

Permalink
Placement groups (#426)
Browse files Browse the repository at this point in the history
* Add placement_groups

* add resource_test for placement_group

* Bump hcloud-go version

* Update docs
  • Loading branch information
Adi146 authored Aug 16, 2021
1 parent f0e2e3c commit e19f2d7
Show file tree
Hide file tree
Showing 21 changed files with 776 additions and 18 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ require (
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0
github.com/hetznercloud/hcloud-go v1.29.1
github.com/hetznercloud/hcloud-go v1.30.0
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210326060303-6b1517762897
)

go 1.16
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ github.com/hashicorp/terraform-plugin-sdk/v2 v2.7.0/go.mod h1:grseeRo9g3yNkYW09i
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hetznercloud/hcloud-go v1.29.1 h1:UiV+GZVEOFramb49ASbXfpJGjXa6FmJe3Hh+Ns3RUJ4=
github.com/hetznercloud/hcloud-go v1.29.1/go.mod h1:2C5uMtBiMoFr3m7lBFPf7wXTdh33CevmZpQIIDPGYJI=
github.com/hetznercloud/hcloud-go v1.30.0 h1:Q8Y+YHgum6XvyVfz2IFp2pLWtupEFbykl12D5TwdBig=
github.com/hetznercloud/hcloud-go v1.30.0/go.mod h1:2C5uMtBiMoFr3m7lBFPf7wXTdh33CevmZpQIIDPGYJI=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
Expand Down
3 changes: 3 additions & 0 deletions hcloud/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/hetznercloud/terraform-provider-hcloud/internal/firewall"
"github.com/hetznercloud/terraform-provider-hcloud/internal/hcclient"
"github.com/hetznercloud/terraform-provider-hcloud/internal/placementgroup"

"github.com/hetznercloud/terraform-provider-hcloud/internal/snapshot"

Expand Down Expand Up @@ -85,6 +86,7 @@ func Provider() *schema.Provider {
sshkey.ResourceType: sshkey.Resource(),
volume.AttachmentResourceType: volume.AttachmentResource(),
volume.ResourceType: volume.Resource(),
placementgroup.ResourceType: placementgroup.Resource(),
},
DataSourcesMap: map[string]*schema.Resource{
certificate.DataSourceType: certificate.DataSource(),
Expand All @@ -103,6 +105,7 @@ func Provider() *schema.Provider {
sshkey.DataSourceType: sshkey.DataSource(),
sshkey.SSHKeysDataSourceType: sshkey.SSHKeysDataSource(),
volume.DataSourceType: volume.DataSource(),
placementgroup.DataSourceType: placementgroup.DataSource(),
},
ConfigureContextFunc: providerConfigure,
}
Expand Down
3 changes: 3 additions & 0 deletions hcloud/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hetznercloud/terraform-provider-hcloud/internal/loadbalancer"
"github.com/hetznercloud/terraform-provider-hcloud/internal/location"
"github.com/hetznercloud/terraform-provider-hcloud/internal/network"
"github.com/hetznercloud/terraform-provider-hcloud/internal/placementgroup"
"github.com/hetznercloud/terraform-provider-hcloud/internal/rdns"
"github.com/hetznercloud/terraform-provider-hcloud/internal/server"
"github.com/hetznercloud/terraform-provider-hcloud/internal/servertype"
Expand Down Expand Up @@ -49,6 +50,7 @@ func TestProvider_Resources(t *testing.T) {
sshkey.ResourceType,
volume.AttachmentResourceType,
volume.ResourceType,
placementgroup.ResourceType,
}

resources := provider.Resources()
Expand Down Expand Up @@ -78,6 +80,7 @@ func TestProvider_DataSources(t *testing.T) {
sshkey.DataSourceType,
sshkey.SSHKeysDataSourceType,
volume.DataSourceType,
placementgroup.DataSourceType,
}

datasources := provider.DataSources()
Expand Down
67 changes: 67 additions & 0 deletions internal/e2etests/placementgroup/data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package placementgroup

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hetznercloud/terraform-provider-hcloud/internal/e2etests"
"github.com/hetznercloud/terraform-provider-hcloud/internal/placementgroup"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testsupport"

"github.com/hetznercloud/terraform-provider-hcloud/internal/testtemplate"
)

func TestAccHcloudDataSourcePlacementGroupTest(t *testing.T) {
tmplMan := testtemplate.Manager{}

res := placementgroup.NewRData(t, "basic-placement-group", "spread")
res.SetRName("placement-group-ds-test")

placementGroupByName := &placementgroup.DData{
PlacementGroupName: res.TFID() + ".name",
}
placementGroupByName.SetRName("placement_group_by_name")

placementGroupByID := &placementgroup.DData{
PlacementGroupID: res.TFID() + ".id",
}
placementGroupByID.SetRName("placement_group_by_id")

placementGroupBySel := &placementgroup.DData{
LabelSelector: fmt.Sprintf("key=${%s.labels[\"key\"]}", res.TFID()),
}
placementGroupBySel.SetRName("placement_group_by_sel")

resource.Test(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(placementgroup.ResourceType, placementgroup.ByID(t, nil)),
Steps: []resource.TestStep{
{
Config: tmplMan.Render(t,
"testdata/r/hcloud_placement_group", res,
),
},
{
Config: tmplMan.Render(t,
"testdata/r/hcloud_placement_group", res,
"testdata/d/hcloud_placement_group", placementGroupByName,
"testdata/d/hcloud_placement_group", placementGroupByID,
"testdata/d/hcloud_placement_group", placementGroupBySel,
),

Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(placementGroupByName.TFID(),
"name", fmt.Sprintf("%s--%d", res.Name, tmplMan.RandInt)),

resource.TestCheckResourceAttr(placementGroupByID.TFID(),
"name", fmt.Sprintf("%s--%d", res.Name, tmplMan.RandInt)),

resource.TestCheckResourceAttr(placementGroupBySel.TFID(),
"name", fmt.Sprintf("%s--%d", res.Name, tmplMan.RandInt)),
),
},
},
})
}
69 changes: 69 additions & 0 deletions internal/e2etests/placementgroup/resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package placementgroup

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/terraform-provider-hcloud/internal/e2etests"
"github.com/hetznercloud/terraform-provider-hcloud/internal/placementgroup"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testsupport"
"github.com/hetznercloud/terraform-provider-hcloud/internal/testtemplate"
)

func TestPlacementGroupResource_Basic(t *testing.T) {
var g hcloud.PlacementGroup

res := placementgroup.NewRData(t, "basic-placement-group", "spread")
resRenamed := &placementgroup.RData{
Name: res.Name + "-renamed",
Type: "spread",
Labels: map[string]string{
"key1": "value1",
"key2": "value2",
},
}
resRenamed.SetRName(res.RName())

updated := placementgroup.NewRData(t, "basic-placement-group", "spread")
updated.SetRName(res.RName())
tmplMan := testtemplate.Manager{}
resource.Test(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(placementgroup.ResourceType, placementgroup.ByID(t, &g)),
Steps: []resource.TestStep{
{
// Create a new Placement Group using the required values
// only.
Config: tmplMan.Render(t, "testdata/r/hcloud_placement_group", res),
Check: resource.ComposeTestCheckFunc(
testsupport.CheckResourceExists(res.TFID(), placementgroup.ByID(t, &g)),
resource.TestCheckResourceAttr(res.TFID(), "name",
fmt.Sprintf("basic-placement-group--%d", tmplMan.RandInt)),
resource.TestCheckResourceAttr(res.TFID(), "type", "spread"),
),
},
{
// Try to import the newly created Placement Group
ResourceName: res.TFID(),
ImportState: true,
ImportStateVerify: true,
},
{
// Update the Placement Group created in the previous step by
// setting all optional fields and renaming the volume.
Config: tmplMan.Render(t,
"testdata/r/hcloud_placement_group", resRenamed,
),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resRenamed.TFID(), "name",
fmt.Sprintf("basic-placement-group-renamed--%d", tmplMan.RandInt)),
resource.TestCheckResourceAttr(resRenamed.TFID(), "labels.key1", "value1"),
resource.TestCheckResourceAttr(resRenamed.TFID(), "labels.key2", "value2"),
),
},
},
})
}
48 changes: 48 additions & 0 deletions internal/e2etests/server/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"crypto/sha1"
"encoding/base64"
"fmt"
"strconv"
"testing"

"github.com/hetznercloud/terraform-provider-hcloud/internal/e2etests"
"github.com/hetznercloud/terraform-provider-hcloud/internal/firewall"
"github.com/hetznercloud/terraform-provider-hcloud/internal/network"
"github.com/hetznercloud/terraform-provider-hcloud/internal/placementgroup"
"github.com/hetznercloud/terraform-provider-hcloud/internal/sshkey"
"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -429,6 +431,52 @@ func TestServerResource_Firewalls(t *testing.T) {
})
}

func TestServerResource_PlacementGroup(t *testing.T) {
var (
pg hcloud.PlacementGroup
srv hcloud.Server
)

pgRes := placementgroup.NewRData(t, "server-test", "spread")

srvRes := &server.RData{
Name: "server-placement-group",
Type: e2etests.TestServerType,
Image: e2etests.TestImage,
PlacementGroupID: pgRes.TFID() + ".id",
}
srvRes.SetRName("server-placement-group")

tmplMan := testtemplate.Manager{}

resource.Test(t, resource.TestCase{
PreCheck: e2etests.PreCheck(t),
Providers: e2etests.Providers(),
CheckDestroy: testsupport.CheckResourcesDestroyed(server.ResourceType, server.ByID(t, &srv)),
Steps: []resource.TestStep{
{
// Create a new Server using the required values
// only.
Config: tmplMan.Render(t,
"testdata/r/hcloud_placement_group", pgRes,
"testdata/r/hcloud_server", srvRes,
),
Check: resource.ComposeTestCheckFunc(
testsupport.CheckResourceExists(srvRes.TFID(), server.ByID(t, &srv)),
testsupport.CheckResourceExists(pgRes.TFID(), placementgroup.ByID(t, &pg)),
resource.TestCheckResourceAttr(srvRes.TFID(), "name",
fmt.Sprintf("server-placement-group--%d", tmplMan.RandInt)),
resource.TestCheckResourceAttr(srvRes.TFID(), "server_type", srvRes.Type),
resource.TestCheckResourceAttr(srvRes.TFID(), "image", srvRes.Image),
testsupport.CheckResourceAttrFunc(srvRes.TFID(), "placement_group_id", func() string {
return strconv.Itoa(pg.ID)
}),
),
},
},
})
}

func isRecreated(new, old *hcloud.Server) func() error {
return func() error {
if new.ID == old.ID {
Expand Down
108 changes: 108 additions & 0 deletions internal/placementgroup/data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package placementgroup

import (
"log"
"sort"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/hetznercloud/terraform-provider-hcloud/internal/hcclient"
"golang.org/x/net/context"
)

const DataSourceType = "hcloud_placement_group"

func DataSource() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceHcloudPlacementGroupRead,
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeInt,
Optional: true,
},
"name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"labels": {
Type: schema.TypeMap,
Optional: true,
},
"servers": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
},
"type": {
Type: schema.TypeString,
Optional: true,
},
"most_recent": {
Type: schema.TypeBool,
Optional: true,
},
"with_selector": {
Type: schema.TypeString,
Optional: true,
},
},
}
}

func dataSourceHcloudPlacementGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(*hcloud.Client)
if id, ok := d.GetOk("id"); ok {
i, _, err := client.PlacementGroup.GetByID(ctx, id.(int))
if err != nil {
return hcclient.ErrorToDiag(err)
}
if i == nil {
return diag.Errorf("no placement group found with id %d", id)
}
setSchema(d, i)
return nil
}
if name, ok := d.GetOk("name"); ok {
i, _, err := client.PlacementGroup.GetByName(ctx, name.(string))
if err != nil {
return hcclient.ErrorToDiag(err)
}
if i == nil {
return diag.Errorf("no placement group found with name %v", name)
}
setSchema(d, i)
return nil
}
if selector, ok := d.GetOk("with_selector"); ok {
var allPlacementGroups []*hcloud.PlacementGroup

opts := hcloud.PlacementGroupListOpts{ListOpts: hcloud.ListOpts{LabelSelector: selector.(string)}}
allPlacementGroups, err := client.PlacementGroup.AllWithOpts(ctx, opts)
if err != nil {
return hcclient.ErrorToDiag(err)
}
if len(allPlacementGroups) == 0 {
return diag.Errorf("no placement group found for selector %q", selector)
}
if len(allPlacementGroups) > 1 {
if _, ok := d.GetOk("most_recent"); !ok {
return diag.Errorf("more than one placement group found for selector %q", selector)
}
sortPlacementGroupListByCreated(allPlacementGroups)
log.Printf("[INFO] %d placement groups found for selector %q, using %d as the most recent one", len(allPlacementGroups), selector, allPlacementGroups[0].ID)
}
setSchema(d, allPlacementGroups[0])
return nil
}
return diag.Errorf("please specify an id, a name or a selector to lookup the placement group")
}

func sortPlacementGroupListByCreated(placementGroupList []*hcloud.PlacementGroup) {
sort.Slice(placementGroupList, func(i, j int) bool {
return placementGroupList[i].Created.After(placementGroupList[j].Created)
})
}
Loading

0 comments on commit e19f2d7

Please sign in to comment.