exampleterraformprovider

package module
v0.0.0-...-dcf58f9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2022 License: Apache-2.0 Imports: 0 Imported by: 0

README

example-terraform-provider

Me just toying around with the problem space of writing a custom Terraform provider

Prerequisites

Bring up the database (and ensure migrations run, etc.)

make restart-postgres
# It's a bit janky, there is a race condition with grants causing occasional
# errors, e.g.
#
# # ...
# # module.books_grants.postgresql_grant.app_table_grant: Creation complete after 0s [id=books_app_books_books_table]
# # ╷
# # │ Error: could not execute revoke query: pq: tuple concurrently updated
# # │
# # │   with module.books_grants.postgresql_grant.grant_application_schema_to_app,
# # │   on ../../modules/postgresql-grants/main.tf line 23, in resource "postgresql_grant" "grant_application_schema_to_app":
# # │   23: resource "postgresql_grant" "grant_application_schema_to_app" {
# # │
# # ╵
# # ...

Run the Books API server

make serve

Clear / seed the database with some data

make clear-database
make seed-data

Install the provider into ~/.terraform.d/plugins (or a different Terraform plugins directory if configured):

make install-terraform-provider

Demo

First setup / cleanup the workspace:

# Or: `make apply-books-workspace`

cd _terraform/workspaces/books/
# Technically should also remove the 'George R.R. Martin' row from the
# database if it's still present there; can just do this via
# `make clear-database && make seed-data`.
rm --force .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup
rm --force --recursive .terraform/
terraform init

After doing this, our provider should have been pulled from the local Terraform plugins directory:

$ tree .terraform/
.terraform/
└── providers
    └── tf-registry.invalid
        └── dhermes
            └── books
                └── 0.0.1
                    └── linux_amd64 -> /home/dhermes/.terraform.d/plugins/tf-registry.invalid/dhermes/books/0.0.1/linux_amd64

6 directories, 0 files
$
$ # OR
$
$ tree .terraform/
.terraform
└── providers
    └── tf-registry.invalid
        └── dhermes
            └── books
                └── 0.0.1
                    └── darwin_arm64 -> /Users/dhermes/.terraform.d/plugins/tf-registry.invalid/dhermes/books/0.0.1/darwin_arm64

6 directories, 0 files

then apply the workspace:

terraform apply

Then check out the newly created resources:

$ terraform apply
...
  Enter a value: yes

books_api_author.grr_martin: Creating...
books_api_book.sirens: Creating...
books_api_author.grr_martin: Creation complete after 0s [id=04a76223-6d29-43d7-bcae-744773efd964]
books_api_book.song_fire_ice5: Creating...
books_api_book.song_fire_ice2: Creating...
books_api_book.song_fire_ice3: Creating...
books_api_book.song_fire_ice1: Creating...
books_api_book.song_fire_ice4: Creating...
books_api_book.song_fire_ice4: Creation complete after 0s [id=b368334e-4c05-4d55-ab7a-0c8fd1afa1c5]
books_api_book.sirens: Creation complete after 0s [id=d1f0f231-554d-4e2a-8c53-a007ad67763b]
books_api_book.song_fire_ice3: Creation complete after 0s [id=af2a723a-1fce-415d-8024-2ead084493ed]
books_api_book.song_fire_ice1: Creation complete after 0s [id=1ff1b78c-37b1-45b3-8ca8-93219edf2ea4]
books_api_book.song_fire_ice5: Creation complete after 0s [id=bc2fab4f-2d30-495a-8c9c-7324aaed93b1]
books_api_book.song_fire_ice2: Creation complete after 0s [id=9001ae4c-6eba-40ef-bdb1-1d95961201e3]

Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
$
$
$ jq '.resources | length' terraform.tfstate
8
$
$
$ jq '.resources[0]' terraform.tfstate
{
  "mode": "data",
  "type": "books_api_author",
  "name": "vonnegut",
  "provider": "provider[\"tf-registry.invalid/dhermes/books\"].local",
  "instances": [
    {
      "schema_version": 0,
      "attributes": {
        "book_count": 0,
        "first_name": "Kurt",
        "id": "f4c5a610-753a-4940-8314-c5dff54477af",
        "last_name": "Vonnegut"
      },
      "sensitive_attributes": []
    }
  ]
}
$
$
$ jq '.resources[1]' terraform.tfstate
{
  "mode": "managed",
  "type": "books_api_author",
  "name": "grr_martin",
  "provider": "provider[\"tf-registry.invalid/dhermes/books\"].local",
  "instances": [
    {
      "schema_version": 0,
      "attributes": {
        "book_count": 0,
        "first_name": "George R.R.",
        "id": "04a76223-6d29-43d7-bcae-744773efd964",
        "last_name": "Martin"
      },
      "sensitive_attributes": [],
      "private": "bnVsbA=="
    }
  ]
}
$
$
$ jq '.resources[2]' terraform.tfstate
{
  "mode": "managed",
  "type": "books_api_book",
  "name": "sirens",
  "provider": "provider[\"tf-registry.invalid/dhermes/books\"].local",
  "instances": [
    {
      "schema_version": 0,
      "attributes": {
        "author_id": "f4c5a610-753a-4940-8314-c5dff54477af",
        "id": "d1f0f231-554d-4e2a-8c53-a007ad67763b",
        "publish_date": "1959-01-01",
        "title": "The Sirens of Titan"
      },
      "sensitive_attributes": [],
      "private": "bnVsbA==",
      "dependencies": [
        "data.books_api_author.vonnegut"
      ]
    }
  ]
}
$
$
$ psql --dbname 'postgres://books_app:testpassword_app@127.0.0.1:22411/books' --command "SELECT * FROM authors WHERE last_name IN ('Martin', 'Vonnegut')"
                  id                  | first_name  | last_name
--------------------------------------+-------------+-----------
 f4c5a610-753a-4940-8314-c5dff54477af | Kurt        | Vonnegut
 04a76223-6d29-43d7-bcae-744773efd964 | George R.R. | Martin
(2 rows)

$
$
$ psql --dbname 'postgres://books_app:testpassword_app@127.0.0.1:22411/books' --command "SELECT * FROM books WHERE author_id IN ('f4c5a610-753a-4940-8314-c5dff54477af', '04a76223-6d29-43d7-bcae-744773efd964')"
                  id                  |              author_id               |        title         |      publish_date
--------------------------------------+--------------------------------------+----------------------+------------------------
 b368334e-4c05-4d55-ab7a-0c8fd1afa1c5 | 04a76223-6d29-43d7-bcae-744773efd964 | A Feast for Crows    | 2005-10-17 00:00:00+00
 d1f0f231-554d-4e2a-8c53-a007ad67763b | f4c5a610-753a-4940-8314-c5dff54477af | The Sirens of Titan  | 1959-01-01 00:00:00+00
 1ff1b78c-37b1-45b3-8ca8-93219edf2ea4 | 04a76223-6d29-43d7-bcae-744773efd964 | A Game of Thrones    | 1996-08-01 00:00:00+00
 af2a723a-1fce-415d-8024-2ead084493ed | 04a76223-6d29-43d7-bcae-744773efd964 | A Storm of Swords    | 2000-08-08 00:00:00+00
 9001ae4c-6eba-40ef-bdb1-1d95961201e3 | 04a76223-6d29-43d7-bcae-744773efd964 | A Clash of Kings     | 1998-11-16 00:00:00+00
 bc2fab4f-2d30-495a-8c9c-7324aaed93b1 | 04a76223-6d29-43d7-bcae-744773efd964 | A Dance with Dragons | 2011-07-12 00:00:00+00
(6 rows)

$

To make sure other state transitions are in good shape, apply it again:

$ terraform apply
...
Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # books_api_author.grr_martin has changed
  ~ resource "books_api_author" "grr_martin" {
      ~ book_count = 0 -> 5
        id         = "04a76223-6d29-43d7-bcae-744773efd964"
        # (2 unchanged attributes hidden)
    }


Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes.

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

No changes. Your infrastructure matches the configuration.

Your configuration already matches the changes detected above. If you'd like to update the Terraform state to match, create and apply a refresh-only plan:
  terraform apply -refresh-only

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
$
$
$ terraform apply
books_api_author.grr_martin: Refreshing state... [id=04a76223-6d29-43d7-bcae-744773efd964]
books_api_book.sirens: Refreshing state... [id=d1f0f231-554d-4e2a-8c53-a007ad67763b]
books_api_book.song_fire_ice2: Refreshing state... [id=9001ae4c-6eba-40ef-bdb1-1d95961201e3]
books_api_book.song_fire_ice1: Refreshing state... [id=1ff1b78c-37b1-45b3-8ca8-93219edf2ea4]
books_api_book.song_fire_ice4: Refreshing state... [id=b368334e-4c05-4d55-ab7a-0c8fd1afa1c5]
books_api_book.song_fire_ice5: Refreshing state... [id=bc2fab4f-2d30-495a-8c9c-7324aaed93b1]
books_api_book.song_fire_ice3: Refreshing state... [id=af2a723a-1fce-415d-8024-2ead084493ed]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

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

(Homework: make a small edit to _terraform/workspaces/books/vonnegut.tf to make sure updates work.)

Now, test out deletes for the resources:

# Or: `make destroy-books-workspace`
terraform apply --destroy

Running this with the examples above:

$ terraform apply --destroy
...
  Enter a value: yes

books_api_book.sirens: Destroying... [id=d1f0f231-554d-4e2a-8c53-a007ad67763b]
books_api_book.song_fire_ice1: Destroying... [id=1ff1b78c-37b1-45b3-8ca8-93219edf2ea4]
books_api_book.song_fire_ice2: Destroying... [id=9001ae4c-6eba-40ef-bdb1-1d95961201e3]
books_api_book.song_fire_ice5: Destroying... [id=bc2fab4f-2d30-495a-8c9c-7324aaed93b1]
books_api_book.song_fire_ice3: Destroying... [id=af2a723a-1fce-415d-8024-2ead084493ed]
books_api_book.song_fire_ice4: Destroying... [id=b368334e-4c05-4d55-ab7a-0c8fd1afa1c5]
books_api_book.song_fire_ice2: Destruction complete after 0s
books_api_book.song_fire_ice4: Destruction complete after 0s
books_api_book.song_fire_ice1: Destruction complete after 0s
books_api_book.sirens: Destruction complete after 0s
books_api_book.song_fire_ice5: Destruction complete after 0s
books_api_book.song_fire_ice3: Destruction complete after 0s
books_api_author.grr_martin: Destroying... [id=04a76223-6d29-43d7-bcae-744773efd964]
books_api_author.grr_martin: Destruction complete after 0s

Apply complete! Resources: 0 added, 0 changed, 7 destroyed.
$
$
$ psql --dbname 'postgres://books_app:testpassword_app@127.0.0.1:22411/books' --command "SELECT * FROM authors WHERE last_name IN ('Martin', 'Vonnegut')"
                  id                  | first_name | last_name
--------------------------------------+------------+-----------
 f4c5a610-753a-4940-8314-c5dff54477af | Kurt       | Vonnegut
(1 row)

$
$
$ psql --dbname 'postgres://books_app:testpassword_app@127.0.0.1:22411/books' --command "SELECT * FROM books WHERE author_id IN ('f4c5a610-753a-4940-8314-c5dff54477af', '04a76223-6d29-43d7-bcae-744773efd964')"
 id | author_id | title | publish_date
----+-----------+-------+--------------
(0 rows)

$

Development

$ make  # Or `make help`
Makefile for `example-terraform-provider` project

Usage:
   make serve                         Run the Books API application
   make seed-data                     Seed the database via the Books API
   make clean                         Forcefully remove all generated artifacts (e.g. Terraform state files)
   make vet                           Run `go vet` over source tree
   make shellcheck                    Run `shellcheck` on all shell files in `./_bin/`
Terraform-specific Targets:
   make install-terraform-provider    Install `terraform-provider-books` into Terraform plugins directory
   make apply-books-workspace         Apply the workspace that uses `terraform-provider-books`
   make destroy-books-workspace       Destroy the workspace that uses `terraform-provider-books`
   make start-postgres-container      Start PostgreSQL Docker containers
   make stop-postgres-container       Stop PostgreSQL Docker containers
   make initialize-database           Initialize the database, schema, roles and grants in the PostgreSQL instances
   make teardown-database             Teardown the database, schema, roles and grants in the PostgreSQL instances
PostgreSQL-specific Targets:
   make migrations-up                 Run PostgreSQL migrations for Books database
   make start-postgres                Starts a PostgreSQL database running in a Docker container and set up users
   make stop-postgres                 Stops the PostgreSQL database running in a Docker container
   make restart-postgres              Stops the PostgreSQL database (if running) and starts a fresh Docker container
   make clear-database                Deletes data from all existing tables
   make require-postgres              Determine if PostgreSQL database is running; fail if not
   make psql                          Connects to currently running PostgreSQL DB via `psql` as app user
   make psql-admin                    Connects to currently running PostgreSQL DB via `psql` as admin user
   make psql-superuser                Connects to currently running PostgreSQL DB via `psql` as superuser

Documentation

Overview

Package exampleterraformprovider is the root package.

It contains no code but is here for documentation purposes.

Directories

Path Synopsis
cmd
migrations-up command
provider command
server command
pkg
booksclient
Package booksclient is an API client for a fictional books API.
Package booksclient is an API client for a fictional books API.
booksprovider
Package booksprovider implements the core Books Terraform Provider.
Package booksprovider implements the core Books Terraform Provider.
model
Package model implements helpers for persisting records to the database.
Package model implements helpers for persisting records to the database.
providershim
Package providershim wraps the core Books Terraform Provider.
Package providershim wraps the core Books Terraform Provider.
server
Package server implements a basic Books API server.
Package server implements a basic Books API server.
sqlmigrations
Package sqlmigrations implements bare bones migrations for SQL schema.
Package sqlmigrations implements bare bones migrations for SQL schema.
terraform
Package terraform provides generic helpers for interacting with `hashicorp/terraform-plugin-sdk` packages.
Package terraform provides generic helpers for interacting with `hashicorp/terraform-plugin-sdk` packages.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL