diff --git a/.gitignore b/.gitignore index 4f8d1a8..c95322c 100644 --- a/.gitignore +++ b/.gitignore @@ -138,4 +138,8 @@ dist .terraform/ terraform.tfstate terraform.tfstate.backup -*.tfvars \ No newline at end of file +*.tfvars + +# Example secrets and lock files +examples/scope-definition/terraform.tfvars.json +examples/scope-definition/.terraform.lock.hcl \ No newline at end of file diff --git a/examples/scope-definition/main.tf b/examples/scope-definition/main.tf new file mode 100644 index 0000000..a966ca1 --- /dev/null +++ b/examples/scope-definition/main.tf @@ -0,0 +1,56 @@ +terraform { + required_providers { + nullplatform = { + source = "nullplatform/nullplatform" + } + http = { + source = "hashicorp/http" + } + } +} + +provider "nullplatform" { + api_key = var.np_api_key +} + +################################################################################ +# Example: scope-definition module +# +# This example shows how to use the scope-definition module to create a scope +# type, service specification, and action specifications from templates stored +# in a GitHub repository. +# +# Set create_scope_configuration = true when the scope repository contains a +# /specs/scope-configuration.json.tpl file. This will also create a +# nullplatform_provider_specification resource from the template. +################################################################################ + +module "scope_definition" { + source = "../../modules/nullplatform/scope-definition" + + nrn = var.nrn + organization_nrn = var.organization_nrn + + github_repo_url = var.github_repo_url + github_ref = var.github_ref + github_scope_path = var.github_scope_path + + scope_name = var.scope_name + scope_description = var.scope_description + + create_scope_configuration = var.create_scope_configuration + + np_api_key = var.np_api_key +} + +output "service_specification_id" { + value = module.scope_definition.service_specification_id +} + +output "scope_type_id" { + value = module.scope_definition.scope_type_id +} + +output "provider_specification_id" { + value = module.scope_definition.provider_specification_id +} diff --git a/examples/scope-definition/terraform.tfvars.json.example b/examples/scope-definition/terraform.tfvars.json.example new file mode 100644 index 0000000..98fb5d5 --- /dev/null +++ b/examples/scope-definition/terraform.tfvars.json.example @@ -0,0 +1,11 @@ +{ + "np_api_key": "your-nullplatform-api-key", + "nrn": "organization=1234567890:account=987654321", + "organization_nrn": "organization=1234567890", + "github_repo_url": "https://github.com/nullplatform/scopes-static-files", + "github_ref": "main", + "github_scope_path": "static-files", + "scope_name": "my-scope", + "scope_description": "My custom scope type", + "create_scope_configuration": false +} diff --git a/examples/scope-definition/variables.tf b/examples/scope-definition/variables.tf new file mode 100644 index 0000000..34fb915 --- /dev/null +++ b/examples/scope-definition/variables.tf @@ -0,0 +1,43 @@ +variable "np_api_key" { + type = string + sensitive = true +} + +variable "nrn" { + type = string + description = "Nullplatform Resource Name (organization:account format)" +} + +variable "organization_nrn" { + type = string + description = "Organization NRN used when create_scope_configuration is true" + default = "" +} + +variable "github_repo_url" { + type = string + default = "https://github.com/nullplatform/scopes-static-files" +} + +variable "github_ref" { + type = string + default = "main" +} + +variable "github_scope_path" { + type = string + default = "static-files" +} + +variable "scope_name" { + type = string +} + +variable "scope_description" { + type = string +} + +variable "create_scope_configuration" { + type = bool + default = false +} diff --git a/modules/nullplatform/scope-definition/backend.tf b/modules/nullplatform/scope-definition/backend.tf index 8fda109..ebd7fc0 100644 --- a/modules/nullplatform/scope-definition/backend.tf +++ b/modules/nullplatform/scope-definition/backend.tf @@ -1,16 +1,16 @@ terraform { required_providers { nullplatform = { - source = "nullplatform/nullplatform" + source = "nullplatform/nullplatform" } http = { - source = "hashicorp/http" + source = "hashicorp/http" } external = { - source = "hashicorp/external" + source = "hashicorp/external" } null = { - source = "hashicorp/null" + source = "hashicorp/null" } } } diff --git a/modules/nullplatform/scope-definition/main.tf b/modules/nullplatform/scope-definition/main.tf index 1d9dac8..ead8c77 100644 --- a/modules/nullplatform/scope-definition/main.tf +++ b/modules/nullplatform/scope-definition/main.tf @@ -7,6 +7,12 @@ data "http" "service_spec_template" { url = "${var.github_repo_url}/raw/${var.github_ref}/${var.github_scope_path}/specs/service-spec.json.tpl" } +# Fetch scope configuration template (optional - may not exist for all scopes) +data "http" "scope_configuration_template" { + count = var.create_scope_configuration ? 1 : 0 + url = "${var.github_repo_url}/raw/${var.github_ref}/${var.github_scope_path}/specs/scope-configuration.json.tpl" +} + # Fetch action specification templates data "http" "action_templates" { for_each = toset(var.action_spec_names) @@ -18,22 +24,22 @@ data "http" "action_templates" { ################################################################################ locals { - # Process the template by replacing the template variables - # replace is done because some old templates contain gomplate placeholders - service_spec_rendered = replace( - data.http.service_spec_template.response_body, - "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", - "\"${var.nrn}\"" - ) - service_spec_parsed = jsondecode(local.service_spec_rendered) + # Process the template by replacing the template variables + # replace is done because some old templates contain gomplate placeholders + service_spec_rendered = replace( + data.http.service_spec_template.response_body, + "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", + "\"${var.nrn}\"" + ) + service_spec_parsed = jsondecode(local.service_spec_rendered) } # Create service specification resource "nullplatform_service_specification" "from_template" { name = local.service_spec_parsed.name visible_to = [var.nrn] - type = local.service_spec_parsed.type - attributes = jsonencode(local.service_spec_parsed.attributes) + type = local.service_spec_parsed.type + attributes = jsonencode(local.service_spec_parsed.attributes) use_default_actions = local.service_spec_parsed.use_default_actions selectors { @@ -56,6 +62,31 @@ locals { } } +################################################################################ +# Step 2.5: Process Scope Configuration (optional) +################################################################################ + +locals { + # Replace the NRN gomplate placeholder with the organization_nrn variable + scope_configuration_rendered = var.create_scope_configuration ? replace( + data.http.scope_configuration_template[0].response_body, + "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", + "\"${var.organization_nrn}\"" + ) : "{}" + scope_configuration = var.create_scope_configuration ? jsondecode(local.scope_configuration_rendered) : null +} + +resource "nullplatform_provider_specification" "from_scope_configuration" { + count = var.create_scope_configuration ? 1 : 0 + + name = local.scope_configuration.name + description = local.scope_configuration.description + category = local.scope_configuration.category + allow_dimensions = local.scope_configuration.allow_dimensions + visible_to = [var.organization_nrn] + schema = jsonencode(local.scope_configuration.schema) +} + ################################################################################ # Step 3: Process and Create Scope Type ################################################################################ @@ -79,19 +110,21 @@ resource "nullplatform_scope_type" "from_template" { # Process action templates - direct JSON parsing (they don't contain template variables) # replace is done because some old templates contain gomplate placeholders locals { + # Only parse templates that returned a 200 — missing files return an HTML 404 page action_specs_parsed = { for name in var.action_spec_names : name => jsondecode(replace( - data.http.action_templates[name].response_body, - "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", - "\"\"" + data.http.action_templates[name].response_body, + "/\"{{\\s+env.Getenv\\s+\".*\"\\s+}}\"/", + "\"\"" )) + if data.http.action_templates[name].status_code == 200 } } # Create action specifications resource "nullplatform_action_specification" "from_templates" { - for_each = toset(var.action_spec_names) + for_each = toset(keys(local.action_specs_parsed)) depends_on = [nullplatform_service_specification.from_template] service_specification_id = local.service_specification_id diff --git a/modules/nullplatform/scope-definition/outputs.tf b/modules/nullplatform/scope-definition/outputs.tf index d6bc51c..c702dd7 100644 --- a/modules/nullplatform/scope-definition/outputs.tf +++ b/modules/nullplatform/scope-definition/outputs.tf @@ -49,4 +49,14 @@ output "scope_name" { output "scope_description" { value = var.scope_description description = "The name of the scope definition" +} + +output "scope_configuration" { + value = local.scope_configuration + description = "Parsed scope configuration from scope-configuration.json.tpl, or null if not fetched" +} + +output "provider_specification_id" { + value = var.create_scope_configuration ? nullplatform_provider_specification.from_scope_configuration[0].id : null + description = "The ID of the created provider specification, or null if scope configuration was not fetched" } \ No newline at end of file diff --git a/modules/nullplatform/scope-definition/variables.tf b/modules/nullplatform/scope-definition/variables.tf index 062dcaa..9d90000 100644 --- a/modules/nullplatform/scope-definition/variables.tf +++ b/modules/nullplatform/scope-definition/variables.tf @@ -22,7 +22,7 @@ variable "github_ref" { variable "github_scope_path" { type = string default = "k8s" - description = "Path within the repository for the specific scope (e.g., k8s, ecs)" + description = "Path within the repository for the specific scope (e.g., k8s, ecs)" } variable "scope_name" { @@ -32,7 +32,7 @@ variable "scope_name" { variable "scope_description" { type = string description = "Description of the scope type to be created" -} +} variable "action_spec_names" { type = list(string) @@ -54,6 +54,18 @@ variable "action_spec_names" { description = "List of action specification template names to fetch and create" } +variable "organization_nrn" { + type = string + description = "Organization NRN used to replace the NRN placeholder in scope-configuration.json.tpl" + default = "" +} + +variable "create_scope_configuration" { + type = bool + default = false + description = "Whether to fetch and apply scope-configuration.json.tpl from the template repo. Set to true only if the file exists for this scope." +} + # NRN Patch Configuration variable "np_api_key" { type = string