Skip to content

Commit

Permalink
chore(tests): add sweeper to remove workspaces (#355)
Browse files Browse the repository at this point in the history
* chore(tests): add sweeper to remove workspaces

Adds sweepers to clean up workspaces in cases where they were not
automatically cleaned up.

https://developer.hashicorp.com/terraform/plugin/testing/acceptance-tests/sweepers

Related to https://linear.app/prefect/issue/PLA-642/ensure-ephemeral-workspaces-are-cleaned-up-consistently

* Only set workspace handle filter if provided

Only sets the workspace handle filter if one is provided. Otherwise,
passes in an empty filter struct.

This allows the List method to return all workspaces. Before this
change, the empty "Any" field was causing no matches to be returned.

This will help us with the sweeper so it can return all workspaces and
let us filter the ones that match the prefix we use for acceptance
testing.

* Change sweeper to list all workspaces

The ephemeral workspaces we make during tests have random values.
Because sweepers are run in a separate command from the acceptance
tests, there's currently no way to persist those unique values between
commands so the sweeper can delete those specific workspaces.

Instead, as a first iteration, this change lists all workspaces in an
account and deletes any that match the acceptance testing prefix we use.
This is designed to run either by hand or by CI at a given interval,
such as overnight, when other acceptance tests are not running.

* Update Makefile to support sweepers

* Add sweep arg in Makefile doc

* Move sweeper to sweep package

Follows other patterns and allows us to test this specific package
instead of other packages with other tests.

* Split files into sweep.go, sweep_test.go

* Update testacc-dev to sweep ./internal/sweep

* Add CI workflow to sweep nightly

Adds a CI workflow that will run the Terraform sweepers each night.

---------

Co-authored-by: Edward Park <[email protected]>
  • Loading branch information
mitchnielsen and parkedwards authored Jan 16, 2025
1 parent 3ac1fa6 commit 5a6386d
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 17 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/acceptance-tests-sweepers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This workflow runs the Terraform acceptance tests sweepers every day.
name: Acceptance Tests - Sweepers

"on":
# Run at 7 AM UTC every day (2 AM EST)
schedule:
- cron: '0 7 * * *'
# Run manually
workflow_dispatch: {}

permissions: {}

jobs:
sweepers:
permissions:
contents: read
name: Run Sweepers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: Run sweeper tests
run: make testacc-sweepers
env:
PREFECT_API_URL: ${{ secrets.ACC_TEST_PREFECT_API_URL }}
PREFECT_API_KEY: ${{ secrets.ACC_TEST_PREFECT_API_KEY }}
PREFECT_CLOUD_ACCOUNT_ID: ${{ secrets.ACC_TEST_PREFECT_CLOUD_ACCOUNT_ID }}
27 changes: 17 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ BINARY=terraform-provider-${NAME}

TESTS?=""
LOG_LEVEL?="INFO"
SWEEP?=""

default: build
.PHONY: default
Expand All @@ -12,15 +13,16 @@ help:
@echo ""
@echo "This project defines the following build targets:"
@echo ""
@echo " build - compiles source code to build/"
@echo " clean - removes built artifacts"
@echo " lint - run static code analysis"
@echo " test - run automated unit tests"
@echo " testacc - run automated acceptance tests"
@echo " testacc-dev - run automated acceptance tests from a local machine (args TESTS=<tests> LOG_LEVEL=<level>)"
@echo " docs - builds Terraform documentation"
@echo " dev-new - creates a new dev testfile (args: resource=<resource> name=<name>)"
@echo " dev-clean - cleans up dev directory"
@echo " build - compiles source code to build/"
@echo " clean - removes built artifacts"
@echo " lint - run static code analysis"
@echo " test - run automated unit tests"
@echo " testacc - run automated acceptance tests"
@echo " testacc-sweepers - run automated acceptance tests sweepers"
@echo " testacc-dev - run automated acceptance tests from a local machine (args: TESTS=<tests or empty> LOG_LEVEL=<level> SWEEP=<yes or empty>)"
@echo " docs - builds Terraform documentation"
@echo " dev-new - creates a new dev testfile (args: resource=<resource> name=<name>)"
@echo " dev-clean - cleans up dev directory"
.PHONY: help

build: $(BINARY)
Expand Down Expand Up @@ -49,8 +51,13 @@ testacc:
TF_ACC=1 make test
.PHONY: testacc

# NOTE: Acceptance Test sweepers delete real infrastructure against a dedicated testing account
testacc-sweepers:
go test ./internal/sweep -v -sweep=all
.PHONY: testacc-sweepers

testacc-dev:
./scripts/testacc-dev $(TESTS) $(LOG_LEVEL)
./scripts/testacc-dev $(TESTS) $(LOG_LEVEL) $(SWEEP)
.PHONY: testacc-dev

docs:
Expand Down
5 changes: 4 additions & 1 deletion internal/client/flows.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ func (c *FlowsClient) Create(ctx context.Context, data api.FlowCreate) (*api.Flo
// List returns a list of Flows, based on the provided list of handle names.
func (c *FlowsClient) List(ctx context.Context, handleNames []string) ([]*api.Flow, error) {
filterQuery := api.WorkspaceFilter{}
filterQuery.Workspaces.Handle.Any = handleNames

if len(handleNames) != 0 {
filterQuery.Workspaces.Handle.Any = handleNames
}

cfg := requestConfig{
method: http.MethodPost,
Expand Down
5 changes: 4 additions & 1 deletion internal/client/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ func (c *WorkspacesClient) Create(ctx context.Context, data api.WorkspaceCreate)
// List returns a list of Workspaces, based on the provided list of handle names.
func (c *WorkspacesClient) List(ctx context.Context, handleNames []string) ([]*api.Workspace, error) {
filterQuery := api.WorkspaceFilter{}
filterQuery.Workspaces.Handle.Any = handleNames

if len(handleNames) != 0 {
filterQuery.Workspaces.Handle.Any = handleNames
}

cfg := requestConfig{
method: http.MethodPost,
Expand Down
59 changes: 59 additions & 0 deletions internal/sweep/sweep.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package sweep

import (
"context"
"fmt"
"log"
"strings"

"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/prefecthq/terraform-provider-prefect/internal/testutils"
)

// AddWorkspaceSweeper adds a sweeper that deletes any workspaces that match
// the prefix we use for ephemeral workspaces in acceptance tests.
//
// This is designed to run at a given interval when other acceptance tests are
// not likely running.
func AddWorkspaceSweeper() {
resource.AddTestSweepers("workspaces", &resource.Sweeper{
Name: "workspaces",
F: func(_ string) error {
client, err := testutils.NewTestClient()
if err != nil {
return fmt.Errorf("unable to get prefect client: %w", err)
}

// NOTE: the accountID is inherited by the one set in the test environment
workspacesClient, err := client.Workspaces(uuid.Nil)
if err != nil {
return fmt.Errorf("unable to get workspaces client: %w", err)
}

workspaces, err := workspacesClient.List(context.Background(), []string{})
if err != nil {
return fmt.Errorf("unable to list workspaces: %w", err)
}

if len(workspaces) == 0 {
return fmt.Errorf("no workspaces found for this account")
}

for _, workspace := range workspaces {
if strings.HasPrefix(workspace.Name, testutils.TestAccPrefix) {
log.Printf("found acceptance testing workspace %s, deleting...\n", workspace.Name)

err := workspacesClient.Delete(context.Background(), workspace.ID)
if err != nil {
log.Printf("unable to delete workspaces %s during sweep: %s\n", workspace.Name, err)
}
} else {
log.Printf("workspace %s does not match acceptance testing prefix, skipping...\n", workspace.Name)
}
}

return nil
},
})
}
15 changes: 15 additions & 0 deletions internal/sweep/sweep_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package sweep_test

import (
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/prefecthq/terraform-provider-prefect/internal/sweep"
)

// TestMain adds sweeper functionality to the "go test" command.
func TestMain(m *testing.M) {
sweep.AddWorkspaceSweeper()

resource.TestMain(m)
}
20 changes: 15 additions & 5 deletions scripts/testacc-dev
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,28 @@ vault_entry='op://Platform/Terraform provider acceptance test secrets'

tests=${1:-""}
log_level=${2:-"INFO"}
sweep=${3:-""}

run_arg=""
if [ "${tests}" == "" ]; then
echo "no specific test configured, running all"
else
if [ "${sweep}" != "" ]; then
# If sweep is set to anything, only run the sweepers and skip the acceptance tests.
# This only runs on the 'resources' and 'datasources' packages, as those contain
# acceptance tests where sweepers apply.
#
# The '-v' flag ensures that 'fmt.Printf' statements are visible.
echo "sweeping..."
run_arg="go test ./internal/sweep -v -sweep=all"
elif [ "${tests}" != "" ]; then
echo "specific test(s) configured: ${tests}"
run_arg="-run ${tests}"
run_arg="gotestsum --max-fails=50 ./... -count=1 -v -run ^${tests}$"
else
echo "no specific test configured, running all"
run_arg="gotestsum --max-fails=50 ./... -count=1 -v"
fi

TF_ACC=1 \
TF_LOG=${log_level} \
PREFECT_API_URL=$(op read "${vault_entry}/PREFECT_API_URL") \
PREFECT_API_KEY=$(op read "${vault_entry}/PREFECT_API_KEY") \
PREFECT_CLOUD_ACCOUNT_ID=$(op read "${vault_entry}/PREFECT_CLOUD_ACCOUNT_ID") \
gotestsum --max-fails=50 ./... -count=1 -v ${run_arg}
${run_arg}

0 comments on commit 5a6386d

Please sign in to comment.