OpsLevel / terraform-provider-opslevel

Terraform provider for OpsLevel.com
https://registry.terraform.io/providers/OpsLevel/opslevel/latest/docs
MIT License
8 stars 5 forks source link

correctly handle alias slugs on aliases reconciliation #387

Closed davidbloss closed 1 month ago

davidbloss commented 1 month ago

Issues

Changelog

Remove stringAliasesToSetValue() - replaced by use of opslevel.UniqueIdentifiers() Add "slugs" (non-managed aliases) from UniqueIdentifiers to aliases passed into ReconcileAliases(). This ensures we do not try to create a existing aliases while providing the flexibility for users to include or exclude slugs in their Terraform configs.

Tophatting

With this Terraform config, aliases = []

resource "opslevel_team" "example" {
  name    = "Test Team One"
  aliases = []

  member {
    email = "david+pat@opslevel.com"
    role  = "contributor"
  }
}

resource "opslevel_service" "example" {
  name    = "Test Service One"
  aliases = []
}

resource "opslevel_infrastructure" "example" {
  schema = "Database"
  owner  = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU0Ng"
  data = jsonencode({
    name = "my-database"
  })
  aliases = []
}

Create resources with aliases set and empty, terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # opslevel_infrastructure.example will be created
  + resource "opslevel_infrastructure" "example" {
      + aliases = []
      + data    = jsonencode(
            {
              + name = "my-database"
            }
        )
      + id      = (known after apply)
      + owner   = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU0Ng"
      + schema  = "Database"
    }

  # opslevel_service.example will be created
  + resource "opslevel_service" "example" {
      + aliases = []
      + id      = (known after apply)
      + name    = "Test Service One"
    }

  # opslevel_team.example will be created
  + resource "opslevel_team" "example" {
      + aliases = []
      + id      = (known after apply)
      + name    = "Test Team One"

      + member {
          + email = "david+pat@opslevel.com"
          + role  = "contributor"
        }
    }

Plan: 3 to add, 0 to change, 0 to destroy.
opslevel_team.example: Creating...
opslevel_infrastructure.example: Creating...
opslevel_service.example: Creating...
opslevel_team.example: Creation complete after 0s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_infrastructure.example: Creation complete after 0s [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_service.example: Creation complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Add "default aliases" of Team and Service to Terraform config

resource "opslevel_team" "example" {
  name    = "Test Team One"
  aliases = ["test_team_one_2"] # <-- in aliases but not managedAliases

  member {
    email = "david+pat@opslevel.com"
    role  = "contributor"
  }
}

resource "opslevel_service" "example" {
  name    = "Test Service One"
  aliases = ["test_service_one_2"] # <-- in aliases but not managedAliases
}

resource "opslevel_infrastructure" "example" {
  schema = "Database"
  owner  = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU0Ng"
  data = jsonencode({
    name = "my-database"
  })
  aliases = [] # no defaults created here
}

Update team and service aliases, terraform apply

opslevel_infrastructure.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_team.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_service.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # opslevel_service.example will be updated in-place
  ~ resource "opslevel_service" "example" {
      ~ aliases = [
          + "test_service_one_2",
        ]
        id      = "Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI"
        name    = "Test Service One"
    }

  # opslevel_team.example will be updated in-place
  ~ resource "opslevel_team" "example" {
      ~ aliases = [
          + "test_team_one_2",
        ]
        id      = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ"
        name    = "Test Team One"

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 2 to change, 0 to destroy.
opslevel_team.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_service.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]
opslevel_team.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_service.example: Modifications complete after 2s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]

Apply complete! Resources: 0 added, 2 changed, 0 destroyed.

Update config, replace "slugs" from aliases with new aliases

resource "opslevel_team" "example" {
  name    = "Test Team One"
  aliases = ["fancy_team"]

  member {
    email = "david+pat@opslevel.com"
    role  = "contributor"
  }
}

resource "opslevel_service" "example" {
  name    = "Test Service One"
  aliases = ["fancy_service"]
}

resource "opslevel_infrastructure" "example" {
  schema = "Database"
  owner  = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU0Ng"
  data = jsonencode({
    name = "my-database"
  })
  aliases = ["fancy_infra"]
}

Update aliases for all resources, terraform apply

opslevel_infrastructure.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_service.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]
opslevel_team.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # opslevel_infrastructure.example will be updated in-place
  ~ resource "opslevel_infrastructure" "example" {
      ~ aliases = [
          + "fancy_infra",
        ]
        id      = "Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU"
        # (3 unchanged attributes hidden)
    }

  # opslevel_service.example will be updated in-place
  ~ resource "opslevel_service" "example" {
      ~ aliases = [
          - "test_service_one_2",
          + "fancy_service",
        ]
        id      = "Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI"
        name    = "Test Service One"
    }

  # opslevel_team.example will be updated in-place
  ~ resource "opslevel_team" "example" {
      ~ aliases = [
          - "test_team_one_2",
          + "fancy_team",
        ]
        id      = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ"
        name    = "Test Team One"

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 3 to change, 0 to destroy.
opslevel_infrastructure.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_team.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_service.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]
opslevel_team.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_infrastructure.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_service.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]

Apply complete! Resources: 0 added, 3 changed, 0 destroyed.

Update config, aliases omitted

resource "opslevel_team" "example" {
  name = "Test Team One"
  # aliases = ["fancy_team"]

  member {
    email = "david+pat@opslevel.com"
    role  = "contributor"
  }
}

resource "opslevel_service" "example" {
  name = "Test Service One"
  # aliases = ["fancy_service"]
}

resource "opslevel_infrastructure" "example" {
  schema = "Database"
  owner  = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU0Ng"
  data = jsonencode({
    name = "my-database"
  })
  # aliases = ["fancy_infra"]
}

Remove aliases (set to null)

opslevel_infrastructure.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_team.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_service.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # opslevel_infrastructure.example will be updated in-place
  ~ resource "opslevel_infrastructure" "example" {
      - aliases = [
          - "fancy_infra",
        ] -> null
        id      = "Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU"
        # (3 unchanged attributes hidden)
    }

  # opslevel_service.example will be updated in-place
  ~ resource "opslevel_service" "example" {
      - aliases = [
          - "fancy_service",
        ] -> null
        id      = "Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI"
        name    = "Test Service One"
    }

  # opslevel_team.example will be updated in-place
  ~ resource "opslevel_team" "example" {
      - aliases = [
          - "fancy_team",
        ] -> null
        id      = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ"
        name    = "Test Team One"

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 3 to change, 0 to destroy.
opslevel_infrastructure.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_team.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_service.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]
opslevel_team.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTU4NQ]
opslevel_infrastructure.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvRW50aXR5T2JqZWN0LzI3NzQ1MzU]
opslevel_service.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvU2VydmljZS8xMjU5MjI]

Apply complete! Resources: 0 added, 3 changed, 0 destroyed.