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(automations): resource support #305

Merged
merged 13 commits into from
Nov 25, 2024
Merged

Conversation

parkedwards
Copy link
Contributor

@parkedwards parkedwards commented Nov 21, 2024

resolves https://linear.app/prefect/issue/PLA-576/tfp-automations-scaffold-resource-and-add-acceptance-tests

Documentation on Automations

This PR adds prefect_automation support at the resource level

Automations are polymorphic, at two different levels - the trigger and the actions. The general structure of the schema is:

resource "prefect_automation" "test" {
  name = "foo"
  description = "baz"
  enabled = true

  trigger = {
    // there are 4 different trigger types: event, metric, compound, and sequence
  }

  actions = [
    { type: "run-deployment", // extra attributes here that pertain to this action type },
    { type: "pause-work-queue" },
  ]
}

The resource logic got pretty length, so I've split them up into the following files:

  • automation_model.go --> this is the tfsdk-based struct representation of the TF config, in golang
  • automation_schema.go --> this defines the HCL configuration schema
  • automation_resource.go --> this has the lifecycle hooks, as well as some lengthy helpers to parse and map the automation, trigger, and action objects both to and from the TF layer

Testing

Build the provider locally at this branch

$ pwd
terraform-provider-prefect

➜ cat ~/.terraformrc
provider_installation {

  dev_overrides {
      "prefecthq/prefect" = "/Users/edwardpark/CODE/prefect/terraform-provider-prefect/build"
  }

  # For all other providers, install them directly from their origin provider
  # registries as normal. If you omit this, Terraform will _only_ use
  # the dev_overrides block, and so no other providers will be available.
  direct {}
}

Navigate to the examples file

$ cd ./examples/resources/prefect_automation

Add your provider configuration

# in resource.tf

terraform {
  required_providers {
    prefect = {
      source = "prefecthq/prefect"
    }
  }
}

provider "prefect" {
  account_id   = "<your account id>"
  workspace_id = "<your workspace id>"
}
$ terraform init

➜ terraform apply --auto-approve
╷
│ Warning: Provider development overrides are in effect
│
│ The following provider development overrides are set in the CLI configuration:
│  - prefecthq/prefect in /Users/edwardpark/CODE/prefect/terraform-provider-prefect/build
│
│ The behavior may therefore not match any released version of the provider and applying changes may cause the state to become incompatible with
│ published releases.
╵

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:

  # prefect_automation.compound_trigger will be created
  + resource "prefect_automation" "compound_trigger" {
      + actions            = [
          + {
              + job_variables = jsonencode(
                    {
                      + var1 = "value1"
                      + var2 = "value2"
                      + var3 = 3
                      + var4 = true
                      + var5 = {
                          + key1 = "value1"
                        }
                    }
                )
              + parameters    = jsonencode({})
              + source        = "inferred"
              + type          = "run-deployment"
            },
        ]
      + actions_on_resolve = []
      + actions_on_trigger = []
      + created            = (known after apply)
      + description        = "compound trigger dos!"
      + enabled            = true
      + id                 = (known after apply)
      + name               = "tfp-test-compound-trigger"
      + trigger            = {
          + compound = {
              + require  = "any"
              + triggers = [
                  + {
                      + event = {
                          + after         = []
                          + expect        = [
                              + "prefect.flow-run.Failed",
                            ]
                          + for_each      = []
                          + match         = jsonencode(
                                {
                                  + "prefect.resource.id" = "prefect.flow-run.*"
                                }
                            )
                          + match_related = jsonencode(
                                {
                                  + "prefect.resource.id"   = "prefect.flow-run.*"
                                  + "prefect.resource.role" = "flow"
                                }
                            )
                          + posture       = "Reactive"
                          + threshold     = 1
                          + within        = 0
                        }
                    },
                  + {
                      + event = {
                          + after         = []
                          + expect        = [
                              + "prefect.flow-run.NonExistent",
                            ]
                          + for_each      = []
                          + match         = jsonencode(
                                {
                                  + "prefect.resource.id" = "prefect.flow-run.*"
                                }
                            )
                          + match_related = jsonencode(
                                {
                                  + "prefect.resource.id"   = "prefect.flow-run.*"
                                  + "prefect.resource.role" = "flow"
                                }
                            )
                          + posture       = "Reactive"
                          + threshold     = 1
                          + within        = 0
                        }
                    },
                ]
              + within   = 300
            }
        }
      + updated            = (known after apply)
    }

  # prefect_automation.event_trigger will be created
  + resource "prefect_automation" "event_trigger" {
      + actions            = [
          + {
              + job_variables = jsonencode(
                    {
                      + var1 = "value1"
                      + var2 = "value2"
                      + var3 = 3
                      + var4 = true
                      + var5 = {
                          + key1 = "value1"
                        }
                    }
                )
              + parameters    = jsonencode(
                    {
                      + param1 = "value1"
                      + param2 = "value2"
                    }
                )
              + source        = "inferred"
              + type          = "run-deployment"
            },
          + {
              + job_variables = jsonencode({})
              + parameters    = jsonencode({})
              + type          = "declare-incident"
            },
          + {
              + job_variables = jsonencode({})
              + parameters    = jsonencode({})
              + type          = "do-nothing"
            },
        ]
      + actions_on_resolve = []
      + actions_on_trigger = []
      + created            = (known after apply)
      + description        = "ayu carumba"
      + enabled            = true
      + id                 = (known after apply)
      + name               = "tfp-test-event-trigger"
      + trigger            = {
          + event = {
              + after         = [
                  + "prefect.flow-run.completed",
                ]
              + expect        = [
                  + "prefect.flow-run.failed",
                ]
              + for_each      = [
                  + "prefect.resource.id",
                ]
              + match         = jsonencode(
                    {
                      + "prefect.resource.id" = "prefect.flow-run.*"
                    }
                )
              + match_related = jsonencode(
                    {
                      + "prefect.resource.id"   = [
                          + "prefect.flow.ce6ec0c9-4b51-483b-a776-43c085b6c4f8",
                        ]
                      + "prefect.resource.role" = "flow"
                    }
                )
              + posture       = "Reactive"
              + threshold     = 1
              + within        = 60
            }
        }
      + updated            = (known after apply)
    }

  # prefect_automation.metric_trigger will be created
  + resource "prefect_automation" "metric_trigger" {
      + actions            = [
          + {
              + job_variables = jsonencode({})
              + message       = "Flow run failed due to {{ event.reason }}"
              + name          = "Failed by automation"
              + parameters    = jsonencode({})
              + state         = "FAILED"
              + type          = "change-flow-run-state"
            },
        ]
      + actions_on_resolve = []
      + actions_on_trigger = []
      + created            = (known after apply)
      + description        = "boom shakkala"
      + enabled            = true
      + id                 = (known after apply)
      + name               = "tfp-test-metric-trigger"
      + trigger            = {
          + metric = {
              + match         = jsonencode({})
              + match_related = jsonencode({})
              + metric        = {
                  + firing_for = 300
                  + name       = "duration"
                  + operator   = ">="
                  + range      = 300
                  + threshold  = 10
                }
            }
        }
      + updated            = (known after apply)
    }

  # prefect_automation.sequence_trigger will be created
  + resource "prefect_automation" "sequence_trigger" {
      + actions            = [
          + {
              + block_document_id = "123e4567-e89b-12d3-a456-426614174000"
              + body              = "Flow run {{ event.resource['prefect.resource.id'] }} failed at {{ event.occurred }}"
              + job_variables     = jsonencode({})
              + parameters        = jsonencode({})
              + subject           = "Flow Run Failed: {{ event.resource['prefect.resource.name'] }}"
              + type              = "send-notification"
            },
        ]
      + actions_on_resolve = [
          + {
              + block_document_id = "123e4567-e89b-12d3-a456-426614174000"
              + job_variables     = jsonencode({})
              + parameters        = jsonencode({})
              + payload           = jsonencode(
                    {
                      + flow_run_id = "{{ event.resource['prefect.resource.id'] }}"
                      + status      = "{{ event.event }}"
                    }
                )
              + type              = "call-webhook"
            },
        ]
      + actions_on_trigger = [
          + {
              + job_variables = jsonencode({})
              + message       = "Flow run failed due to {{ event.resource['prefect.resource.name'] }}"
              + name          = "Failed by automation"
              + parameters    = jsonencode({})
              + state         = "FAILED"
              + type          = "change-flow-run-state"
            },
        ]
      + created            = (known after apply)
      + description        = "sequence trigger tres!"
      + enabled            = true
      + id                 = (known after apply)
      + name               = "tfp-test-sequence-trigger"
      + trigger            = {
          + sequence = {
              + triggers = [
                  + {
                      + event = {
                          + after         = []
                          + expect        = [
                              + "prefect.flow-run.Pending",
                            ]
                          + for_each      = []
                          + match         = jsonencode(
                                {
                                  + "prefect.resource.id" = "prefect.flow-run.*"
                                }
                            )
                          + match_related = jsonencode({})
                          + posture       = "Reactive"
                          + threshold     = 1
                          + within        = 0
                        }
                    },
                  + {
                      + event = {
                          + after         = []
                          + expect        = [
                              + "prefect.flow-run.Running",
                            ]
                          + for_each      = []
                          + match         = jsonencode(
                                {
                                  + "prefect.resource.id" = "prefect.flow-run.*"
                                }
                            )
                          + match_related = jsonencode({})
                          + posture       = "Reactive"
                          + threshold     = 1
                          + within        = 0
                        }
                    },
                  + {
                      + event = {
                          + after         = []
                          + expect        = [
                              + "prefect.flow-run.Completed",
                            ]
                          + for_each      = []
                          + match         = jsonencode(
                                {
                                  + "prefect.resource.id" = "prefect.flow-run.*"
                                }
                            )
                          + match_related = jsonencode({})
                          + posture       = "Reactive"
                          + threshold     = 1
                          + within        = 0
                        }
                    },
                ]
              + within   = 300
            }
        }
      + updated            = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.
prefect_automation.metric_trigger: Creating...
prefect_automation.compound_trigger: Creating...
prefect_automation.event_trigger: Creating...
prefect_automation.sequence_trigger: Creating...
prefect_automation.metric_trigger: Creation complete after 1s [id=f0455956-f1a6-4d8b-9d9b-b37ed867752f]
prefect_automation.compound_trigger: Creation complete after 0s [id=6c8928a4-bb53-419f-917a-bd6e1c7c9f88]
prefect_automation.event_trigger: Creation complete after 0s [id=13960689-7fcb-4f42-8c81-fcfc1a208560]
prefect_automation.sequence_trigger: Creation complete after 0s [id=7759cb06-ea19-48fc-b277-61db8bdb3de9]

And you'll see the Automations created

image
image

@parkedwards parkedwards changed the title feat(automations): add resource feat(automations): resource support Nov 25, 2024
@parkedwards parkedwards marked this pull request as ready for review November 25, 2024 01:59
@parkedwards parkedwards requested a review from a team as a code owner November 25, 2024 01:59
Copy link
Contributor

@mitchnielsen mitchnielsen left a comment

Choose a reason for hiding this comment

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

This is a huge one, great work - couple small comments

docs/resources/automation.md Show resolved Hide resolved
Copy link
Contributor

@mitchnielsen mitchnielsen left a comment

Choose a reason for hiding this comment

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

Was able to replicate the testing notes using the example resource 👌🏼

@parkedwards parkedwards merged commit 85f7297 into main Nov 25, 2024
@parkedwards parkedwards deleted the feat/automations-api-redux branch November 25, 2024 17:25
@parkedwards parkedwards added the enhancement New feature or request label Nov 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs enhancement New feature or request feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants