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

fix setting of opslevel_filter fields, add config validation #376

Closed davidbloss closed 1 month ago

davidbloss commented 1 month ago

Issues

https://github.com/OpsLevel/team-platform/issues/391

Changelog

✅ - relies on updates in this opslevel-go PR

Fix the where case_insensitive and case_sensitive were being set - they should be left null by default.

Tophatting

With this Terraform config (invalid):

resource "opslevel_filter" "example" {
  name       = "foobarbaz"
  connective = "and"
  predicate {
    key              = "language"
    type             = "contains"
    value            = "bar"
    case_sensitive   = true
    case_insensitive = true
  }
}

Run terraform validate

│ Error: Invalid Attribute Combination
│ 
│   with opslevel_filter.example,
│   on main.tf line 8, in resource "opslevel_filter" "example":
│    8:     case_sensitive   = true
│ 
│ Attribute "predicate[0].case_insensitive" cannot be specified when "predicate[0].case_sensitive" is specified
╵
╷
│ Error: Invalid Attribute Combination
│ 
│   with opslevel_filter.example,
│   on main.tf line 9, in resource "opslevel_filter" "example":
│    9:     case_insensitive = true
│ 
│ Attribute "predicate[0].case_sensitive" cannot be specified when "predicate[0].case_insensitive" is specified

Update Terraform config (valid):

resource "opslevel_filter" "example" {
  name       = "foobarbaz"
  connective = "and"
  predicate {
    key              = "language"
    type             = "contains"
    value            = "bar"
    case_sensitive   = true
    case_insensitive = null
  }
}

Run terraform validate

Success! The configuration is valid.

With this Terraform config

resource "opslevel_filter" "example" {
  name       = "foobarbaz1"
  connective = "and"
  predicate {
    key              = "language"
    type             = "contains"
    value            = "barbaz"
    case_sensitive   = true
    case_insensitive = null
  }
  predicate {
    key   = "tier_index"
    type  = "less_than_or_equal_to"
    value = 3
  }
  predicate {
    key   = "language"
    type  = "exists"
    value = null
  }
}

resource "opslevel_check_tool_usage" "example" {
  name          = "TF V1 Upgrade part deux"
  enabled       = true
  category      = "Z2lkOi8vb3BzbGV2ZWwvQ2F0ZWdvcnkvNjk3Nw"
  level         = "Z2lkOi8vb3BzbGV2ZWwvTGV2ZWwvMzcxMA"
  owner         = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTI0Nw"
  filter        = "Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzM1NzA"
  tool_category = "metrics"
  tool_name_predicate = {
    type  = "equals"
    value = "datadog"
  }
  environment_predicate = {
    type  = "exists"
    value = null
  }
  notes = "Optional additional info on why this check is run or how to fix it"
}

Create Resources with Predicates 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_check_tool_usage.example will be created
  + resource "opslevel_check_tool_usage" "example" {
      + category              = "Z2lkOi8vb3BzbGV2ZWwvQ2F0ZWdvcnkvNjk3Nw"
      + description           = (known after apply)
      + enabled               = true
      + environment_predicate = {
          + type = "exists"
        }
      + filter                = "Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzM1NzA"
      + id                    = (known after apply)
      + level                 = "Z2lkOi8vb3BzbGV2ZWwvTGV2ZWwvMzcxMA"
      + name                  = "TF V1 Upgrade part deux"
      + notes                 = "Optional additional info on why this check is run or how to fix it"
      + owner                 = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTI0Nw"
      + tool_category         = "metrics"
      + tool_name_predicate   = {
          + type  = "equals"
          + value = "datadog"
        }
    }

  # opslevel_filter.example will be created
  + resource "opslevel_filter" "example" {
      + connective = "and"
      + id         = (known after apply)
      + name       = "foobarbaz1"

      + predicate {
          + case_insensitive = (known after apply)
          + case_sensitive   = true
          + key              = "language"
          + type             = "contains"
          + value            = "barbaz"
        }
      + predicate {
          + case_insensitive = (known after apply)
          + case_sensitive   = (known after apply)
          + key              = "tier_index"
          + type             = "less_than_or_equal_to"
          + value            = "3"
        }
      + predicate {
          + case_insensitive = (known after apply)
          + case_sensitive   = (known after apply)
          + key              = "language"
          + type             = "exists"
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.
opslevel_check_tool_usage.example: Creating...
opslevel_filter.example: Creating...
opslevel_check_tool_usage.example: Creation complete after 0s [id=Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY]
opslevel_filter.example: Creation complete after 0s [id=Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI]

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

Update Terraform config:

resource "opslevel_filter" "example" {
  name       = "foobarbaz1"
  connective = "and"
  predicate {
    key              = "language"
    type             = "contains"
    value            = "barbaz"
    case_sensitive   = true
    case_insensitive = null
  }
  # predicate {
  #   key   = "tier_index"
  #   type  = "less_than_or_equal_to"
  #   value = 3
  # }
  predicate {
    key   = "language"
    type  = "exists"
    value = null
  }
}

resource "opslevel_check_tool_usage" "example" {
  name          = "TF V1 Upgrade part deux"
  enabled       = true
  category      = "Z2lkOi8vb3BzbGV2ZWwvQ2F0ZWdvcnkvNjk3Nw"
  level         = "Z2lkOi8vb3BzbGV2ZWwvTGV2ZWwvMzcxMA"
  owner         = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTI0Nw"
  filter        = "Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzM1NzA"
  tool_category = "metrics"
  tool_name_predicate = {
    type  = "does_not_equal"
    value = "datadog"
  }
  environment_predicate = {
    type  = "starts_with"
    value = "water"
  }
  notes = "Optional additional info on why this check is run or how to fix it"
}

Update the Resource's predicates, terraform apply

opslevel_filter.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI]
opslevel_check_tool_usage.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY]

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_check_tool_usage.example will be updated in-place
  ~ resource "opslevel_check_tool_usage" "example" {
      ~ description           = "The service is using 'datadog' as a metrics tool in an environment which exists ''." -> (known after apply)
      ~ environment_predicate = {
          ~ type  = "exists" -> "starts_with"
          + value = "water"
        }
        id                    = "Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY"
        name                  = "TF V1 Upgrade part deux"
      ~ tool_name_predicate   = {
          ~ type  = "equals" -> "does_not_equal"
            # (1 unchanged attribute hidden)
        }
        # (7 unchanged attributes hidden)
    }

  # opslevel_filter.example will be updated in-place
  ~ resource "opslevel_filter" "example" {
        id         = "Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI"
        name       = "foobarbaz1"
        # (1 unchanged attribute hidden)

      ~ predicate {
          + case_insensitive = (known after apply)
            # (4 unchanged attributes hidden)
        }
      ~ predicate {
          + case_insensitive = (known after apply)
          + case_sensitive   = (known after apply)
          ~ key              = "tier_index" -> "language"
          ~ type             = "less_than_or_equal_to" -> "exists"
          - value            = "3" -> null
        }
      - predicate {
          - key  = "language" -> null
          - type = "exists" -> null
        }
    }

Plan: 0 to add, 2 to change, 0 to destroy.
opslevel_filter.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI]
opslevel_check_tool_usage.example: Modifying... [id=Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY]
opslevel_filter.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI]
opslevel_check_tool_usage.example: Modifications complete after 1s [id=Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY]

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

Destroy Resources, terraform destroy

opslevel_filter.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI]
opslevel_check_tool_usage.example: Refreshing state... [id=Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY]

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

Terraform will perform the following actions:

  # opslevel_check_tool_usage.example will be destroyed
  - resource "opslevel_check_tool_usage" "example" {
      - category              = "Z2lkOi8vb3BzbGV2ZWwvQ2F0ZWdvcnkvNjk3Nw" -> null
      - description           = "The service has a metrics tool whose name does not equal 'datadog' in an environment which starts with 'water'." -> null
      - enabled               = true -> null
      - environment_predicate = {
          - type  = "starts_with" -> null
          - value = "water" -> null
        } -> null
      - filter                = "Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzM1NzA" -> null
      - id                    = "Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY" -> null
      - level                 = "Z2lkOi8vb3BzbGV2ZWwvTGV2ZWwvMzcxMA" -> null
      - name                  = "TF V1 Upgrade part deux" -> null
      - notes                 = "Optional additional info on why this check is run or how to fix it" -> null
      - owner                 = "Z2lkOi8vb3BzbGV2ZWwvVGVhbS8xOTI0Nw" -> null
      - tool_category         = "metrics" -> null
      - tool_name_predicate   = {
          - type  = "does_not_equal" -> null
          - value = "datadog" -> null
        } -> null
    }

  # opslevel_filter.example will be destroyed
  - resource "opslevel_filter" "example" {
      - connective = "and" -> null
      - id         = "Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI" -> null
      - name       = "foobarbaz1" -> null

      - predicate {
          - case_sensitive = true -> null
          - key            = "language" -> null
          - type           = "contains" -> null
          - value          = "barbaz" -> null
        }
      - predicate {
          - key  = "language" -> null
          - type = "exists" -> null
        }
    }

Plan: 0 to add, 0 to change, 2 to destroy.
opslevel_filter.example: Destroying... [id=Z2lkOi8vb3BzbGV2ZWwvRmlsdGVyLzQ4NzI]
opslevel_check_tool_usage.example: Destroying... [id=Z2lkOi8vb3BzbGV2ZWwvQ2hlY2tzOjpUb29sVXNhZ2UvMjY0NzY]
opslevel_filter.example: Destruction complete after 1s
opslevel_check_tool_usage.example: Destruction complete after 1s

Destroy complete! Resources: 2 destroyed.