From 4632b71af41e3eb00e852c1e541209b35c624156 Mon Sep 17 00:00:00 2001 From: Bret Mogilefsky Date: Wed, 13 Sep 2023 22:14:51 -0700 Subject: [PATCH 1/2] Add semver module This does not update the main README's examples to use semver; we should probably discuss how best to do that. --- README.md | 6 ++++- semver/README.md | 35 +++++++++++++++++++++++++ semver/main.tf | 28 ++++++++++++++++++++ semver/tests/default/default.tf | 46 +++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 semver/README.md create mode 100644 semver/main.tf create mode 100644 semver/tests/default/default.tf diff --git a/README.md b/README.md index ea7dc20..5d62bb6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # terraform-cloudgov -Terraform modules for cloud.gov managed services commonly used by [18f/rails-template](https://github.com/18f/rails-template) based apps +Terraform modules for working with cloud.gov commonly used by [18f/rails-template](https://github.com/18f/rails-template) based apps ## Module Examples @@ -117,3 +117,7 @@ module "egress_space" { ] } ``` + +## Staying up to date + +Note that [Terraform doesn't support version constraints for github module sources](https://developer.hashicorp.com/terraform/language/modules/sources#github). To use the latest available versions of these modules using a version constraint, use our [semver module](./semver). \ No newline at end of file diff --git a/semver/README.md b/semver/README.md new file mode 100644 index 0000000..4375c87 --- /dev/null +++ b/semver/README.md @@ -0,0 +1,35 @@ +# terraform-cloudgov/semver + +Find a tag for this repository matching NPM-style version constraints + +## Example + +```terraform +# Specify a version constraint for each module we plan to use +locals { + module_versions = { + database = ">0.5.0", + s3 = ">0.6.0" + } +} + +# Divine the most recent versions matching those constraints... +module "version" { + for_each = local.module_versions + source = "github.com/18f/terraform-cloudgov//semver" + version_constraint = each.value +} + +# ...then refer to the source for those modules using the calculated versions. + +module "database" { + source = "github.com/18f/terraform-cloudgov//database?ref=v${module.version["database"].target_version}" + # [...] +} + +module "s3" { + source = "github.com/18f/terraform-cloudgov//s3?ref=v${module.version["s3"].target_version}" + # [...] +} +``` + diff --git a/semver/main.tf b/semver/main.tf new file mode 100644 index 0000000..d81ab85 --- /dev/null +++ b/semver/main.tf @@ -0,0 +1,28 @@ +# This is just a thin wrapper around the following module, prefilling +# the 18f/terraform-cloudgov repository +# https://registry.terraform.io/modules/rhythmictech/find-release-by-semver + +# Just accepts the one parameter +variable "version_constraint" { + type = string + description = "The NPM-style version constraint you want to use to find the right version" +} + +module "find-cloudgov-module-version" { + source = "rhythmictech/find-release-by-semver/github" + version = "~> 1.1.2" + + repo_name = "terraform-cloudgov" + repo_owner = "18f" + version_constraint = var.version_constraint +} + +output "target_version" { + description = "Version matched to constraint" + value = module.find-cloudgov-module-version.target_version +} + +output "version_info" { + description = "All available info about the target release" + value = module.find-cloudgov-module-version.version_info +} diff --git a/semver/tests/default/default.tf b/semver/tests/default/default.tf new file mode 100644 index 0000000..1cc6b77 --- /dev/null +++ b/semver/tests/default/default.tf @@ -0,0 +1,46 @@ +terraform { + required_providers { + test = { + # See https://developer.hashicorp.com/terraform/language/modules/testing-experiment#writing-tests-for-a-module + source = "terraform.io/builtin/test" + } + http = { + source = "hashicorp/http" + } + } +} + +# Fixture constraints +locals { + module_versions = { + greaterthan = ">0.5.0", + } + + latest_tag = trimprefix(jsondecode(data.http.latest_version.response_body).tag_name, "v") +} + +# Divine the most recent versions matching fixture +module "version" { + for_each = local.module_versions + source = "../.." + version_constraint = each.value +} + +data "http" "latest_version" { + url = "https://api.github.com/repos/18f/terraform-cloudgov/releases/latest" + + request_headers = { + accept = "vnd.github+json" + } +} + +resource "test_assertions" "greater-than-is-latest" { + component = "outputs" + equal "target_version" { + description = "greater than should always be the latest in the repo" + got = module.version["greaterthan"].target_version + # want = "0.7.0" # Need to programmatically derive this + want = local.latest_tag + } +} + From aa1262f18281c6517260c50896cbe73f5f5b9081 Mon Sep 17 00:00:00 2001 From: Bret Mogilefsky Date: Mon, 18 Sep 2023 12:30:27 -0700 Subject: [PATCH 2/2] Use semver module in README.md --- README.md | 39 ++++++++++++++++++++++++--------- semver/README.md | 5 ++--- semver/tests/default/default.tf | 6 +++-- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5d62bb6..1199b49 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,29 @@ Terraform modules for working with cloud.gov commonly used by [18f/rails-template](https://github.com/18f/rails-template) based apps +## Usage + +Specify acceptable versions of these modules using an [NPM-style version constraint](https://github.com/npm/node-semver#versions), using our [semver module](./semver). ([Terraform doesn't support version constraints for github-hosted modules](https://developer.hashicorp.com/terraform/language/modules/sources#github).) + +```terraform +# Specify a (NPM-style) version constraint for the modules you use +locals { + module_versions = { + database = "^0.x", # major version 0 + s3 = "^0.x" # major version 0 + } +} + +# Find the most recent versions matching those constraints... +module "version" { + for_each = local.module_versions + source = "github.com/18f/terraform-cloudgov//semver" + version_constraint = each.value +} + +# ...then refer to the source for those modules using the calculated versions, as demonstrated below +``` + ## Module Examples ### database @@ -10,7 +33,7 @@ Creates an RDS database based on the `rds_plan_name` variable and outputs the `i ``` module "database" { - source = "github.com/18f/terraform-cloudgov//database?ref=v0.6.0" + source = "github.com/18f/terraform-cloudgov//database?ref=v${module.version["database"].target_version}" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -25,7 +48,7 @@ Creates a Elasticache redis instance and outputs the `instance_id` for use elsew ``` module "redis" { - source = "github.com/18f/terraform-cloudgov//redis?ref=v0.6.0" + source = "github.com/18f/terraform-cloudgov//redis?ref=v${module.version["redis"].target_version}" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -40,7 +63,7 @@ Creates an s3 bucket and outputs the `bucket_id` for use elsewhere. ``` module "s3" { - source = "github.com/18f/terraform-cloudgov//s3?ref=v0.6.0" + source = "github.com/18f/terraform-cloudgov//s3?ref=v${module.version["s3"].target_version}" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -58,7 +81,7 @@ Note that the domain must be created in cloud.gov by an OrgManager before this m ``` module "domain" { - source = "github.com/18f/terraform-cloudgov//domain?ref=v0.7.0" + source = "github.com/18f/terraform-cloudgov//domain?ref=v${module.version["domain"].target_version}" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -79,7 +102,7 @@ Notes: ``` module "clamav" { - source = "github.com/18f/terraform-cloudgov//clamav?ref=v0.6.0" + source = "github.com/18f/terraform-cloudgov//clamav?ref=v${module.version["clamav"].target_version}" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -102,7 +125,7 @@ Creates a new cloud.gov space, such as when creating an egress space, and output ``` module "egress_space" { - source = "github.com/18f/terraform-cloudgov//cg_space?ref=v0.6.0" + source = "github.com/18f/terraform-cloudgov//cg_space?ref=v${module.version["cg_space"].target_version}" cf_org_name = local.cf_org_name cf_space_name = "${local.cf_space_name}-egress" @@ -117,7 +140,3 @@ module "egress_space" { ] } ``` - -## Staying up to date - -Note that [Terraform doesn't support version constraints for github module sources](https://developer.hashicorp.com/terraform/language/modules/sources#github). To use the latest available versions of these modules using a version constraint, use our [semver module](./semver). \ No newline at end of file diff --git a/semver/README.md b/semver/README.md index 4375c87..721ce68 100644 --- a/semver/README.md +++ b/semver/README.md @@ -8,8 +8,8 @@ Find a tag for this repository matching NPM-style version constraints # Specify a version constraint for each module we plan to use locals { module_versions = { - database = ">0.5.0", - s3 = ">0.6.0" + database = "^0.x", # major version 0 + s3 = "^0.x" # major version 0 } } @@ -32,4 +32,3 @@ module "s3" { # [...] } ``` - diff --git a/semver/tests/default/default.tf b/semver/tests/default/default.tf index 1cc6b77..81caa59 100644 --- a/semver/tests/default/default.tf +++ b/semver/tests/default/default.tf @@ -13,7 +13,10 @@ terraform { # Fixture constraints locals { module_versions = { - greaterthan = ">0.5.0", + # This test will break after we tag something higher than 1.0.0; fix and + # expand these tests then! + # https://github.com/npm/node-semver#tilde-ranges-123-12-1 + greaterthan = "~0", } latest_tag = trimprefix(jsondecode(data.http.latest_version.response_body).tag_name, "v") @@ -39,7 +42,6 @@ resource "test_assertions" "greater-than-is-latest" { equal "target_version" { description = "greater than should always be the latest in the repo" got = module.version["greaterthan"].target_version - # want = "0.7.0" # Need to programmatically derive this want = local.latest_tag } }