Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Terraform provider for Cosmoner

Manages Cosmoner resources from Terraform, through the same public API and API keys as the SDKs and the CLI.

terraform {
  required_providers {
    cosmoner = {
      source = "datablock-dev/cosmoner"
    }
  }
}

provider "cosmoner" {}

resource "cosmoner_variable" "log_level" {
  name        = "LOG_LEVEL"
  environment = "production"
  value       = "info"
}

resource "cosmoner_secret" "database_password" {
  name             = "DB_PASSWORD"
  environment      = "production"
  value_wo         = var.database_password
  value_wo_version = 1
}

The provider reads the CLI's environment variables, so a shell or CI job already set up for cosmoner works unchanged:

Variable Provider argument Notes
COSMONER_API_KEY api_key Required. Keep it in the environment, not HCL.
COSMONER_PROJECT_ID project_id Default project for every resource.
COSMONER_API_URL api_url Defaults to https://api.cosmoner.com.

A value set in the provider block wins over the environment.

What it manages

Type Kind Scopes the key needs
cosmoner_secret resource secrets:read, secrets:write, owner or admin
cosmoner_variable resource variables:read, variables:write, owner or admin
cosmoner_webhook_endpoint resource webhooks:read, webhooks:write
cosmoner_ssh_key resource servers:read, servers:write
cosmoner_resource_group resource projects:read, projects:write
cosmoner_app resource apps:read, apps:write
cosmoner_postgres_database resource databases:read, databases:write
cosmoner_redis_database resource redis:read, redis:write
cosmoner_bucket resource object-storage:read, object-storage:write
cosmoner_project data source projects:read

Every resource takes an optional project_id that overrides the provider's, and imports as <project_id>/<id>, or as a bare <id> when the provider has a default project. docs/ has the full reference for each one.

Billable resources

Apps, PostgreSQL databases, Redis databases and buckets are charged to the project's saved card from the moment they exist. apply waits until each one is ready, up to the create time in its timeouts block. One that fails to become ready, or is still starting at the deadline, stays in state as tainted: the next apply replaces it, and terraform destroy removes it. The provider never deletes it on its own, so a failed deploy's logs are still there to read. A create the API refuses leaves nothing behind.

Databases and Redis have no resize route, so changing their size or plan replaces them, data included. Use lifecycle { prevent_destroy = true } on anything holding data you care about.

Secrets

The API never returns a secret's value after setting it, so the provider cannot compare the stored value with the configured one. It watches the secret's version instead, which every write bumps. When the version moves without Terraform, someone set the value elsewhere, and the next plan sets the configured value again.

value_wo (Terraform 1.11+) keeps the value out of state and plan entirely. Terraform cannot see a change to a write-only value, so bump value_wo_version to send a new one. value works on any version, but is stored in state as a sensitive value.

Development

go test ./...                  # unit tests, plus Terraform runs against a fake API
golangci-lint run ./...
go generate ./...              # regenerates docs/ — commit the result

The provider tests run a real terraform binary through plan, apply, refresh and import, against an in-process fake of the API in internal/provider/fake_api_test.go. They need no credentials. The harness uses the terraform on PATH, or TF_ACC_TERRAFORM_PATH, and downloads one if it finds neither. Set CHECKPOINT_DISABLE=1 to skip HashiCorp's version check.

The fake mirrors the platform's routes, status codes and quirks: secrets that never return their value, descriptions kept unless one is sent, keys whitespace-normalised. When the platform changes one of those, change the fake with it, or the tests prove behaviour the real API no longer has.

docs/ is generated by tfplugindocs from the schema descriptions and examples/, and CI fails if it is stale. Write the documentation in the schema's MarkdownDescription, never in docs/ itself.

To try a local build in a real configuration, build it and point Terraform at it with a development override:

go build -o "$HOME/.terraform.d/dev/terraform-provider-cosmoner" .
# ~/.terraformrc
provider_installation {
  dev_overrides {
    "datablock-dev/cosmoner" = "/home/you/.terraform.d/dev"
  }
  direct {}
}

Releasing

Push a version tag:

git tag v0.1.0
git push origin v0.1.0

.github/workflows/release.yml runs GoReleaser, which builds every platform, writes SHA256SUMS and the Registry manifest, signs the checksums with GPG and attaches everything to a GitHub release. The Terraform Registry picks the release up from there.

A tag whose release job failed does not need a new tag: once the cause is fixed, re-run the job from the Actions tab and it builds the tagged commit again.

Tags follow semantic versioning, and the Registry treats them as the provider's version: a breaking schema change (a removed attribute, a renamed resource, a new required argument) is a major version.

One-time setup

Nothing is published until all of this is done. The v0.1.0 tag was pushed before it was, and its release job stopped at the GPG import, so no GitHub release and nothing on the Registry came of it.

  1. Generate an RSA signing key (the Registry does not accept ECC keys) and export both halves:

    gpg --full-generate-key                # RSA and RSA, 4096 bits
    gpg --armor --export-secret-keys <fingerprint> > private.asc
    gpg --armor --export <fingerprint> > public.asc
  2. Add the private half and its passphrase as Actions secrets on this repository: GPG_PRIVATE_KEY (the whole of private.asc) and PASSPHRASE. The release job checks for the key first and says so when it is missing.

  3. Sign in to registry.terraform.io with GitHub and allow it access to the datablock-dev organization. If the organization restricts third-party OAuth apps, an owner has to approve the Terraform Registry under the organization's OAuth app policy first, or the namespace never appears.

  4. Under User Settings → Signing Keys, add public.asc to the datablock-dev namespace. The Registry verifies every release's SHA256SUMS.sig against it.

  5. Cut a release (above) and check that the GitHub release carries the zips, …_SHA256SUMS, …_SHA256SUMS.sig and …_manifest.json.

  6. Publish → Provider, choose datablock-dev/terraform-provider-cosmoner. The Registry ingests the existing releases and installs a webhook, so every later tag is picked up without visiting it again.

source = "datablock-dev/cosmoner" and registryAddress in main.go both follow from the namespace. Publishing from another one changes both.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages