diff --git a/docs/data-sources/resource_pool.md b/docs/data-sources/resource_pool.md new file mode 100644 index 0000000..2dc1542 --- /dev/null +++ b/docs/data-sources/resource_pool.md @@ -0,0 +1,36 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "maas_resource_pool Data Source - terraform-provider-maas" +subcategory: "" +description: |- + Provides details about an existing MAAS resource pool. +--- + +# maas_resource_pool (Data Source) + +Provides details about an existing MAAS resource pool. + +## Example Usage + +```terraform +resource "maas_resource_pool" "test_resource_pool" { + description = "Test description" + name = "test-resource-pool" +} + +data "maas_resource_pool" "test_resource_pool" { + name = maas_resource_pool.test_resource_pool.name +} +``` + + +## Schema + +### Required + +- `name` (String) The name of the resource pool. + +### Read-Only + +- `description` (String) The description of the resource pool. +- `id` (String) The ID of this resource. diff --git a/docs/resources/resource_pool.md b/docs/resources/resource_pool.md new file mode 100644 index 0000000..c428068 --- /dev/null +++ b/docs/resources/resource_pool.md @@ -0,0 +1,44 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "maas_resource_pool Resource - terraform-provider-maas" +subcategory: "" +description: |- + Provides a resource to manage MAAS resource pools. +--- + +# maas_resource_pool (Resource) + +Provides a resource to manage MAAS resource pools. + +## Example Usage + +```terraform +resource "maas_resource_pool" "test_resource_pool" { + description = "Test description" + name = "test-resource-pool" +} +``` + + +## Schema + +### Required + +- `name` (String) The name of the resource pool. + +### Optional + +- `description` (String) The description of the resource pool. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +# Resource pools can be imported using their ID or name. e.g. +$ terraform import maas_resource_pool.test_resource_pool test-resource-pool +``` diff --git a/examples/data-sources/maas_resource_pool/data-source.tf b/examples/data-sources/maas_resource_pool/data-source.tf new file mode 100644 index 0000000..1342bd6 --- /dev/null +++ b/examples/data-sources/maas_resource_pool/data-source.tf @@ -0,0 +1,8 @@ +resource "maas_resource_pool" "test_resource_pool" { + description = "Test description" + name = "test-resource-pool" +} + +data "maas_resource_pool" "test_resource_pool" { + name = maas_resource_pool.test_resource_pool.name +} diff --git a/examples/resources/maas_resource_pool/import.sh b/examples/resources/maas_resource_pool/import.sh new file mode 100644 index 0000000..476fcc9 --- /dev/null +++ b/examples/resources/maas_resource_pool/import.sh @@ -0,0 +1,2 @@ +# Resource pools can be imported using their ID or name. e.g. +$ terraform import maas_resource_pool.test_resource_pool test-resource-pool diff --git a/examples/resources/maas_resource_pool/resource.tf b/examples/resources/maas_resource_pool/resource.tf new file mode 100644 index 0000000..b286e69 --- /dev/null +++ b/examples/resources/maas_resource_pool/resource.tf @@ -0,0 +1,4 @@ +resource "maas_resource_pool" "test_resource_pool" { + description = "Test description" + name = "test-resource-pool" +} diff --git a/go.mod b/go.mod index bfba760..bfd393b 100644 --- a/go.mod +++ b/go.mod @@ -86,3 +86,5 @@ require ( google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/maas/gomaasclient v0.1.0 => github.com/skatsaounis/gomaasclient v0.0.0-20231225220956-4c6bcbe1988d diff --git a/go.sum b/go.sum index 2f8d2e4..b129a54 100644 --- a/go.sum +++ b/go.sum @@ -206,8 +206,6 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/lunixbochs/vtclean v0.0.0-20160125035106-4fbf7632a2c6/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/maas/gomaasclient v0.1.0 h1:YGTsEiBB7mskJ+PKvxwo68z/dzzwFNIz0CYvrFrOxxk= -github.com/maas/gomaasclient v0.1.0/go.mod h1:egIWxFQv8Q4uSaLJVba30FmI55Vg8CMzUo0lw+VpjKI= github.com/masterzen/azure-sdk-for-go v3.2.0-beta.0.20161014135628-ee4f0065d00c+incompatible/go.mod h1:mf8fjOu33zCqxUjuiU3I8S1lJMyEAlH+0F2+M5xl3hE= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= @@ -258,6 +256,8 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/skatsaounis/gomaasclient v0.0.0-20231225220956-4c6bcbe1988d h1:Jz2NTGfDYdAygkmL1DHDjw0hRl9SPKbxzroo17sPdtg= +github.com/skatsaounis/gomaasclient v0.0.0-20231225220956-4c6bcbe1988d/go.mod h1:egIWxFQv8Q4uSaLJVba30FmI55Vg8CMzUo0lw+VpjKI= github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= diff --git a/maas/data_source_maas_device_test.go b/maas/data_source_maas_device_test.go index 2b7fbb4..a617734 100644 --- a/maas/data_source_maas_device_test.go +++ b/maas/data_source_maas_device_test.go @@ -5,6 +5,7 @@ import ( "terraform-provider-maas/maas/testutils" "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/maas/gomaasclient/entity" ) @@ -13,8 +14,8 @@ func TestAccDataSourceMaasDevice_basic(t *testing.T) { var device entity.Device description := "Test description" - domain := "test-data-domain" - hostname := "test-data-device" + domain := acctest.RandomWithPrefix("tf-domain-") + hostname := acctest.RandomWithPrefix("tf-device-") zone := "default" mac_address := "12:23:45:67:89:fa" diff --git a/maas/data_source_maas_resource_pool.go b/maas/data_source_maas_resource_pool.go new file mode 100644 index 0000000..8f1d7cb --- /dev/null +++ b/maas/data_source_maas_resource_pool.go @@ -0,0 +1,46 @@ +package maas + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/maas/gomaasclient/client" +) + +func dataSourceMaasResourcePool() *schema.Resource { + return &schema.Resource{ + Description: "Provides details about an existing MAAS resource pool.", + ReadContext: dataSourceResourcePoolRead, + + Schema: map[string]*schema.Schema{ + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the resource pool.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the resource pool.", + }, + }, + } +} + +func dataSourceResourcePoolRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*client.Client) + + resourcePool, err := getResourcePool(client, d.Get("name").(string)) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(fmt.Sprintf("%v", resourcePool.ID)) + + d.Set("description", resourcePool.Description) + d.Set("name", resourcePool.Name) + + return nil +} diff --git a/maas/data_source_maas_resource_pool_test.go b/maas/data_source_maas_resource_pool_test.go new file mode 100644 index 0000000..6137837 --- /dev/null +++ b/maas/data_source_maas_resource_pool_test.go @@ -0,0 +1,47 @@ +package maas_test + +import ( + "fmt" + "terraform-provider-maas/maas/testutils" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/maas/gomaasclient/entity" +) + +func TestAccDataSourceMaasResourcePool_basic(t *testing.T) { + + var resourcePool entity.ResourcePool + description := "Test description" + name := acctest.RandomWithPrefix("tf-resource-pool-") + + checks := []resource.TestCheckFunc{ + testAccMaasResourcePoolCheckExists("data.maas_resource_pool.test", &resourcePool), + resource.TestCheckResourceAttr("data.maas_resource_pool.test", "description", description), + resource.TestCheckResourceAttr("data.maas_resource_pool.test", "name", name), + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testutils.PreCheck(t) }, + Providers: testutils.TestAccProviders, + CheckDestroy: testAccCheckMaasResourcePoolDestroy, + ErrorCheck: func(err error) error { return err }, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceMaasResourcePool(description, name), + Check: resource.ComposeTestCheckFunc(checks...), + }, + }, + }) +} + +func testAccDataSourceMaasResourcePool(description string, name string) string { + return fmt.Sprintf(` +%s + +data "maas_resource_pool" "test" { + name = maas_resource_pool.test.name +} +`, testAccMaasResourcePool(description, name)) +} diff --git a/maas/provider.go b/maas/provider.go index 04fe2e4..bdd4515 100644 --- a/maas/provider.go +++ b/maas/provider.go @@ -61,6 +61,7 @@ func Provider() *schema.Provider { "maas_block_device": resourceMaasBlockDevice(), "maas_tag": resourceMaasTag(), "maas_user": resourceMaasUser(), + "maas_resource_pool": resourceMaasResourcePool(), }, DataSourcesMap: map[string]*schema.Resource{ "maas_fabric": dataSourceMaasFabric(), @@ -69,6 +70,7 @@ func Provider() *schema.Provider { "maas_machine": dataSourceMaasMachine(), "maas_network_interface_physical": dataSourceMaasNetworkInterfacePhysical(), "maas_device": dataSourceMaasDevice(), + "maas_resource_pool": dataSourceMaasResourcePool(), }, ConfigureContextFunc: providerConfigure, } diff --git a/maas/resource_maas_device_test.go b/maas/resource_maas_device_test.go index ab57653..1631c84 100644 --- a/maas/resource_maas_device_test.go +++ b/maas/resource_maas_device_test.go @@ -6,6 +6,7 @@ import ( "terraform-provider-maas/maas/testutils" "testing" + "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/maas/gomaasclient/client" @@ -16,8 +17,8 @@ func TestAccResourceMaasDevice_basic(t *testing.T) { var device entity.Device description := "Test description" - domain := "test-domain" - hostname := "test-device" + domain := acctest.RandomWithPrefix("tf-domain-") + hostname := acctest.RandomWithPrefix("tf-device-") zone := "default" mac_address := "12:23:45:67:89:de" diff --git a/maas/resource_maas_resource_pool.go b/maas/resource_maas_resource_pool.go new file mode 100644 index 0000000..e9e3a4e --- /dev/null +++ b/maas/resource_maas_resource_pool.go @@ -0,0 +1,135 @@ +package maas + +import ( + "context" + "fmt" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/maas/gomaasclient/client" + "github.com/maas/gomaasclient/entity" +) + +func resourceMaasResourcePool() *schema.Resource { + return &schema.Resource{ + Description: "Provides a resource to manage MAAS resource pools.", + CreateContext: resourceResourcePoolCreate, + ReadContext: resourceResourcePoolRead, + UpdateContext: resourceResourcePoolUpdate, + DeleteContext: resourceResourcePoolDelete, + Importer: &schema.ResourceImporter{ + StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + client := meta.(*client.Client) + resourcePool, err := getResourcePool(client, d.Id()) + if err != nil { + return nil, err + } + d.SetId(fmt.Sprintf("%v", resourcePool.ID)) + return []*schema.ResourceData{d}, nil + }, + }, + + Schema: map[string]*schema.Schema{ + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description of the resource pool.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the resource pool.", + }, + }, + } +} + +func resourceResourcePoolCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*client.Client) + + resourcePoolParams := entity.ResourcePoolParams{ + Description: d.Get("description").(string), + Name: d.Get("name").(string), + } + + resourcePool, err := client.ResourcePools.Create(&resourcePoolParams) + if err != nil { + return diag.FromErr(err) + } + d.SetId(fmt.Sprintf("%v", resourcePool.ID)) + + return resourceResourcePoolRead(ctx, d, meta) +} + +func resourceResourcePoolUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*client.Client) + + id, err := strconv.Atoi(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + resourcePoolParams := entity.ResourcePoolParams{ + Description: d.Get("description").(string), + Name: d.Get("name").(string), + } + + resourcePool, err := client.ResourcePool.Update(id, &resourcePoolParams) + if err != nil { + return diag.FromErr(err) + } + d.SetId(fmt.Sprintf("%v", resourcePool.ID)) + + return resourceResourcePoolRead(ctx, d, meta) +} + +func resourceResourcePoolDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*client.Client) + + id, err := strconv.Atoi(d.Id()) + if err != nil { + return diag.FromErr(err) + } + return diag.FromErr(client.ResourcePool.Delete(id)) +} + +func resourceResourcePoolRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*client.Client) + + resourcePool, err := getResourcePool(client, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(fmt.Sprintf("%v", resourcePool.ID)) + + d.Set("description", resourcePool.Description) + d.Set("name", resourcePool.Name) + + return nil +} + +func getResourcePool(client *client.Client, identifier string) (*entity.ResourcePool, error) { + resourcePool, err := findResourcePool(client, identifier) + if err != nil { + return nil, err + } + if resourcePool == nil { + return nil, fmt.Errorf("resource pool (%s) was not found", identifier) + } + return resourcePool, nil +} + +func findResourcePool(client *client.Client, identifier string) (*entity.ResourcePool, error) { + resourcePools, err := client.ResourcePools.Get() + if err != nil { + return nil, err + } + for _, d := range resourcePools { + if fmt.Sprintf("%v", d.ID) == identifier || d.Name == identifier { + return &d, nil + } + } + return nil, nil +} diff --git a/maas/resource_maas_resource_pool_test.go b/maas/resource_maas_resource_pool_test.go new file mode 100644 index 0000000..672f4e5 --- /dev/null +++ b/maas/resource_maas_resource_pool_test.go @@ -0,0 +1,135 @@ +package maas_test + +import ( + "fmt" + "strconv" + "strings" + "terraform-provider-maas/maas/testutils" + "testing" + + "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/maas/gomaasclient/client" + "github.com/maas/gomaasclient/entity" +) + +func TestAccResourceMaasResourcePool_basic(t *testing.T) { + + var resourcePool entity.ResourcePool + description := "Test description" + name := acctest.RandomWithPrefix("tf-resource-pool-") + + checks := []resource.TestCheckFunc{ + testAccMaasResourcePoolCheckExists("maas_resource_pool.test", &resourcePool), + resource.TestCheckResourceAttr("maas_resource_pool.test", "description", description), + resource.TestCheckResourceAttr("maas_resource_pool.test", "name", name), + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testutils.PreCheck(t) }, + Providers: testutils.TestAccProviders, + CheckDestroy: testAccCheckMaasResourcePoolDestroy, + ErrorCheck: func(err error) error { return err }, + Steps: []resource.TestStep{ + { + Config: testAccMaasResourcePool(description, name), + Check: resource.ComposeTestCheckFunc(checks...), + }, + // Test import using ID + { + ResourceName: "maas_resource_pool.test", + ImportState: true, + ImportStateVerify: true, + }, + // Test import using name + { + ResourceName: "maas_resource_pool.test", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources["maas_resource_pool.test"] + if !ok { + return "", fmt.Errorf("resource not found: %s", "maas_resource_pool.test") + } + + if rs.Primary.ID == "" { + return "", fmt.Errorf("resource id not set") + } + return rs.Primary.Attributes["name"], nil + }, + }, + }, + }) +} + +func testAccMaasResourcePoolCheckExists(rn string, resourcePool *entity.ResourcePool) 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) + id, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return err + } + gotResourcePool, err := conn.ResourcePool.Get(id) + if err != nil { + return fmt.Errorf("error getting resource pool: %s", err) + } + + *resourcePool = *gotResourcePool + + return nil + } +} + +func testAccMaasResourcePool(description string, name string) string { + return fmt.Sprintf(` +resource "maas_resource_pool" "test" { + name = "%s" + description = "%s" +} +`, name, description) +} + +func testAccCheckMaasResourcePoolDestroy(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_resource_pool + // is destroyed + for _, rs := range s.RootModule().Resources { + if rs.Type != "maas_resource_pool" { + continue + } + + // Retrieve our maas_resource_pool by referencing it's state ID for API lookup + id, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return err + } + response, err := conn.ResourcePool.Get(id) + if err == nil { + if response != nil && response.ID == id { + return fmt.Errorf("MAAS Resource pool (%s) still exists.", rs.Primary.ID) + } + + return nil + } + + // If the error is equivalent to 404 not found, the maas_resource_pool is destroyed. + // Otherwise return the error + if !strings.Contains(err.Error(), "404 Not Found") { + return err + } + } + + return nil +}