Skip to content

Commit

Permalink
feat: add resource and data source for MAAS zones
Browse files Browse the repository at this point in the history
  • Loading branch information
peterctl committed Feb 6, 2025
1 parent 7d90d64 commit 23a0d92
Show file tree
Hide file tree
Showing 6 changed files with 360 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ NAMESPACE=canonical
NAME=maas
PROVIDER_NAME=terraform-provider-${NAME}
BINARY=terraform-provider-${NAME}
VERSION=1.0.1
VERSION=2.4.0

OS?=$$(go env GOOS)
ARCH?=$$(go env GOARCH)
Expand Down
50 changes: 50 additions & 0 deletions maas/data_source_maas_zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package maas

import (
"context"
"fmt"

"github.com/canonical/gomaasclient/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceMaasZone() *schema.Resource {
return &schema.Resource{
Description: "Provides details about an existing MAAS zone.",
ReadContext: dataSourceZoneRead,

Schema: map[string]*schema.Schema{
"description": {
Type: schema.TypeString,
Computed: true,
Description: "A brief description of the zone.",
},
"name": {
Type: schema.TypeString,
Required: true,
Description: "The zone's name.",
},
},
}
}

func dataSourceZoneRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

zone, err := getZone(client, d.Get("name").(string))
if err != nil {
return diag.FromErr(err)
}
d.SetId(zone.Name)
tfstate := map[string]interface{}{
"id": fmt.Sprintf("%v", zone.ID),
"name": zone.Name,
"description": zone.Description,
}
if err := setTerraformState(d, tfstate); err != nil {
return diag.FromErr(err)
}

return nil
}
47 changes: 47 additions & 0 deletions maas/data_source_maas_zone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package maas_test

import (
"fmt"
"terraform-provider-maas/maas/testutils"
"testing"

"github.com/canonical/gomaasclient/entity"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourceMaasZone_basic(t *testing.T) {

var zone entity.Zone
description := "Test description"
name := acctest.RandomWithPrefix("tf-zone-")

checks := []resource.TestCheckFunc{
testAccMaasZoneCheckExists("maas_zone.test", &zone),
resource.TestCheckResourceAttr("data.maas_zone.test", "description", description),
resource.TestCheckResourceAttr("data.maas_zone.test", "name", name),
}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testutils.PreCheck(t, nil) },
Providers: testutils.TestAccProviders,
CheckDestroy: testAccCheckMaasZoneDestroy,
ErrorCheck: func(err error) error { return err },
Steps: []resource.TestStep{
{
Config: testAccDataSourceMaasZone(description, name),
Check: resource.ComposeTestCheckFunc(checks...),
},
},
})
}

func testAccDataSourceMaasZone(description string, name string) string {
return fmt.Sprintf(`
%s
data "maas_zone" "test" {
name = maas_zone.test.name
}
`, testAccMaasZone(name, description))
}
2 changes: 2 additions & 0 deletions maas/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func Provider() *schema.Provider {
"maas_tag": resourceMaasTag(),
"maas_user": resourceMaasUser(),
"maas_resource_pool": resourceMaasResourcePool(),
"maas_zone": resourceMaasZone(),
},
DataSourcesMap: map[string]*schema.Resource{
"maas_boot_source": dataSourceMaasBootSource(),
Expand All @@ -76,6 +77,7 @@ func Provider() *schema.Provider {
"maas_device": dataSourceMaasDevice(),
"maas_resource_pool": dataSourceMaasResourcePool(),
"maas_rack_controller": dataSourceMaasRackController(),
"maas_zone": dataSourceMaasZone(),
},
ConfigureContextFunc: providerConfigure,
}
Expand Down
142 changes: 142 additions & 0 deletions maas/resource_maas_zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package maas

import (
"context"
"fmt"

"github.com/canonical/gomaasclient/client"
"github.com/canonical/gomaasclient/entity"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceMaasZone() *schema.Resource {
return &schema.Resource{
Description: "Provides a resource to manage MAAS zones.",
CreateContext: resourceZoneCreate,
ReadContext: resourceZoneRead,
UpdateContext: resourceZoneUpdate,
DeleteContext: resourceZoneDelete,
Importer: &schema.ResourceImporter{
StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
client := meta.(*client.Client)
zone, err := getZone(client, d.Id())
if err != nil {
return nil, err
}
tfState := map[string]interface{}{
"name": zone.Name,
"description": zone.Description,
}
if err := setTerraformState(d, tfState); err != nil {
return nil, err
}
return []*schema.ResourceData{d}, nil
},
},

Schema: map[string]*schema.Schema{
"description": {
Type: schema.TypeString,
Optional: true,
Description: "A brief description of the new zone.",
},
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the new zone.",
},
},
}
}

func resourceZoneRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

zone, err := client.Zone.Get(d.Id())
if err != nil {
return diag.FromErr(err)
}

tfstate := map[string]any{
"name": zone.Name,
"description": zone.Description,
}

if err := setTerraformState(d, tfstate); err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceZoneCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

params := getZoneParams(d)
zone, err := findZone(client, params.Name)
if err != nil {
return diag.FromErr(err)
}
if zone == nil {
zone, err = client.Zones.Create(params)
if err != nil {
return diag.FromErr(err)
}
}
d.SetId(zone.Name)

return resourceZoneUpdate(ctx, d, meta)
}

func resourceZoneUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

params := getZoneParams(d)
if _, err := client.Zone.Update(d.Id(), params); err != nil {
return diag.FromErr(err)
}

return resourceZoneRead(ctx, d, meta)
}

func resourceZoneDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*client.Client)

if err := client.Zone.Delete(d.Id()); err != nil {
return diag.FromErr(err)
}

return nil
}

func getZoneParams(d *schema.ResourceData) *entity.ZoneParams {
return &entity.ZoneParams{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
}
}

func findZone(client *client.Client, identifier string) (*entity.Zone, error) {
zones, err := client.Zones.Get()
if err != nil {
return nil, err
}
for _, z := range zones {
if fmt.Sprintf("%v", z.ID) == identifier || z.Name == identifier {
return &z, nil
}
}
return nil, nil
}

func getZone(client *client.Client, identifier string) (*entity.Zone, error) {
zone, err := findZone(client, identifier)
if err != nil {
return nil, err
}
if zone == nil {
return nil, fmt.Errorf("zone (%s) was not found", identifier)
}
return zone, nil
}
118 changes: 118 additions & 0 deletions maas/resource_maas_zone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package maas_test

import (
"fmt"
"strings"
"terraform-provider-maas/maas/testutils"
"testing"

"github.com/canonical/gomaasclient/client"
"github.com/canonical/gomaasclient/entity"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/stretchr/testify/assert"
)

func TestAccResourceMaasZone_basic(t *testing.T) {

var zone entity.Zone
name := acctest.RandomWithPrefix("tf-zone-")
description := "Test description"

checks := []resource.TestCheckFunc{
testAccMaasZoneCheckExists("maas_zone.test", &zone),
resource.TestCheckResourceAttr("maas_zone.test", "name", name),
resource.TestCheckResourceAttr("maas_zone.test", "description", description),
}

resource.ParallelTest(t, resource.TestCase{
Providers: testutils.TestAccProviders,
CheckDestroy: testAccCheckMaasZoneDestroy,
ErrorCheck: func(err error) error { return err },
Steps: []resource.TestStep{
{
Config: testAccMaasZone(name, description),
Check: resource.ComposeTestCheckFunc(checks...),
},
// Test import using name
{
ResourceName: "maas_zone.test",
ImportState: true,
ImportStateCheck: func(is []*terraform.InstanceState) error {
var zone *terraform.InstanceState
if len(is) != 1 {
return fmt.Errorf("expected 1 state: %#v", t)
}
zone = is[0]
assert.Equal(t, zone.Attributes["name"], name)
assert.Equal(t, zone.Attributes["description"], description)
return nil
},
},
},
})
}

func testAccMaasZoneCheckExists(rn string, zone *entity.Zone) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[rn]
if !ok {
return fmt.Errorf("resource not found: %s\n %#v", rn, s.RootModule().Resources)
}

if rs.Primary.ID == "" {
return fmt.Errorf("resource id not set")
}

conn := testutils.TestAccProvider.Meta().(*client.Client)
gotZone, err := conn.Zone.Get(rs.Primary.ID)
if err != nil {
return fmt.Errorf("error getting zone: %s", err)
}

*zone = *gotZone

return nil
}
}

func testAccMaasZone(name string, description string) string {
return fmt.Sprintf(`
resource "maas_zone" "test" {
name = "%s"
description = "%s"
}
`, name, description)
}

func testAccCheckMaasZoneDestroy(s *terraform.State) error {
// retrieve the connection established in Provider configuration
conn := testutils.TestAccProvider.Meta().(*client.Client)

// loop through the resources in state, verifying each maas_zone
// is destroyed
for _, rs := range s.RootModule().Resources {
if rs.Type != "maas_zone" {
continue
}

// Retrieve our maas_zone by referencing it's state ID for API lookup
response, err := conn.Zone.Get(rs.Primary.ID)
if err == nil {
if response != nil && response.Name == rs.Primary.ID {
return fmt.Errorf("MAAS Zone (%s) still exists.", rs.Primary.ID)
}

return nil
}

// If the error is equivalent to 404 not found, the maas_zone is destroyed.
// Otherwise return the error
if !strings.Contains(err.Error(), "404 Not Found") {
return err
}
}

return nil
}

0 comments on commit 23a0d92

Please sign in to comment.