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

feat: add resource and data source for MAAS zones #258

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

peterctl
Copy link

@peterctl peterctl commented Feb 6, 2025

Managing MAAS availability zones through Terraform is a missing feature that I needed for a project.

Acceptance test output for the zone resource and datasource can be seen here:

$ make testacc TESTARGS="-run Zone"
TF_ACC=1 go test $(go list ./... | grep -v 'vendor') -v -run Zone -timeout 120m -parallel=4
?       terraform-provider-maas [no test files]
?       terraform-provider-maas/maas/testutils  [no test files]
=== RUN   TestAccDataSourceMaasZone_basic
=== PAUSE TestAccDataSourceMaasZone_basic
=== RUN   TestAccResourceMaasZone_basic
=== PAUSE TestAccResourceMaasZone_basic
=== CONT  TestAccDataSourceMaasZone_basic
=== CONT  TestAccResourceMaasZone_basic
--- PASS: TestAccDataSourceMaasZone_basic (2.61s)
--- PASS: TestAccResourceMaasZone_basic (3.37s)
PASS
ok      terraform-provider-maas/maas    3.386s

@peterctl peterctl changed the title feat: Add resource and data source for MAAS zones. feat: add resource and data source for MAAS zones Feb 6, 2025
Copy link

github-actions bot commented Feb 6, 2025

Everyone contributing to this PR have now signed the CLA. Thanks!

@skatsaounis skatsaounis linked an issue Feb 7, 2025 that may be closed by this pull request
Copy link
Collaborator

@skatsaounis skatsaounis left a comment

Choose a reason for hiding this comment

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

Hi @peterctl

Awesome work. Thank you for your first (I am looking forward to more) contribution to the provider. I tried to be exhaustive with comments to help you tackle the issues faster. Apart from 1-2 things that we can debate, rest of the suggested changes are needed.

Please take a look and let me know to re-review.

PS. Please include examples for data-source, resource and import and run make generate_docs to produce Markdown for the new resources. When you are done, please include the files to your PR too. Naming of directories and files is important for the tool to pick up the examples.

Reference:

🍻

Makefile Outdated Show resolved Hide resolved
if err != nil {
return diag.FromErr(err)
}
d.SetId(zone.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about setting as terraform ID the database ID of the zone instead of the name? In addition, line 41 "id": fmt.Sprintf("%v", zone.ID), is not needed

Suggested change
d.SetId(zone.Name)
d.SetId(fmt.Sprintf("%v", zone.ID))

maas/resource_maas_zone.go Outdated Show resolved Hide resolved
client := meta.(*client.Client)

params := getZoneParams(d)
zone, err := findZone(client, params.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Trying to find if zone already exists in MAAS here costs an extra API call to MAAS. It is accepted to blindly try create the zone as the intention is that Terraform is managing it. If it already exists, MAAS will throw an error and this will be propagated to gomaasclient, then from the client to terraform provider and finally being presented to user as part of diagnostic errors. Then the user can import the zone to Terraform and continue its management with Terraform.

Let's simplify the function. Resource pool is again a good example as the implementation is more or less identical.

return diag.FromErr(err)
}
}
d.SetId(zone.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

As said earlier, let's save the database ID as a Terraform resource ID

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 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This can be simplified to

Suggested change
if err := client.Zone.Delete(d.Id()); err != nil {
return client.Zone.Delete(id))

given that id is calculated like the previous suggestion

maas/resource_maas_zone_test.go Show resolved Hide resolved
maas/resource_maas_zone_test.go Show resolved Hide resolved
}

conn := testutils.TestAccProvider.Meta().(*client.Client)
gotZone, err := conn.Zone.Get(rs.Primary.ID)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is also needed if we are to save the database ID as the Terraform resource ID

                id, err := strconv.Atoi(rs.Primary.ID)
		if err != nil {
			return err
		}

// 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 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if response != nil && response.Name == rs.Primary.ID {
if response != nil && response.ID == rs.Primary.ID {

@peterctl
Copy link
Author

peterctl commented Feb 7, 2025

Hi @skatsaounis,

Thank you for the thorough review! It's my first time working directly with Terraform providers so the feedback is very useful to me. I have applied most of the requested changes already, but I have an issue with setting the database ID as the Terraform ID instead of the name. The reason for this is that the gomaasclient will only accept names as arguments to Get, Update and Destroy. If I try using the numeric database ID, I get 404 errors. I wrote this quick snippet to show you what I get.

// client_tests.go
package main

import (
        "fmt"
        "os"
        "terraform-provider-maas/maas"
)

func playground() error {
        config := maas.Config{
                APIURL: os.Getenv("MAAS_API_URL"),
                APIKey: os.Getenv("MAAS_API_KEY"),
                ApiVersion: "2.0",
        }

        client, err := config.Client()
        if err != nil {
                return err
        }

        zoneByName, errByName := client.Zone.Get("az1")
        zoneByID, errByID := client.Zone.Get("3")
        fmt.Printf("zoneByName: %+v\n", zoneByName)
        fmt.Printf("errByName: %+v\n", errByName)
        fmt.Printf("zoneByID: %+v\n", zoneByID)
        fmt.Printf("errByID: %+v\n", errByID)

        return nil
}

func main() {
        err := playground()
        fmt.Println(err)
}
$ go run client_playground.go
zoneByName: &{Name:az1 Description: ResourceURI:/MAAS/api/2.0/zones/az1/ ID:3}
errByName: <nil>
zoneByID: &{Name: Description: ResourceURI: ID:0}
errByID: ServerError: 404 Not Found (No Zone matches the given query.)

For clarity, the ID and name do match as can be seen in zoneByName.

If we want to use the database ID as the Terraform ID, I think we would need to rely on findZone to get the zone entity, then using zone.Name as the argument for the client, but this solution would add MAAS API calls any time we need to find the zone name from the database ID. For example:

params := getZoneParams(d)
zone, err := getZone(d.Id())
if err != nil {
        return diag.FromErr(err)
}

zone, err = client.Zone.Update(zone.Name, params)
if err != nil {
        return diag.FromErr(err)
}

Please advise on how to proceed with name vs ID. Is there a better approach?

@peterctl peterctl requested a review from skatsaounis February 10, 2025 18:54
@skatsaounis
Copy link
Collaborator

Hi @peterctl ,

The fact that MAAS API v2 is using the zone name instead of the database ID is problematic by itself. Since we cannot change the contract, it is what it is. With the v3 MAAS API we aim to solve this issue among others, but it is not there yet. As you spotted, the findZone is what you need here. We may add an extra API call, but it is a cheap one since zones response is small and we should expect something in the magnitude of tens or hundreds. What is the benefit though? Imagine that someone outside terraform, updated the zone name but there are already existing machines assigned to that zone. What you would expect from Terraform is to identify that change and try to update the same zone, instead of creating a new record. One of the weird behaviors of the API v2 is that you can use /zones/zone_name/update?name=new_zone_name, but again it is what it is.

Please proceed with the database ID as the Terraform resource ID.

In addition, we have to add a field of machines, like we do with tags so that we can assign the zone to machines with Terraform. I forgot to tell you in the first review. An alternative would be a new resource maas_machine_zone_assignment or maas_zone_machine_assignment or another alternative. I am undecided about which one is the best approach. But we can start with the machines field like tags to introduce a similar mechanism.

@skatsaounis skatsaounis reopened this Feb 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing zone resource configuration
2 participants