├── .go-version ├── version └── VERSION ├── nomad ├── test-fixtures │ └── hello.txt ├── helper │ ├── pointer │ │ └── pointer.go │ ├── types.go │ ├── state_writer.go │ └── import.go ├── data_source_scheduler_config_test.go ├── data_source_variable.go ├── data_source_regions.go ├── data_source_namespaces.go ├── data_source_jwks_test.go ├── data_source_regions_test.go ├── data_source_namespaces_test.go ├── data_source_acl_policy.go ├── data_source_acl_tokens_test.go ├── data_source_deployments.go ├── data_source_job_parser.go ├── data_source_acl_roles_test.go ├── data_source_acl_token_test.go ├── data_source_acl_role_test.go ├── data_source_plugins.go ├── data_source_scaling_policy_test.go ├── data_source_scheduler_config.go ├── data_source_acl_role.go ├── data_source_datacenters.go ├── data_source_acl_policy_test.go ├── data_source_scaling_policy.go ├── data_source_acl_policies.go ├── data_source_node_pools_test.go ├── data_source_acl_roles.go ├── data_source_namespace.go ├── data_source_node_pool.go ├── data_source_acl_policies_test.go ├── data_source_scaling_policies.go ├── data_source_datacenters_test.go ├── data_source_volumes.go ├── data_source_acl_tokens.go ├── data_source_deployments_test.go ├── data_source_node_pools.go ├── data_source_acl_token.go ├── data_source_jwks.go ├── datasource_nomad_plugin.go ├── resource_acl_role_test.go ├── resource_sentinel_policy.go ├── data_source_allocations.go ├── data_source_node_pool_test.go ├── resource_acl_role.go └── resource_variable_test.go ├── CODEOWNERS ├── terraform-registry-manifest.json ├── .github ├── CODE_OF_CONDUCT.md ├── dependabot.yml ├── SUPPORT.md ├── pull_request_template.md ├── workflows │ ├── copywrite.yml │ ├── test.yaml │ └── jira-sync.yml ├── ISSUE_TEMPLATE.md └── ISSUE_TEMPLATE │ └── bug_report.md ├── .release ├── security-scan.hcl ├── release-metadata.hcl ├── terraform-provider-nomad-artifacts.hcl └── ci.hcl ├── scripts ├── stop-nomad.sh ├── gofmtcheck.sh ├── getnomad.sh ├── errcheck.sh ├── changelog-links.sh └── start-nomad.sh ├── main.go ├── .gitignore ├── website └── docs │ ├── d │ ├── acl_policy.html.markdown │ ├── job_parser.html.markdown │ ├── deployments.html.markdown │ ├── acl_role.html.markdown │ ├── acl_policies.html.markdown │ ├── scheduler_config.html.md │ ├── datacenters.html.md │ ├── acl_roles.html.markdown │ ├── plugins.html.markdown │ ├── namespaces.html.markdown │ ├── scaling_policy.html.markdown │ ├── variable.html.markdown │ ├── regions.html.markdown │ ├── jwks.html.markdown │ ├── volumes.html.markdown │ ├── namespace.html.markdown │ ├── scaling_policies.html.markdown │ ├── node_pool.html.markdown │ ├── acl_tokens.html.markdown │ ├── acl_token.html.markdown │ ├── node_pools.html.markdown │ ├── plugin.html.markdown │ ├── allocations.html.markdown │ ├── job.html.markdown │ └── dynamic_host_volume.html.markdown │ └── r │ ├── acl_role.html.markdown │ ├── variable.html.markdown │ ├── node_pool.html.markdown │ ├── sentinel_policy.html.markdown │ ├── acl_policy.html.markdown │ ├── quota_specification.html.markdown │ ├── scheduler_config.html.markdown │ ├── acl_binding_rule.html.markdown │ ├── acl_token.html.markdown │ ├── namespace.html.markdown │ └── dynamic_host_volume_registration.html.markdown ├── .copywrite.hcl ├── GNUmakefile ├── README.md └── go.mod /.go-version: -------------------------------------------------------------------------------- 1 | 1.25.4 2 | -------------------------------------------------------------------------------- /version/VERSION: -------------------------------------------------------------------------------- 1 | 2.5.2 2 | -------------------------------------------------------------------------------- /nomad/test-fixtures/hello.txt: -------------------------------------------------------------------------------- 1 | Hello :) 2 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # codeowner default 2 | * @hashicorp/github-nomad-core @hashicorp/nomad-eng 3 | -------------------------------------------------------------------------------- /terraform-registry-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "metadata": { 4 | "protocol_versions": ["5.0"] 5 | } 6 | } -------------------------------------------------------------------------------- /nomad/helper/pointer/pointer.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package pointer 5 | 6 | // Of returns a pointer to a. 7 | func Of[A any](a A) *A { 8 | return &a 9 | } 10 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | HashiCorp Community Guidelines apply to you when interacting with the community here on GitHub and contributing code. 4 | 5 | Please read the full text at https://www.hashicorp.com/community-guidelines 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | day: "sunday" 8 | time: "09:00" 9 | labels: 10 | - "theme/dependencies" 11 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | Terraform is a mature project with a growing community. There are active, dedicated people willing to help you through various mediums. 4 | 5 | Take a look at those mediums listed at https://www.terraform.io/community.html 6 | -------------------------------------------------------------------------------- /nomad/data_source_scheduler_config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | // The scheduler config needs to be changed sequencially. 7 | // Tests for this data source are defined in resource_scheduler_config_test.go. 8 | -------------------------------------------------------------------------------- /.release/security-scan.hcl: -------------------------------------------------------------------------------- 1 | # Reference: https://github.com/hashicorp/security-scanner/blob/main/CONFIG.md#binary (private repository) 2 | 3 | binary { 4 | secrets { 5 | all = true 6 | } 7 | go_modules = true 8 | osv = true 9 | oss_index = false 10 | nvd = false 11 | } 12 | -------------------------------------------------------------------------------- /nomad/helper/types.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package helper 5 | 6 | func ToMapStringString(m any) map[string]string { 7 | mss := map[string]string{} 8 | for k, v := range m.(map[string]any) { 9 | mss[k] = v.(string) 10 | } 11 | return mss 12 | } 13 | -------------------------------------------------------------------------------- /scripts/stop-nomad.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright IBM Corp. 2016, 2025 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | if [ -e /tmp/nomad-test.pid ]; then 6 | echo "Stopping nomad" 7 | sudo kill "$(cat /tmp/nomad-test.pid)" && sudo rm -f /tmp/nomad-test.pid 8 | fi 9 | 10 | rm -f /tmp/nomad-test.token 11 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [ ] If a change needs to be reverted, we will roll out an update to the code within 7 days. 4 | 5 | ## Changes to Security Controls 6 | 7 | Are there any changes to security controls (access controls, encryption, logging) in this pull request? If so, explain. 8 | 9 | -------------------------------------------------------------------------------- /.release/release-metadata.hcl: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | url_source_repository = "https://github.com/hashicorp/terraform-provider-nomad" 5 | url_project_website = "https://registry.terraform.io/providers/hashicorp/nomad" 6 | url_license = "https://github.com/hashicorp/terraform-provider-nomad/blob/main/LICENSE" 7 | url_release_notes = "https://github.com/hashicorp/terraform-provider-nomad/blob/main/CHANGELOG.md" 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" 9 | 10 | "github.com/hashicorp/terraform-provider-nomad/nomad" 11 | ) 12 | 13 | func main() { 14 | plugin.Serve(&plugin.ServeOpts{ 15 | ProviderFunc: func() *schema.Provider { return nomad.Provider() }, 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /scripts/gofmtcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright IBM Corp. 2016, 2025 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | # Check gofmt 7 | echo "==> Checking that code complies with gofmt requirements..." 8 | gofmt_files=$(gofmt -l `find . -name '*.go'`) 9 | if [[ -n ${gofmt_files} ]]; then 10 | echo 'gofmt needs running on the following files:' 11 | echo "${gofmt_files}" 12 | echo "You can use the command: \`make fmt\` to reformat code." 13 | exit 1 14 | fi 15 | 16 | exit 0 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | *.exe 3 | .DS_Store 4 | example.tf 5 | terraform.tfplan 6 | terraform.tfstate 7 | bin/ 8 | vendor/ 9 | modules-dev/ 10 | /pkg/ 11 | website/.vagrant 12 | website/.bundle 13 | website/build 14 | website/node_modules 15 | .vagrant/ 16 | *.backup 17 | ./*.tfstate 18 | .terraform/ 19 | *.log 20 | *.bak 21 | *~ 22 | .*.swp 23 | .idea 24 | *.iml 25 | *.test 26 | *.iml 27 | 28 | website/vendor 29 | 30 | # Test exclusions 31 | !command/test-fixtures/**/*.tfstate 32 | !command/test-fixtures/**/.terraform/ 33 | -------------------------------------------------------------------------------- /scripts/getnomad.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright IBM Corp. 2016, 2025 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | set -e 7 | 8 | NOMAD_VERSION='1.10.0' 9 | if [[ -n "$NOMAD_LICENSE" || -n "$NOMAD_LICENSE_PATH" ]]; then 10 | NOMAD_VERSION=${NOMAD_VERSION}+ent 11 | fi 12 | NOMAD_BINARY=https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip 13 | 14 | curl -L $NOMAD_BINARY > nomad.zip 15 | sudo unzip -o nomad.zip -d /usr/local/bin 16 | sudo chmod 0755 /usr/local/bin/nomad 17 | sudo chown root:root /usr/local/bin/nomad 18 | 19 | which nomad 20 | nomad -v 21 | -------------------------------------------------------------------------------- /.github/workflows/copywrite.yml: -------------------------------------------------------------------------------- 1 | name: Check Copywrite Headers 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | copywrite: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 12 | - uses: hashicorp/setup-copywrite@867a1a2a064a0626db322392806428f7dc59cb3e # v1.1.2 13 | name: Setup Copywrite 14 | with: 15 | version: v0.16.4 16 | archive-checksum: c299f830e6eef7e126a3c6ef99ac6f43a3c132d830c769e0d36fa347fa1af254 17 | - name: Check Header Compliance 18 | run: copywrite headers --plan 19 | permissions: 20 | contents: read 21 | -------------------------------------------------------------------------------- /website/docs/d/acl_policy.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_acl_policy" 4 | sidebar_current: "docs-nomad-datasource-acl_policy" 5 | description: |- 6 | Retrieve information on an ACL Policy. 7 | --- 8 | 9 | # nomad_acl_policy 10 | 11 | Retrieve information on an ACL Policy. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_acl_policy" "my_policy" { 17 | name = "my-policy" 18 | } 19 | ``` 20 | 21 | ## Attribute Reference 22 | 23 | The following attributes are exported: 24 | 25 | - `name` `(string)` - the name of the ACL Policy. 26 | - `description` `(string)` - the description of the ACL Policy. 27 | - `rules` `(string)` - the ACL Policy rules in HCL format. 28 | -------------------------------------------------------------------------------- /nomad/helper/state_writer.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package helper 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/go-multierror" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | ) 12 | 13 | type StateWriter struct { 14 | d *schema.ResourceData 15 | mErr *multierror.Error 16 | } 17 | 18 | func NewStateWriter(d *schema.ResourceData) *StateWriter { 19 | return &StateWriter{d: d} 20 | } 21 | 22 | func (sw *StateWriter) Set(key string, value interface{}) { 23 | err := sw.d.Set(key, value) 24 | if err != nil { 25 | sw.mErr = multierror.Append(sw.mErr, fmt.Errorf("failed to set %q: %v", key, err)) 26 | } 27 | } 28 | 29 | func (sw *StateWriter) Error() error { 30 | return sw.mErr.ErrorOrNil() 31 | } 32 | -------------------------------------------------------------------------------- /.copywrite.hcl: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. 2016, 2025 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | schema_version = 1 5 | 6 | project { 7 | license = "MPL-2.0" 8 | copyright_year = 2024 9 | 10 | header_ignore = [ 11 | # changie tooling configuration and CHANGELOG entries (prose) 12 | ".changes/unreleased/*.yaml", 13 | ".changie.yaml", 14 | 15 | # examples used within documentation (prose) 16 | "examples/**", 17 | 18 | # GitHub issue template configuration 19 | ".github/ISSUE_TEMPLATE/*.yml", 20 | 21 | # GitHub Actions workflow-specific configurations 22 | ".github/labeler-*.yml", 23 | 24 | # golangci-lint tooling configuration 25 | ".golangci.yml", 26 | 27 | # Release Engineering tooling configuration 28 | ".release/*.hcl", 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /website/docs/d/job_parser.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_job_parser" 4 | sidebar_current: "docs-nomad-datasource-job-parser" 5 | description: |- 6 | Parse a HCL jobspec and produce the equivalent JSON encoded job. 7 | --- 8 | 9 | # nomad_job_parser 10 | 11 | Parse a HCL jobspec and produce the equivalent JSON encoded job. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_job_parser" "my_job" { 17 | hcl = file("${path.module}/jobspec.hcl") 18 | canonicalize = false 19 | } 20 | ``` 21 | 22 | ## Attribute Reference 23 | 24 | The following attributes are exported: 25 | 26 | - `hcl` `(string)` - the HCL definition of the job. 27 | - `canonicalize` `(boolean: true)` - flag to enable setting any unset fields to their default values. 28 | - `json` `(string)` - the parsed job as JSON string. 29 | -------------------------------------------------------------------------------- /.release/terraform-provider-nomad-artifacts.hcl: -------------------------------------------------------------------------------- 1 | schema = 1 2 | artifacts { 3 | # This should match the `matrix` in .github/workflows/build.yml 4 | zip = [ 5 | "terraform-provider-nomad_${version}_darwin_amd64.zip", 6 | "terraform-provider-nomad_${version}_darwin_arm64.zip", 7 | "terraform-provider-nomad_${version}_freebsd_386.zip", 8 | "terraform-provider-nomad_${version}_freebsd_amd64.zip", 9 | "terraform-provider-nomad_${version}_freebsd_arm.zip", 10 | "terraform-provider-nomad_${version}_linux_386.zip", 11 | "terraform-provider-nomad_${version}_linux_amd64.zip", 12 | "terraform-provider-nomad_${version}_linux_arm.zip", 13 | "terraform-provider-nomad_${version}_linux_arm64.zip", 14 | "terraform-provider-nomad_${version}_windows_386.zip", 15 | "terraform-provider-nomad_${version}_windows_amd64.zip", 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /scripts/errcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright IBM Corp. 2016, 2025 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | # Check gofmt 7 | echo "==> Checking for unchecked errors..." 8 | 9 | if ! which errcheck > /dev/null; then 10 | echo "==> Installing errcheck..." 11 | go get -u github.com/kisielk/errcheck 12 | fi 13 | 14 | err_files=$(errcheck -ignoretests \ 15 | -ignore 'github.com/hashicorp/terraform/helper/schema:Set' \ 16 | -ignore 'bytes:.*' \ 17 | -ignore 'io:Close|Write' \ 18 | $(go list ./...)) 19 | 20 | if [[ -n ${err_files} ]]; then 21 | echo 'Unchecked errors found in the following places:' 22 | echo "${err_files}" 23 | echo "Please handle returned errors. You can check directly with \`make errcheck\`" 24 | exit 1 25 | fi 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /website/docs/d/deployments.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_deployments" 4 | sidebar_current: "docs-nomad-datasource-deployments" 5 | description: |- 6 | Retrieve a list of deployments and a summary of their attributes. 7 | --- 8 | 9 | # nomad_deployments 10 | 11 | Retrieve a list of deployments in Nomad. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_deployments" "example" {} 17 | ``` 18 | 19 | ## Attribute Reference 20 | 21 | The following attributes are exported: 22 | 23 | * `deployments`: `list of maps` a list of deployments in the cluster. 24 | * `ID`: `string` Deployment ID. 25 | * `JobID`: `string` Job ID associated with the deployment. 26 | * `JobVersion`: `string` Job version. 27 | * `Status`: `string` Deployment status. 28 | * `StatusDescription`: `string` Detailed description of the deployment's status. 29 | -------------------------------------------------------------------------------- /website/docs/d/acl_role.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_acl_role" 4 | sidebar_current: "docs-nomad-datasource-acl-role" 5 | description: |- 6 | Get information on an ACL Role. 7 | --- 8 | 9 | # nomad_acl_role 10 | 11 | Get information on an ACL Role. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_acl_role" "example" { 17 | id = "aa534e09-6a07-0a45-2295-a7f77063d429" 18 | } 19 | ``` 20 | 21 | ## Argument Reference 22 | 23 | The following arguments are supported: 24 | 25 | * `id`: `(string)` The unique identifier of the ACL Role. 26 | 27 | ## Attributes Reference 28 | 29 | The following attributes are exported: 30 | 31 | * `id` `(string)` - The ACL Role unique identifier. 32 | * `name` `(string)` - Unique name of the ACL role. 33 | * `description` `(string)` - The description of the ACL Role. 34 | * `policies` `(set)` - The policies applied to the role. 35 | -------------------------------------------------------------------------------- /website/docs/d/acl_policies.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_acl_policies" 4 | sidebar_current: "docs-nomad-datasource-acl_policies" 5 | description: |- 6 | Retrieve a list of ACL Policies. 7 | --- 8 | 9 | # nomad_acl_policies 10 | 11 | Retrieve a list of ACL Policies. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_acl_policies" "example" { 17 | prefix = "prod" 18 | } 19 | ``` 20 | 21 | ## Argument Reference 22 | 23 | The following arguments are supported: 24 | 25 | * `prefix`: `(string)` An optional string to filter ACL policies based on name prefix. If not provided, all policies are returned. 26 | 27 | ## Attribute Reference 28 | 29 | The following attributes are exported: 30 | 31 | * `policies`: `list of maps` a list of ACL policies. 32 | * `name` `(string)` - the name of the ACL Policy. 33 | * `description` `(string)` - the description of the ACL Policy. 34 | 35 | -------------------------------------------------------------------------------- /website/docs/d/scheduler_config.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_scheduler_config" 4 | sidebar_current: "docs-nomad-datasource-scheduler-config" 5 | description: |- 6 | Retrieve the cluster's scheduler configuration. 7 | --- 8 | 9 | # nomad_scheduler_config 10 | 11 | Retrieve the cluster's [scheduler configuration](https://www.nomadproject.io/api-docs/operator#sample-response-3). 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_scheduler_config" "global" {} 17 | ``` 18 | 19 | ## Attribute Reference 20 | 21 | The following attributes are exported: 22 | 23 | * `memory_oversubscription_enabled` `(bool: false)` - When `true`, tasks may exceed their reserved memory limit. 24 | * `scheduler_algorithm` `(string)` - Specifies whether scheduler binpacks or spreads allocations on available nodes. 25 | * `preemption_config` `(map[string]bool)` - Options to enable preemption for various schedulers. 26 | -------------------------------------------------------------------------------- /nomad/data_source_variable.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "github.com/hashicorp/nomad/api" 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | ) 10 | 11 | func dataSourceVariable() *schema.Resource { 12 | return &schema.Resource{ 13 | Read: resourceVariableRead, 14 | 15 | Schema: map[string]*schema.Schema{ 16 | "path": { 17 | Description: "The path of the variable", 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "namespace": { 22 | Description: "Variable namespace", 23 | Type: schema.TypeString, 24 | Optional: true, 25 | Default: api.DefaultNamespace, 26 | }, 27 | "items": { 28 | Description: "A map of values from the stored variable", 29 | Type: schema.TypeMap, 30 | Computed: true, 31 | Sensitive: true, 32 | }, 33 | }, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /website/docs/d/datacenters.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_datacenters" 4 | sidebar_current: "docs-nomad-datasource-datacenters" 5 | description: |- 6 | Retrieve a list of datacenters. 7 | --- 8 | 9 | # nomad_datacenters 10 | 11 | Retrieve a list of datacenters. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_datacenters" "datacenters" { 17 | prefix = "prod" 18 | ignore_down_nodes = true 19 | } 20 | ``` 21 | 22 | ## Argument Reference 23 | 24 | The following arguments are supported: 25 | 26 | * `prefix` `(string)`: An optional string to filter datacenters based on name prefix. If not provided, all datacenters are returned. 27 | * `ignore_down_nodes` `(bool: false)`: An optional flag that, if set to `true` will ignore down nodes when compiling the list of datacenters. 28 | 29 | ## Attribute Reference 30 | 31 | The following attributes are exported: 32 | 33 | * `datacenters`: `list(string)` a list of datacenters. 34 | -------------------------------------------------------------------------------- /website/docs/d/acl_roles.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_acl_roles" 4 | sidebar_current: "docs-nomad-datasource-acl_roles" 5 | description: |- 6 | Retrieve a list of ACL Roles. 7 | --- 8 | 9 | # nomad_acl_roles 10 | 11 | Retrieve a list of ACL Roles. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_acl_roles" "example" { 17 | prefix = "a242" 18 | } 19 | ``` 20 | 21 | ## Argument Reference 22 | 23 | The following arguments are supported: 24 | 25 | * `prefix`: `(string)` An optional string to filter ACL Roles based on ID 26 | prefix. If not provided, all policies are returned. 27 | 28 | ## Attribute Reference 29 | 30 | The following attributes are exported: 31 | 32 | * `roles`: `list of maps` a list of ACL Roles. 33 | * `id` `(string)` - The ACL Role unique identifier. 34 | * `name` `(string)` - Unique name of the ACL role. 35 | * `description` `(string)` - The description of the ACL Role. 36 | * `policies` `(set)` - The policies applied to the role. 37 | -------------------------------------------------------------------------------- /website/docs/d/plugins.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_plugins" 4 | sidebar_current: "docs-nomad-datasource-plugins" 5 | description: |- 6 | Retrieve a list of plugins. 7 | --- 8 | 9 | # nomad_plugins 10 | 11 | Retrieve a list of dynamic plugins in Nomad. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_plugins" "example" {} 17 | ``` 18 | 19 | ## Attribute Reference 20 | 21 | The following attributes are exported: 22 | 23 | * `plugins`: `(list of maps)` a list of dynamic plugins registered in the cluster. 24 | * `id`: `(string)` ID for the plugin. 25 | * `provider`: `(string)` Plugin provider vendor. 26 | * `controller_required`: `(boolean)` Whether a controller is required. 27 | * `controllers_healthy`: `(integer)` Number of healthy controllers. 28 | * `controllers_expected`: `(integer)` Number of expected controllers. 29 | * `nodes_healthy`: `(integer)` Number of nodes with a healthy client. 30 | * `nodes_expected`: `(integer)` Expectec number of nodes with a client. 31 | -------------------------------------------------------------------------------- /website/docs/d/namespaces.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_namespaces" 4 | sidebar_current: "docs-nomad-datasource-namespaces" 5 | description: |- 6 | Retrieve a list of namespaces available in Nomad. 7 | --- 8 | 9 | # nomad_namespaces 10 | 11 | Retrieve a list of namespaces available in Nomad. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_namespaces" "namespaces" { 17 | } 18 | 19 | resource "nomad_acl_policy" "namespace" { 20 | count = "${length(data.nomad_namespaces.namespaces.namespaces)}" 21 | name = "namespace-${data.nomad_namespaces.namespaces[count.index]}" 22 | description = "Write to the namespace ${data.nomad_namespaces.namespaces[count.index]}" 23 | rules_hcl = <)` - The ID of the scaling policy. 26 | 27 | ## Attribute Reference 28 | 29 | The following attributes are exported: 30 | 31 | * `enabled` `(boolean)` - Whether or not the scaling policy is enabled. 32 | * `type` `(string)` - The scaling policy type. 33 | * `min` `(integer)` - The minimum value set in the scaling policy. 34 | * `max` `(integer)` - The maximum value set in the scaling policy. 35 | * `policy` `(string)` - The policy inside the scaling policy. 36 | * `target` `(map[string]string)` - The scaling policy target. 37 | -------------------------------------------------------------------------------- /website/docs/d/variable.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_variable" 4 | sidebar_current: "docs-nomad-datasource-variable" 5 | description: |- 6 | Get the information about a Nomad variable. 7 | --- 8 | 9 | # nomad_variable 10 | 11 | Get the information about a Nomad variable. 12 | 13 | ~> **Warning:** this data source will store the sensitive values from `items` 14 | in the Terraform's state file. Take care to 15 | [protect your state file](/docs/state/sensitive-data.html). 16 | 17 | ## Example Usage 18 | 19 | ```hcl 20 | resource "nomad_variable" "example" { 21 | path = "path/of/existing/variable" 22 | } 23 | ``` 24 | 25 | ## Argument Reference 26 | 27 | - `path` `(string)` - Path to the existing variable. 28 | - `namespace` `(string: "default")` - The namepsace in which the variable exists. 29 | 30 | ## Attribute Reference 31 | 32 | The following attributes are exported: 33 | - `path` `(string)` - The path at which the variable exists. 34 | - `namespace` `(string)` - The namespace in which the variable exists. 35 | - `items` `(map[string]string)` - Map of items in the variable. 36 | -------------------------------------------------------------------------------- /website/docs/d/regions.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_regions" 4 | sidebar_current: "docs-nomad-datasource-regions" 5 | description: |- 6 | Retrieve a list of regions available in Nomad. 7 | --- 8 | 9 | # nomad_regions 10 | 11 | Retrieve a list of regions available in Nomad. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_regions" "my_regions" {} 17 | 18 | data "template_file" "jobs" { 19 | count = length(data.nomad_regions.my_regions.regions) 20 | template = <)` - A human-friendly name for this ACL Role. 43 | 44 | - `description` `(string: "")` - A description of the ACL Role. 45 | 46 | - `policy` `(set: )` - A set of policy names to associate with this 47 | ACL Role. It may be used multiple times. 48 | -------------------------------------------------------------------------------- /nomad/helper/import.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package helper 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "strings" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | ) 13 | 14 | var ( 15 | missingNamespaceImportErr = errors.New("missing namespace, the import ID should follow the pattern @") 16 | missingIDImportErr = errors.New("missing resource ID, the import ID should follow the pattern @") 17 | ) 18 | 19 | // NamespacedImporterContext imports a namespaced resource that doesn't have 20 | // its namespace as part of the Terraform resource ID. 21 | func NamespacedImporterContext(_ context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { 22 | namespacedID := d.Id() 23 | sepIdx := strings.LastIndex(namespacedID, "@") 24 | if sepIdx == -1 { 25 | return nil, missingNamespaceImportErr 26 | } 27 | 28 | ns := namespacedID[sepIdx+1:] 29 | if len(ns) == 0 { 30 | return nil, missingNamespaceImportErr 31 | } 32 | 33 | id := namespacedID[:sepIdx] 34 | if len(id) == 0 { 35 | return nil, missingIDImportErr 36 | } 37 | 38 | d.SetId(id) 39 | d.Set("namespace", ns) 40 | 41 | return []*schema.ResourceData{d}, nil 42 | } 43 | -------------------------------------------------------------------------------- /website/docs/d/node_pool.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_node_pool" 4 | sidebar_current: "docs-nomad-datasource-node-pool" 5 | description: |- 6 | Get information about a node pool in Nomad. 7 | --- 8 | 9 | # nomad_node_pool 10 | 11 | Get information about a node pool in Nomad. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_node_pool" "dev" { 17 | name = "dev" 18 | } 19 | ``` 20 | 21 | ## Argument Reference 22 | 23 | - `name` `(string)` - The name of the node pool to fetch. 24 | 25 | ## Attribute Reference 26 | 27 | The following attributes are exported: 28 | 29 | - `description` `(string)` - The description of the node pool. 30 | - `meta` `(map[string]string)` - Arbitrary KV metadata associated with the 31 | node pool. 32 | - `scheduler_config` `(block)` - Scheduler configuration for the node pool. 33 | - `scheduler_algorithm` `(string)` - The scheduler algorithm used in the node 34 | pool. If empty or not defined the global cluster configuration is used. 35 | - `memory_oversubscription` `(string)` - Whether or not memory 36 | oversubscription is enabled in the node pool. If empty or not defined the 37 | global cluster configuration is used. 38 | 39 | -> This option differs from Nomad, where it's represented as a boolean, to 40 | allow distinguishing between memory oversubscription being disabled in the 41 | node pool and this property not being set. 42 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 13 | - name: Read Go version 14 | id: go-version 15 | run: echo "version=$(cat ./.go-version)" >> $GITHUB_OUTPUT 16 | - name: Install Go 17 | uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 18 | with: 19 | go-version: ${{ steps.go-version.outputs.version }} 20 | - name: Run go vet 21 | run: make vet 22 | - name: Install Nomad 23 | env: 24 | NOMAD_LICENSE: ${{ secrets.NOMAD_LICENSE }} 25 | run: | 26 | ./scripts/getnomad.sh 27 | - name: Start nomad 28 | env: 29 | NOMAD_LICENSE: ${{ secrets.NOMAD_LICENSE }} 30 | run: ./scripts/start-nomad.sh 31 | - name: Run acceptance tests 32 | run: NOMAD_TOKEN=${{ env.NOMAD_TOKEN }} make testacc 33 | env: 34 | NOMAD_TOKEN: 00000000-0000-0000-0000-000000000000 35 | - name: Stop nomad 36 | if: always() 37 | run: ./scripts/stop-nomad.sh 38 | - name: Make Nomad data dir and log file readable 39 | if: always() 40 | run: | 41 | sudo chmod -R 777 /tmp/nomad 42 | sudo chmod 666 /tmp/nomad.log 43 | -------------------------------------------------------------------------------- /website/docs/d/acl_tokens.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_acl_tokens" 4 | sidebar_current: "docs-nomad-datasource-acl-token" 5 | description: |- 6 | Get a list of ACL tokens. 7 | --- 8 | 9 | # nomad_acl_tokens 10 | 11 | Get a list of ACL tokens. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_acl_tokens" "tokens" { 17 | prefix = "a242" 18 | } 19 | ``` 20 | 21 | ## Argument Reference 22 | 23 | The following arguments are supported: 24 | 25 | * `prefix`: `(string)` Optional prefix to filter the tokens. 26 | 27 | ## Attributes Reference 28 | 29 | The following attributes are exported: 30 | 31 | * `acl_tokens`: `(list of objects)` The list of tokens found in the given prefix. 32 | 33 | The objects in the `acl_tokens` list have the following attributes: 34 | 35 | * `accessor_id`: `(TypeString)` Non-sensitive identifier for the token. 36 | * `name`: `(TypeString)` The name of the token. 37 | * `type`: `(TypeString)` The type of the token. 38 | * `policies`: `(list of strings)` The list of policies attached to the token. 39 | * `roles` `(set: [])` - The list of roles attached to the token. Each entry has 40 | `name` and `id` attributes. 41 | * `global`: `(bool)` Whether the token is replicated to all regions. 42 | * `create_time`: `(string)` Date and time the token was created at. 43 | * `expiration_time` `(string)` - The timestamp after which the token is 44 | considered expired and eligible for destruction. -------------------------------------------------------------------------------- /scripts/start-nomad.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright IBM Corp. 2016, 2025 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | set -e 6 | 7 | export NOMAD_TOKEN=00000000-0000-0000-0000-000000000000 8 | 9 | if [ ! -e /tmp/nomad-test.pid ]; then 10 | cat < /tmp/nomad-config.hcl 11 | log_file = "/tmp/nomad.log" 12 | 13 | plugin "docker" { 14 | config { 15 | allow_privileged = true 16 | } 17 | } 18 | EOF 19 | 20 | sudo -Eb bash -c 'nomad agent -dev -acl-enabled \ 21 | -data-dir=/tmp/nomad/data \ 22 | -config=/tmp/nomad-config.hcl \ 23 | echo $! > /tmp/nomad-test.pid' 24 | 25 | # Give some time for the process to initialize 26 | sleep 10 27 | 28 | retries=30 29 | while [ $retries -ge 0 ]; do 30 | echo $NOMAD_TOKEN | nomad acl bootstrap - 31 | if [ $? -eq 0 ]; then 32 | break 33 | fi 34 | sleep 5 35 | retries=$(( retries - 1 )) 36 | done 37 | 38 | # Run hostpath CSI plugin and wait for it to be healthy. 39 | nomad job run https://raw.githubusercontent.com/hashicorp/nomad/v1.8.0/demo/csi/hostpath/plugin.nomad 1>&2 40 | echo "Waiting for hostpath CSI plugin to become healthy" 1>&2 41 | retries=30 42 | while [ $retries -ge 0 ]; do 43 | nomad plugin status hostpath \ 44 | | grep -q "Nodes Healthy = 1" && break 45 | sleep 2 46 | retries=$(( retries - 1 )) 47 | done 48 | nomad plugin status hostpath 1>&2 49 | fi 50 | -------------------------------------------------------------------------------- /nomad/data_source_jwks_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "crypto/x509" 8 | "encoding/pem" 9 | "fmt" 10 | "regexp" 11 | "testing" 12 | 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 14 | ) 15 | 16 | const testAccNomadJWKSConfig = `data "nomad_jwks" "test" {}` 17 | 18 | func TestAccDataSourceNomadJWKS_Basic(t *testing.T) { 19 | dataSourceName := "data.nomad_jwks.test" 20 | expectedKeyCount := "1" 21 | 22 | resource.Test(t, resource.TestCase{ 23 | PreCheck: func() { testAccPreCheck(t) }, 24 | Providers: testProviders, 25 | Steps: []resource.TestStep{ 26 | { 27 | Config: testAccNomadJWKSConfig, 28 | }, 29 | { 30 | Config: testAccNomadJWKSConfig, 31 | Check: resource.ComposeTestCheckFunc( 32 | resource.TestCheckResourceAttr(dataSourceName, "keys.#", expectedKeyCount), 33 | resource.TestMatchResourceAttr(dataSourceName, "keys.0.key_type", regexp.MustCompile("RSA")), 34 | resource.TestCheckResourceAttr(dataSourceName, "pem_keys.#", expectedKeyCount), 35 | resource.TestCheckResourceAttrWith(dataSourceName, "pem_keys.0", validateKeyPEM), 36 | ), 37 | }, 38 | }, 39 | }) 40 | } 41 | 42 | func validateKeyPEM(keyPEM string) error { 43 | block, _ := pem.Decode([]byte(keyPEM)) 44 | if block == nil { 45 | return fmt.Errorf("failed to parse key PEM") 46 | } 47 | _, err := x509.ParsePKIXPublicKey(block.Bytes) 48 | return err 49 | } 50 | -------------------------------------------------------------------------------- /website/docs/r/variable.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_variable" 4 | sidebar_current: "docs-nomad-resource-variable" 5 | description: |- 6 | Manages the lifecycle of Nomad variables. 7 | --- 8 | 9 | # nomad_variable 10 | 11 | Creates and manages a variable, including it's contents, within a 12 | Nomad cluster. 13 | 14 | ~> **Warning:** this resource will store the sensitive values placed in 15 | `items` in the Terraform's state file. Take care to 16 | [protect your state file](/docs/state/sensitive-data.html). 17 | 18 | ## Example Usage 19 | 20 | Creating a variable in the default namespace: 21 | 22 | ```hcl 23 | resource "nomad_variable" "example" { 24 | path = "some/path/of/your/choosing" 25 | items = { 26 | example_key = "example_value" 27 | } 28 | } 29 | ``` 30 | 31 | Creating a variable in a custom namespace: 32 | 33 | ```hcl 34 | resource "nomad_namespace" "example" { 35 | name = "example" 36 | description = "Example namespace." 37 | } 38 | 39 | resource "nomad_variable" "example" { 40 | path = "some/path/of/your/choosing" 41 | namespace = nomad_namespace.example.name 42 | items = { 43 | example_key = "example_value" 44 | } 45 | } 46 | ``` 47 | 48 | ## Argument Reference 49 | 50 | - `path` `(string: )` - A unique path to create the variable at. 51 | - `namespace` `(string: "default")` - The namepsace to create the variable in. 52 | - `items` `(map[string]string: )` - An arbitrary map of items to create in the variable. 53 | -------------------------------------------------------------------------------- /nomad/data_source_regions_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 13 | ) 14 | 15 | func TestDataSourceRegions(t *testing.T) { 16 | resource.Test(t, resource.TestCase{ 17 | Providers: testProviders, 18 | PreCheck: func() { testAccPreCheck(t) }, 19 | Steps: []resource.TestStep{ 20 | { 21 | Config: testDataSourceRegions_config, 22 | Check: testDataSourceRegions_check, 23 | }, 24 | }, 25 | }) 26 | } 27 | 28 | var testDataSourceRegions_config = ` 29 | 30 | data "nomad_regions" "test" { 31 | } 32 | 33 | ` 34 | 35 | func testDataSourceRegions_check(s *terraform.State) error { 36 | resourceState := s.Modules[0].Resources["data.nomad_regions.test"] 37 | if resourceState == nil { 38 | return fmt.Errorf("resource not found in state %v", s.Modules[0].Resources) 39 | } 40 | 41 | iState := resourceState.Primary 42 | if iState == nil { 43 | return fmt.Errorf("resource has no primary instance") 44 | } 45 | 46 | results, err := strconv.ParseInt(iState.Attributes["regions.#"], 10, 64) 47 | if err != nil { 48 | return fmt.Errorf("expected integer in state, got %s (%T)", iState.Attributes["regions.#"], iState.Attributes["regions.#"]) 49 | } 50 | 51 | if results < 1 { 52 | return fmt.Errorf("got %d regions, expected at least 1", results) 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /nomad/data_source_namespaces_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | "testing" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 13 | ) 14 | 15 | func TestDataSourceNamespaces(t *testing.T) { 16 | resource.Test(t, resource.TestCase{ 17 | Providers: testProviders, 18 | PreCheck: func() { testAccPreCheck(t) }, 19 | Steps: []resource.TestStep{ 20 | { 21 | Config: testDataSourceNamespaces_config, 22 | Check: testDataSourceNamespaces_check, 23 | }, 24 | }, 25 | }) 26 | } 27 | 28 | var testDataSourceNamespaces_config = ` 29 | 30 | data "nomad_namespaces" "test" { 31 | } 32 | 33 | ` 34 | 35 | func testDataSourceNamespaces_check(s *terraform.State) error { 36 | resourceState := s.Modules[0].Resources["data.nomad_namespaces.test"] 37 | if resourceState == nil { 38 | return fmt.Errorf("resource not found in state %v", s.Modules[0].Resources) 39 | } 40 | 41 | iState := resourceState.Primary 42 | if iState == nil { 43 | return fmt.Errorf("resource has no primary instance") 44 | } 45 | 46 | results, err := strconv.ParseInt(iState.Attributes["namespaces.#"], 10, 64) 47 | if err != nil { 48 | return fmt.Errorf("expected integer in state, got %s (%T)", iState.Attributes["namespaces.#"], iState.Attributes["namespaces.#"]) 49 | } 50 | 51 | if results < 1 { 52 | return fmt.Errorf("got %d namespaces, expected at least 1", results) 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /website/docs/r/node_pool.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_node_pool" 4 | sidebar_current: "docs-nomad-resource-node-pool" 5 | description: |- 6 | Provisions a node pool within a Nomad cluster. 7 | --- 8 | 9 | # nomad_node_pool 10 | 11 | Provisions a node pool within a Nomad cluster. 12 | 13 | ## Example Usage 14 | 15 | Registering a node pool: 16 | 17 | ```hcl 18 | resource "nomad_node_pool" "dev" { 19 | name = "dev" 20 | description = "Nodes for the development environment." 21 | 22 | meta = { 23 | department = "Engineering" 24 | env = "dev" 25 | } 26 | } 27 | ``` 28 | 29 | ## Argument Reference 30 | 31 | The following arguments are supported: 32 | 33 | - `name` `(string)` - The name of the node pool. 34 | - `description` `(string)` - The description of the node pool. 35 | - `meta` `(map[string]string)` - Arbitrary KV metadata associated with the 36 | node pool. 37 | - `scheduler_config` `(block)` - Scheduler configuration for the node pool. 38 | - `scheduler_algorithm` `(string)` - The scheduler algorithm used in the node 39 | pool. Possible values are `binpack` or `spread`. If not defined the global 40 | cluster configuration is used. 41 | - `memory_oversubscription` `(string)` - Whether or not memory 42 | oversubscription is enabled in the node pool. Possible values are 43 | `"enabled"` or `"disabled"`. If not defined the global cluster 44 | configuration is used. 45 | 46 | -> This option differs from Nomad, where it's represented as a boolean, to 47 | allow distinguishing between memory oversubscription being disabled in the 48 | node pool and this property not being set. 49 | -------------------------------------------------------------------------------- /website/docs/d/acl_token.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_acl_token" 4 | sidebar_current: "docs-nomad-datasource-acl-token" 5 | description: |- 6 | Get information on an ACL token. 7 | --- 8 | 9 | # nomad_acl_token 10 | 11 | Get information on an ACL token. 12 | 13 | ~> **Warning:** this data source will store tokens in the Terraform state. Take care to 14 | [protect your state file](/docs/state/sensitive-data.html). 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | data "nomad_acl_token" "my_token" { 20 | accessor_id = "aa534e09-6a07-0a45-2295-a7f77063d429" 21 | } 22 | ``` 23 | 24 | ## Argument Reference 25 | 26 | The following arguments are supported: 27 | 28 | * `accessor_id`: `(string)` Non-sensitive identifier for this token. 29 | 30 | ## Attributes Reference 31 | 32 | The following attributes are exported: 33 | 34 | * `name`: `(string)` Non-sensitive identifier for this token. 35 | * `accessor_id`: `(string)` Non-sensitive identifier for this token. 36 | * `secret_id`: `(string)` The token value itself. 37 | * `type`: `(string)` The type of the token. 38 | * `policies`: `(list of strings)` List of policy names associated with this token. 39 | * `roles` `(set: [])` - The list of roles attached to the token. Each entry has 40 | `name` and `id` attributes. 41 | * `global`: `(bool)` Whether the token is replicated to all regions, or if it 42 | will only be used in the region it was created. 43 | * `create_time`: `(string)` Date and time the token was created. 44 | * `expiration_ttl`: `(string)` The expiration TTL for the token. 45 | * `expiration_time` `(string)` - The timestamp after which the token is 46 | considered expired and eligible for destruction. 47 | -------------------------------------------------------------------------------- /nomad/data_source_acl_policy.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strings" 10 | 11 | "github.com/hashicorp/nomad/api" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 13 | ) 14 | 15 | func dataSourceAclPolicy() *schema.Resource { 16 | return &schema.Resource{ 17 | Read: dataSourceAclPolicyRead, 18 | Schema: map[string]*schema.Schema{ 19 | "name": { 20 | Description: "Name", 21 | Type: schema.TypeString, 22 | Required: true, 23 | }, 24 | "description": { 25 | Description: "Description", 26 | Type: schema.TypeString, 27 | Computed: true, 28 | }, 29 | "rules": { 30 | Description: "ACL Rules in HCL format", 31 | Type: schema.TypeString, 32 | Computed: true, 33 | }, 34 | }, 35 | } 36 | } 37 | 38 | func dataSourceAclPolicyRead(d *schema.ResourceData, meta interface{}) error { 39 | providerConfig := meta.(ProviderConfig) 40 | client := providerConfig.client 41 | 42 | name := d.Get("name").(string) 43 | 44 | log.Printf("[DEBUG] Getting ACL Policy %q", name) 45 | policy, _, err := client.ACLPolicies().Info(name, &api.QueryOptions{}) 46 | if err != nil { 47 | // As of Nomad 0.4.1, the API client returns an error for 404 48 | // rather than a nil result, so we must check this way. 49 | if strings.Contains(err.Error(), "404") { 50 | d.SetId("") 51 | return nil 52 | } 53 | 54 | return fmt.Errorf("error getting ACL policy: %#v", err) 55 | } 56 | 57 | d.SetId(policy.Name) 58 | d.Set("name", policy.Name) 59 | d.Set("description", policy.Description) 60 | d.Set("rules", policy.Rules) 61 | 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /nomad/data_source_acl_tokens_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 10 | ) 11 | 12 | func TestDataSourceACLTokens_Basic(t *testing.T) { 13 | resourceName := "data.nomad_acl_tokens.test" 14 | 15 | resource.Test(t, resource.TestCase{ 16 | Providers: testProviders, 17 | PreCheck: func() { testAccPreCheck(t) }, 18 | Steps: []resource.TestStep{ 19 | { 20 | Config: testDataSourceACLTokensConfig, 21 | Check: resource.ComposeTestCheckFunc( 22 | resource.TestCheckResourceAttrSet(resourceName, "prefix"), 23 | resource.TestCheckResourceAttr(resourceName, "acl_tokens.#", "1"), 24 | resource.TestCheckResourceAttrSet(resourceName, "acl_tokens.0.accessor_id"), 25 | resource.TestCheckResourceAttr(resourceName, "acl_tokens.0.name", "Terraform Test Token"), 26 | resource.TestCheckResourceAttr(resourceName, "acl_tokens.0.type", "client"), 27 | resource.TestCheckResourceAttr(resourceName, "acl_tokens.0.policies.#", "2"), 28 | resource.TestCheckResourceAttr(resourceName, "acl_tokens.0.policies.0", "qa"), 29 | resource.TestCheckResourceAttr(resourceName, "acl_tokens.0.policies.1", "dev"), 30 | resource.TestCheckResourceAttr(resourceName, "acl_tokens.0.global", "false"), 31 | resource.TestCheckResourceAttrSet(resourceName, "acl_tokens.0.create_time"), 32 | ), 33 | }, 34 | }, 35 | }) 36 | } 37 | 38 | const testDataSourceACLTokensConfig = ` 39 | resource "nomad_acl_token" "test" { 40 | name = "Terraform Test Token" 41 | type = "client" 42 | policies = ["dev", "qa"] 43 | } 44 | 45 | data "nomad_acl_tokens" "test" { 46 | prefix = split("-", nomad_acl_token.test.accessor_id)[0] 47 | } 48 | ` 49 | -------------------------------------------------------------------------------- /nomad/data_source_deployments.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 13 | ) 14 | 15 | func dataSourceDeployments() *schema.Resource { 16 | return &schema.Resource{ 17 | Read: dataSourceDeploymentsRead, 18 | Schema: map[string]*schema.Schema{ 19 | 20 | "deployments": { 21 | Description: "Deployments", 22 | Computed: true, 23 | Type: schema.TypeList, 24 | Elem: &schema.Schema{Type: schema.TypeMap}, 25 | }, 26 | }, 27 | } 28 | } 29 | 30 | func dataSourceDeploymentsRead(d *schema.ResourceData, meta interface{}) error { 31 | providerConfig := meta.(ProviderConfig) 32 | client := providerConfig.client 33 | 34 | log.Printf("[DEBUG] Getting deployments...") 35 | deployment_list, _, err := client.Deployments().List(nil) 36 | if err != nil { 37 | // As of Nomad 0.4.1, the API client returns an error for 404 38 | // rather than a nil result, so we must check this way. 39 | if strings.Contains(err.Error(), "404") { 40 | return err 41 | } 42 | 43 | return fmt.Errorf("error checking for deployments: %#v", err) 44 | } 45 | 46 | var deployments []map[string]interface{} 47 | 48 | for _, deployment := range deployment_list { 49 | entry := make(map[string]interface{}) 50 | entry["ID"] = deployment.ID 51 | entry["JobID"] = deployment.JobID 52 | entry["JobVersion"] = strconv.Itoa(int(deployment.JobVersion)) 53 | entry["Status"] = deployment.Status 54 | entry["StatusDescription"] = deployment.StatusDescription 55 | deployments = append(deployments, entry) 56 | } 57 | 58 | d.SetId(client.Address() + "/deployments") 59 | 60 | return d.Set("deployments", deployments) 61 | } 62 | -------------------------------------------------------------------------------- /website/docs/r/sentinel_policy.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_sentinel_policy" 4 | sidebar_current: "docs-nomad-resource-sentinel-policy" 5 | description: |- 6 | Manages a Sentinel policy registered on the Nomad server. 7 | --- 8 | 9 | # nomad_sentinel_policy 10 | 11 | Manages a Sentinel policy registered in Nomad. 12 | 13 | ~> **Enterprise Only!** This API endpoint and functionality only exists in 14 | Nomad Enterprise. This is not present in the open source version of Nomad. 15 | 16 | ## Example Usage 17 | 18 | ```hcl 19 | resource "nomad_sentinel_policy" "exec-only" { 20 | name = "exec-only" 21 | description = "Only allow jobs that are based on an exec driver." 22 | 23 | policy = <)` - A unique name for the policy. 48 | - `policy` `(string: )` - The contents of the policy to register. 49 | - `enforcement_level` `(strings: )` - The [enforcement level][enforcement-level] 50 | for this policy. 51 | - `scope` `(strings: )` - The [scope][scope] for this policy. 52 | - `description` `(string: "")` - A description of the policy. 53 | 54 | [scope]: https://www.nomadproject.io/guides/sentinel-policy.html#policy-scope 55 | [enforcement-level]: https://www.nomadproject.io/guides/sentinel-policy.html#enforcement-level 56 | -------------------------------------------------------------------------------- /nomad/data_source_job_parser.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "log" 10 | "strings" 11 | 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 13 | ) 14 | 15 | func dataSourceJobParser() *schema.Resource { 16 | return &schema.Resource{ 17 | Read: dataSourceJobParserRead, 18 | Schema: map[string]*schema.Schema{ 19 | "hcl": { 20 | Description: "Specifies the HCL definition of the job encoded in a JSON string.", 21 | Type: schema.TypeString, 22 | Required: true, 23 | }, 24 | "canonicalize": { 25 | Description: "Flag to enable setting any unset fields to their default values.", 26 | Type: schema.TypeBool, 27 | Optional: true, 28 | Default: false, 29 | }, 30 | "json": { 31 | Description: "The parsed job as JSON string.", 32 | Type: schema.TypeString, 33 | Computed: true, 34 | }, 35 | }, 36 | } 37 | } 38 | 39 | func dataSourceJobParserRead(d *schema.ResourceData, meta interface{}) error { 40 | providerConfig := meta.(ProviderConfig) 41 | client := providerConfig.client 42 | 43 | hcl := d.Get("hcl").(string) 44 | canonicalize := d.Get("canonicalize").(bool) 45 | 46 | log.Printf("[DEBUG] Parsing Job with Canonicalize set to %t", canonicalize) 47 | job, err := client.Jobs().ParseHCL(hcl, canonicalize) 48 | if err != nil { 49 | return fmt.Errorf("error parsing job: %#v", err) 50 | } 51 | 52 | jobJSON, err := json.Marshal(job) 53 | if err != nil { 54 | return fmt.Errorf("error parsing job: %#v", err) 55 | } 56 | 57 | jobJSONString := string(jobJSON) 58 | 59 | d.SetId(*job.ID) 60 | d.Set("hcl", strings.TrimSpace(hcl)) 61 | d.Set("canonicalize", canonicalize) 62 | d.Set("json", strings.TrimSpace(jobJSONString)) 63 | 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /nomad/data_source_acl_roles_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 10 | ) 11 | 12 | func TestDataSourceACLRoles(t *testing.T) { 13 | resourceName := "data.nomad_acl_roles.test" 14 | 15 | resource.Test(t, resource.TestCase{ 16 | Providers: testProviders, 17 | PreCheck: func() { testAccPreCheck(t); testCheckMinVersion(t, "1.4.0-beta.1") }, 18 | Steps: []resource.TestStep{ 19 | { 20 | Config: testDataSourceACLRolesConfig, 21 | Check: resource.ComposeTestCheckFunc( 22 | resource.TestCheckResourceAttrSet(resourceName, "prefix"), 23 | resource.TestCheckResourceAttr(resourceName, "acl_roles.#", "1"), 24 | resource.TestCheckResourceAttrSet(resourceName, "acl_roles.0.id"), 25 | resource.TestCheckResourceAttr(resourceName, "acl_roles.0.name", "acctest-acl-role"), 26 | resource.TestCheckResourceAttr(resourceName, "acl_roles.0.description", "A Terraform acctest ACL Role"), 27 | resource.TestCheckResourceAttr(resourceName, "acl_roles.0.policies.#", "1"), 28 | ), 29 | }, 30 | }, 31 | }) 32 | } 33 | 34 | const testDataSourceACLRolesConfig = ` 35 | resource "nomad_acl_policy" "test" { 36 | name = "acctest-acl-policy" 37 | description = "A Terraform acctest ACL Policy" 38 | rules_hcl = < This option differs from Nomad, where it's represented as a boolean, to 48 | allow distinguishing between memory oversubscription being disabled in the 49 | node pool and this property not being set. 50 | 51 | [nomad_api_filter]: https://developer.hashicorp.com/nomad/api-docs/v1.6.x#filtering 52 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | TEST?=$$(go list ./...) 2 | GOFMT_FILES?=$$(find . -name '*.go') 3 | WEBSITE_REPO=github.com/hashicorp/terraform-website 4 | PKG_NAME=nomad 5 | 6 | default: build 7 | 8 | build: fmtcheck 9 | go install 10 | 11 | test: fmtcheck 12 | go test -i $(TEST) || exit 1 13 | echo $(TEST) | \ 14 | xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 -count=1 15 | 16 | testacc: fmtcheck 17 | TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m -count=1 18 | 19 | localtestacc-start-nomad: 20 | scripts/start-nomad.sh 21 | 22 | localtestacc: fmtcheck localtestacc-start-nomad 23 | -env NOMAD_TOKEN=00000000-0000-0000-0000-000000000000 \ 24 | TF_ACC=1 \ 25 | go test $(TEST) -v $(TESTARGS) -timeout 120m -count=1 26 | scripts/stop-nomad.sh 27 | 28 | vet: 29 | @echo "go vet ." 30 | @go vet $$(go list ./...) ; if [ $$? -eq 1 ]; then \ 31 | echo ""; \ 32 | echo "Vet found suspicious constructs. Please check the reported constructs"; \ 33 | echo "and fix them if necessary before submitting the code for review."; \ 34 | exit 1; \ 35 | fi 36 | 37 | fmt: 38 | gofmt -w $(GOFMT_FILES) 39 | 40 | fmtcheck: 41 | @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" 42 | 43 | errcheck: 44 | @sh -c "'$(CURDIR)/scripts/errcheck.sh'" 45 | 46 | 47 | test-compile: 48 | @if [ "$(TEST)" = "./..." ]; then \ 49 | echo "ERROR: Set TEST to a specific package. For example,"; \ 50 | echo " make test-compile TEST=./$(PKG_NAME)"; \ 51 | exit 1; \ 52 | fi 53 | go test -c $(TEST) $(TESTARGS) 54 | 55 | website: 56 | ifeq (,$(wildcard $(GOPATH)/src/$(WEBSITE_REPO))) 57 | echo "$(WEBSITE_REPO) not found in your GOPATH (necessary for layouts and assets), get-ting..." 58 | git clone https://$(WEBSITE_REPO) $(GOPATH)/src/$(WEBSITE_REPO) 59 | endif 60 | @$(MAKE) -C $(GOPATH)/src/$(WEBSITE_REPO) website-provider PROVIDER_PATH=$(shell pwd) PROVIDER_NAME=$(PKG_NAME) 61 | 62 | .PHONY: build test testacc vet fmt fmtcheck errcheck test-compile website 63 | -------------------------------------------------------------------------------- /website/docs/d/plugin.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_plugin" 4 | sidebar_current: "docs-nomad-datasource-plugin" 5 | description: |- 6 | Get information on a specific CSI plugin. 7 | --- 8 | 9 | # nomad_plugin 10 | 11 | Lookup a plugin by ID. The aim of this datasource is to determine whether 12 | a particular plugin exists on the cluster, to find information on the health 13 | and availability of the plugin, and to optionally wait for the plugin 14 | before performing actions the require an available plugin controller. 15 | 16 | If a plugin with the specified ID does not exist and the datasource is not 17 | configured to wait, it will result in an error. For simple existence checks, 18 | use the `nomad_plugins` listing datasource. 19 | 20 | ## Example Usage 21 | 22 | Check for the existence of a plugin: 23 | 24 | ```hcl 25 | data "nomad_plugin" "ebs" { 26 | plugin_id = "aws-ebs0" 27 | wait_for_healthy = true 28 | } 29 | ``` 30 | 31 | This will check for a plugin with the ID `aws-ebs0`, waiting until the plugin 32 | is healthy before returning. 33 | 34 | ## Argument Reference 35 | 36 | The following arguments are supported: 37 | 38 | * `plugin_id`: `(string)` ID of the plugin. 39 | * `wait_for_registration`: `(boolean)` if the plugin doesn't exist, retry until it does 40 | * `wait_for_healthy`: `(boolean)` retry until the plugin exists and all controllers are healthy 41 | 42 | ## Attributes Reference 43 | 44 | The following attributes are exported: 45 | 46 | * `plugin_id`: `(string)` ID of the plugin 47 | * `plugin_provider`: `(string)` Plugin provider name 48 | * `plugin_provider_version`: `(string)` Plugin provider version 49 | * `controller_required`: `(boolean)` Whether a controller is required. 50 | * `controllers_expected`: `(integer)` The number of registered controllers. 51 | * `controllers_healthy`: `(integer)` The number of healthy controllers. 52 | * `nodes_expected`: `(integer)` The number of registered nodes. 53 | * `nodes_healthy`: `(integer)` The number of healthy nodes. 54 | -------------------------------------------------------------------------------- /nomad/data_source_acl_role_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 12 | ) 13 | 14 | func TestDataSourceACLRole(t *testing.T) { 15 | resourceName := "data.nomad_acl_role.test" 16 | 17 | resource.Test(t, resource.TestCase{ 18 | Providers: testProviders, 19 | PreCheck: func() { testAccPreCheck(t); testCheckMinVersion(t, "1.4.0-beta.1") }, 20 | Steps: []resource.TestStep{ 21 | { 22 | Config: testDataSourceACLRoleConfig, 23 | Check: resource.ComposeTestCheckFunc( 24 | testDataSourceACLRoleExists(resourceName), 25 | resource.TestCheckResourceAttr(resourceName, "name", "acctest-acl-role"), 26 | resource.TestCheckResourceAttr(resourceName, "description", "A Terraform acctest ACL Role"), 27 | resource.TestCheckResourceAttr(resourceName, "policies.#", "1"), 28 | ), 29 | }, 30 | }, 31 | }) 32 | } 33 | 34 | func testDataSourceACLRoleExists(name string) resource.TestCheckFunc { 35 | return func(s *terraform.State) error { 36 | _, ok := s.RootModule().Resources[name] 37 | if !ok { 38 | return fmt.Errorf("root module has no resource called %s", name) 39 | } 40 | return nil 41 | } 42 | } 43 | 44 | const testDataSourceACLRoleConfig = ` 45 | resource "nomad_acl_policy" "test" { 46 | name = "acctest-acl-policy" 47 | description = "A Terraform acctest ACL Policy" 48 | rules_hcl = <)` - A unique name for the policy. 45 | - `rules_hcl` `(string: )` - The contents of the policy to register, 46 | as HCL or JSON. 47 | - `description` `(string: "")` - A description of the policy. 48 | - `job_acl`: `(`[`JobACL`](#jobacl-1)`: )` - Options for assigning the 49 | ACL rules to a job, group, or task. 50 | 51 | ### JobACL 52 | 53 | The `job_acl` block is used to associate the ACL policy with a given job, group, 54 | or task. Refer to [Workload Associated ACL Policies][nomad_docs_wi] for more 55 | information. The following arguments are supported. 56 | 57 | - `namespace` `(string: "default")` - Attach the policy to the job in this namespace. 58 | - `job_id` `(string)` - Attach the policy to this job. Required. 59 | - `group` `(string: ` - Attach the policy to this group in the 60 | job. Required if `task` is set. 61 | - `task` `(string: ` - Attach the policy to this task in the job. 62 | 63 | [nomad_docs_wi]: https://www.nomadproject.io/docs/concepts/workload-identity#workload-associated-acl-policies 64 | -------------------------------------------------------------------------------- /website/docs/r/quota_specification.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_quota_specification" 4 | sidebar_current: "docs-nomad-resource-quota-specification" 5 | description: |- 6 | Manages a quota specification in a Nomad cluster. 7 | --- 8 | 9 | # nomad_quota_specification 10 | 11 | Manages a quota specification in a Nomad cluster. 12 | 13 | ## Example Usage 14 | 15 | Registering a quota specification: 16 | 17 | ```hcl 18 | resource "nomad_quota_specification" "prod_api" { 19 | name = "prod-api" 20 | description = "Production instances of backend API servers" 21 | 22 | limits { 23 | region = "global" 24 | 25 | region_limit { 26 | cpu = 2400 27 | memory_mb = 1200 28 | } 29 | } 30 | } 31 | ``` 32 | 33 | ## Argument Reference 34 | 35 | The following arguments are supported: 36 | 37 | - `name` `(string: )` - A unique name for the quota specification. 38 | - `description` `(string: "")` - A description of the quota specification. 39 | - `limits` `(block: )` - A block of quota limits to enforce. Can 40 | be repeated. See below for the structure of this block. 41 | 42 | 43 | ### `limits` blocks 44 | 45 | The `limits` block describes the quota limits to be enforced. It supports 46 | the following arguments: 47 | 48 | - `region` `(string: )` - The region these limits should apply to. 49 | - `region_limit` `(block: )` - The limits to enforce. This block 50 | may only be specified once in the `limits` block. Its structure is 51 | documented below. 52 | 53 | ### `region_limit` blocks 54 | 55 | The `region_limit` block describes the quota limits to be enforced on a region. 56 | It supports the following arguments: 57 | 58 | - `cpu` `(int: 0)` - The amount of CPU to limit allocations to. A value of zero 59 | is treated as unlimited, and a negative value is treated as fully disallowed. 60 | - `memory_mb` `(int: 0)` - The amount of memory (in megabytes) to limit 61 | allocations to. A value of zero is treated as unlimited, and a negative value 62 | is treated as fully disallowed. 63 | -------------------------------------------------------------------------------- /nomad/data_source_plugins.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strconv" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" 13 | ) 14 | 15 | func dataSourcePlugins() *schema.Resource { 16 | return &schema.Resource{ 17 | Read: pluginsDataSourceRead, 18 | 19 | Schema: map[string]*schema.Schema{ 20 | "type": { 21 | Type: schema.TypeString, 22 | Optional: true, 23 | Description: "Volume Type (currently only 'csi')", 24 | Default: "csi", 25 | Elem: &schema.Schema{ 26 | Type: schema.TypeString, 27 | ValidateFunc: validation.StringInSlice([]string{"csi"}, false), 28 | }, 29 | }, 30 | 31 | "plugins": { 32 | Description: "Registered plugins", 33 | Computed: true, 34 | Type: schema.TypeList, 35 | Elem: &schema.Schema{Type: schema.TypeMap}, 36 | }, 37 | }, 38 | } 39 | } 40 | 41 | func pluginsDataSourceRead(d *schema.ResourceData, meta interface{}) error { 42 | client := meta.(ProviderConfig).client 43 | 44 | log.Printf("[DEBUG] Reading list of dynamic plugins from Nomad") 45 | resp, _, err := client.CSIPlugins().List(nil) 46 | if err != nil { 47 | return fmt.Errorf("error reading plugins from Nomad: %s", err) 48 | } 49 | plugins := make([]map[string]interface{}, 0, len(resp)) 50 | for _, p := range resp { 51 | plugin := map[string]interface{}{ 52 | "id": p.ID, 53 | "provider": p.Provider, 54 | "controller_required": strconv.FormatBool(p.ControllerRequired), 55 | "controllers_healthy": strconv.Itoa(p.ControllersHealthy), 56 | "controllers_expected": strconv.Itoa(p.ControllersExpected), 57 | "nodes_healthy": strconv.Itoa(p.NodesHealthy), 58 | "nodes_expected": strconv.Itoa(p.NodesExpected), 59 | } 60 | plugins = append(plugins, plugin) 61 | } 62 | log.Printf("[DEBUG] Finished reading plugins from Nomad") 63 | d.SetId(client.Address() + "/v1/plugins") 64 | 65 | return d.Set("plugins", plugins) 66 | } 67 | -------------------------------------------------------------------------------- /nomad/data_source_scaling_policy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 10 | ) 11 | 12 | func TestDataSourceScalingPolicy_Basic(t *testing.T) { 13 | dataSourceName := "data.nomad_scaling_policy.policy" 14 | 15 | resource.Test(t, resource.TestCase{ 16 | Providers: testProviders, 17 | PreCheck: func() { testAccPreCheck(t); testCheckMinVersion(t, "0.11.0") }, 18 | Steps: []resource.TestStep{ 19 | { 20 | Config: testDataSourceScalingPolicyConfig, 21 | Check: resource.ComposeTestCheckFunc( 22 | resource.TestCheckResourceAttrSet(dataSourceName, "id"), 23 | resource.TestCheckResourceAttr(dataSourceName, "enabled", "false"), 24 | resource.TestCheckResourceAttr(dataSourceName, "type", "horizontal"), 25 | resource.TestCheckResourceAttr(dataSourceName, "min", "1"), 26 | resource.TestCheckResourceAttr(dataSourceName, "max", "20"), 27 | resource.TestCheckResourceAttr(dataSourceName, "policy", `{"cooldown":"20s"}`), 28 | resource.TestCheckResourceAttr(dataSourceName, "target.Namespace", "default"), 29 | resource.TestCheckResourceAttr(dataSourceName, "target.Job", "foo-scaling-policy"), 30 | resource.TestCheckResourceAttr(dataSourceName, "target.Group", "foo"), 31 | ), 32 | }, 33 | }, 34 | }) 35 | } 36 | 37 | const testDataSourceScalingPolicyConfig = ` 38 | resource "nomad_job" "job" { 39 | purge_on_destroy = true 40 | 41 | jobspec = < **Warning:** destroying this resource will not have any effect in the 14 | cluster configuration, since there's no clear definition of what a destroy 15 | action should do. The cluster will be left as-is and only the state reference 16 | will be removed. 17 | 18 | ## Example Usage 19 | 20 | Set cluster scheduler configuration: 21 | 22 | ```hcl 23 | resource "nomad_scheduler_config" "config" { 24 | scheduler_algorithm = "spread" 25 | memory_oversubscription_enabled = true 26 | preemption_config = { 27 | system_scheduler_enabled = true 28 | batch_scheduler_enabled = true 29 | service_scheduler_enabled = true 30 | sysbatch_scheduler_enabled = true 31 | } 32 | } 33 | ``` 34 | 35 | ## Argument Reference 36 | 37 | The following arguments are supported: 38 | 39 | - `memory_oversubscription_enabled` `(bool: false)` - When `true`, tasks may exceed their reserved memory limit. 40 | - `scheduler_algorithm` `(string: "binpack")` - Specifies whether scheduler binpacks or spreads allocations on available nodes. Possible values are `binpack` and `spread`. 41 | - `preemption_config` `(map[string]bool)` - Options to enable preemption for various schedulers. 42 | - `system_scheduler_enabled` `(bool: true)` - Specifies whether preemption for system jobs is enabled. Note that if this is set to true, then system jobs can preempt any other jobs. 43 | - `batch_scheduler_enabled` `(bool: false")` - Specifies whether preemption for batch jobs is enabled. Note that if this is set to true, then batch jobs can preempt any other jobs. 44 | - `service_scheduler_enabled` `(bool: false)` - Specifies whether preemption for service jobs is enabled. Note that if this is set to true, then service jobs can preempt any other jobs. 45 | - `sysbatch_scheduler_enabled` `(bool: false)` - Specifies whether preemption for sysbatch (system batch) jobs is enabled. Note that if this is set to true, then system batch jobs can preempt any other jobs. 46 | -------------------------------------------------------------------------------- /nomad/data_source_scheduler_config.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | "github.com/hashicorp/terraform-provider-nomad/nomad/helper" 12 | ) 13 | 14 | func dataSourceSchedulerConfig() *schema.Resource { 15 | return &schema.Resource{ 16 | Read: dataSourceSchedulerConfigRead, 17 | 18 | Schema: map[string]*schema.Schema{ 19 | "memory_oversubscription_enabled": { 20 | Description: "When true, tasks may exceed their reserved memory limit.", 21 | Type: schema.TypeBool, 22 | Computed: true, 23 | }, 24 | "scheduler_algorithm": { 25 | Description: "Specifies whether scheduler binpacks or spreads allocations on available nodes.", 26 | Type: schema.TypeString, 27 | Computed: true, 28 | }, 29 | "preemption_config": { 30 | Description: "Options to enable preemption for various schedulers.", 31 | Computed: true, 32 | Type: schema.TypeMap, 33 | Elem: &schema.Schema{Type: schema.TypeBool}, 34 | }, 35 | }, 36 | } 37 | } 38 | 39 | func dataSourceSchedulerConfigRead(d *schema.ResourceData, meta interface{}) error { 40 | 41 | client := meta.(ProviderConfig).client 42 | 43 | schedCfg, _, err := client.Operator().SchedulerGetConfiguration(nil) 44 | if err != nil { 45 | return fmt.Errorf("failed to query scheduler config: %v", err) 46 | } 47 | 48 | // Set a unique ID, as we have nothing else to go on. 49 | d.SetId(resource.UniqueId()) 50 | 51 | premptMap := map[string]bool{ 52 | "batch_scheduler_enabled": schedCfg.SchedulerConfig.PreemptionConfig.BatchSchedulerEnabled, 53 | "service_scheduler_enabled": schedCfg.SchedulerConfig.PreemptionConfig.ServiceSchedulerEnabled, 54 | "sysbatch_scheduler_enabled": schedCfg.SchedulerConfig.PreemptionConfig.SysBatchSchedulerEnabled, 55 | "system_scheduler_enabled": schedCfg.SchedulerConfig.PreemptionConfig.SystemSchedulerEnabled, 56 | } 57 | 58 | sw := helper.NewStateWriter(d) 59 | sw.Set("memory_oversubscription_enabled", schedCfg.SchedulerConfig.MemoryOversubscriptionEnabled) 60 | sw.Set("scheduler_algorithm", schedCfg.SchedulerConfig.SchedulerAlgorithm) 61 | sw.Set("preemption_config", premptMap) 62 | return sw.Error() 63 | } 64 | -------------------------------------------------------------------------------- /.release/ci.hcl: -------------------------------------------------------------------------------- 1 | // Reference: https://github.com/hashicorp/crt-core-helloworld/blob/main/.release/ci.hcl (private repository) 2 | // 3 | // One way to validate this file, with a local build of the orchestrator (an internal repo): 4 | // 5 | // $ GITHUB_TOKEN="not-used" orchestrator parse config -use-v2 -local-config=.release/ci.hcl 6 | 7 | schema = "2" 8 | 9 | project "terraform-provider-nomad" { 10 | // team is currently unused and has no meaning 11 | // but is required to be non-empty by CRT orchestator 12 | team = "_UNUSED_" 13 | 14 | slack { 15 | notification_channel = "C09LCJBBNE5" 16 | } 17 | 18 | github { 19 | organization = "hashicorp" 20 | repository = "terraform-provider-nomad" 21 | release_branches = ["main", "release/**"] 22 | } 23 | } 24 | 25 | event "merge" { 26 | } 27 | 28 | event "build" { 29 | action "build" { 30 | depends = ["merge"] 31 | 32 | organization = "hashicorp" 33 | repository = "terraform-provider-nomad" 34 | workflow = "build" 35 | } 36 | } 37 | 38 | event "prepare" { 39 | # `prepare` is the Common Release Tooling (CRT) artifact processing workflow. 40 | # It prepares artifacts for potential promotion to staging and production. 41 | # For example, it scans and signs artifacts. 42 | 43 | depends = ["build"] 44 | 45 | action "prepare" { 46 | organization = "hashicorp" 47 | repository = "crt-workflows-common" 48 | workflow = "prepare" 49 | depends = ["build"] 50 | } 51 | 52 | notification { 53 | on = "fail" 54 | } 55 | } 56 | 57 | event "trigger-staging" { 58 | } 59 | 60 | event "promote-staging" { 61 | action "promote-staging" { 62 | organization = "hashicorp" 63 | repository = "crt-workflows-common" 64 | workflow = "promote-staging" 65 | depends = null 66 | config = "release-metadata.hcl" 67 | } 68 | 69 | depends = ["trigger-staging"] 70 | 71 | notification { 72 | on = "always" 73 | } 74 | } 75 | 76 | event "trigger-production" { 77 | } 78 | 79 | event "promote-production" { 80 | action "promote-production" { 81 | organization = "hashicorp" 82 | repository = "crt-workflows-common" 83 | workflow = "promote-production" 84 | depends = null 85 | config = "" 86 | } 87 | 88 | depends = ["trigger-production"] 89 | 90 | notification { 91 | on = "always" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /nomad/data_source_acl_role.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strings" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | ) 13 | 14 | func dataSourceACLRole() *schema.Resource { 15 | return &schema.Resource{ 16 | Read: dataSourceACLRoleRead, 17 | Schema: map[string]*schema.Schema{ 18 | "id": { 19 | Description: "The ACL Role unique identifier.", 20 | Required: true, 21 | Type: schema.TypeString, 22 | }, 23 | "name": { 24 | Description: "Unique name of the ACL role.", 25 | Computed: true, 26 | Type: schema.TypeString, 27 | }, 28 | "description": { 29 | Description: "Description for the ACL role.", 30 | Computed: true, 31 | Type: schema.TypeString, 32 | }, 33 | "policies": { 34 | Description: "The list of policies applied to the role.", 35 | Computed: true, 36 | Type: schema.TypeSet, 37 | Elem: &schema.Resource{ 38 | Schema: map[string]*schema.Schema{ 39 | "name": { 40 | Type: schema.TypeString, 41 | Required: true, 42 | Description: "The name of the ACL policy to link.", 43 | }, 44 | }, 45 | }, 46 | }, 47 | }, 48 | } 49 | } 50 | 51 | func dataSourceACLRoleRead(d *schema.ResourceData, meta interface{}) error { 52 | providerConfig := meta.(ProviderConfig) 53 | client := providerConfig.client 54 | 55 | roleID := d.Get("id").(string) 56 | 57 | log.Printf("[DEBUG] Reading ACL Role %q", roleID) 58 | aclRole, _, err := client.ACLRoles().Get(roleID, nil) 59 | if err != nil { 60 | 61 | // As of Nomad 0.4.1, the API client returns an error for 404 62 | // rather than a nil result, so we must check this way. 63 | if strings.Contains(err.Error(), "404") { 64 | d.SetId("") 65 | return nil 66 | } 67 | return fmt.Errorf("error reading ACL Role %q: %s", roleID, err.Error()) 68 | } 69 | log.Printf("[DEBUG] Read ACL Role %q", roleID) 70 | 71 | policies := make([]map[string]interface{}, len(aclRole.Policies)) 72 | for i, policyLink := range aclRole.Policies { 73 | policies[i] = map[string]interface{}{"name": policyLink.Name} 74 | } 75 | 76 | d.SetId(aclRole.ID) 77 | d.Set("name", aclRole.Name) 78 | d.Set("description", aclRole.Description) 79 | d.Set("policies", policies) 80 | 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /nomad/data_source_datacenters.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "sort" 9 | "strings" 10 | 11 | "github.com/hashicorp/nomad/api" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 14 | ) 15 | 16 | func dataSourceDatacenters() *schema.Resource { 17 | return &schema.Resource{ 18 | Read: dataSourceDatacentersRead, 19 | 20 | Schema: map[string]*schema.Schema{ 21 | "prefix": { 22 | Description: "Prefix value used for filtering results.", 23 | Type: schema.TypeString, 24 | Optional: true, 25 | }, 26 | "ignore_down_nodes": { 27 | Description: "If enabled, this flag will ignore nodes that are down when listing datacenters.", 28 | Type: schema.TypeBool, 29 | Optional: true, 30 | Default: false, 31 | }, 32 | "datacenters": { 33 | Description: "The list of datacenters.", 34 | Computed: true, 35 | Type: schema.TypeList, 36 | Elem: &schema.Schema{ 37 | Type: schema.TypeString, 38 | }, 39 | }, 40 | }, 41 | } 42 | } 43 | 44 | func dataSourceDatacentersRead(d *schema.ResourceData, meta interface{}) error { 45 | client := meta.(ProviderConfig).client 46 | nodes, _, err := client.Nodes().List(nil) 47 | if err != nil { 48 | return fmt.Errorf("failed to query list of nodes: %v", err) 49 | } 50 | 51 | prefix := d.Get("prefix").(string) 52 | ignoreDown := d.Get("ignore_down_nodes").(bool) 53 | 54 | d.SetId(resource.UniqueId()) 55 | if err := d.Set("datacenters", filterDatacenters(nodes, prefix, ignoreDown)); err != nil { 56 | return fmt.Errorf("error setting datacenters: %v", err) 57 | } 58 | 59 | return nil 60 | } 61 | 62 | func filterDatacenters(nodes []*api.NodeListStub, prefix string, ignoreDown bool) []string { 63 | datacentersSet := make(map[string]struct{}) 64 | datacenters := []string{} 65 | 66 | for _, n := range nodes { 67 | ignore := n.Status == "down" && ignoreDown 68 | if ignore || !strings.HasPrefix(n.Datacenter, prefix) { 69 | continue 70 | } 71 | if _, ok := datacentersSet[n.Datacenter]; ok { 72 | continue 73 | } 74 | 75 | datacentersSet[n.Datacenter] = struct{}{} 76 | datacenters = append(datacenters, n.Datacenter) 77 | } 78 | 79 | // Sort output to keep it stable. 80 | sort.Strings(datacenters) 81 | 82 | return datacenters 83 | } 84 | -------------------------------------------------------------------------------- /nomad/data_source_acl_policy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/hashicorp/nomad/api" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 14 | ) 15 | 16 | func TestAccDataSourceNomadAclPolicy_Basic(t *testing.T) { 17 | policyName := acctest.RandomWithPrefix("test-policy") 18 | resourceName := "data.nomad_acl_policy.test" 19 | resource.Test(t, resource.TestCase{ 20 | PreCheck: func() { testAccPreCheck(t) }, 21 | Providers: testProviders, 22 | CheckDestroy: testResourceACLPolicy_checkDestroy(policyName), 23 | Steps: []resource.TestStep{ 24 | { 25 | Config: testAccNomadAclPolicyConfig(policyName), 26 | Check: resource.ComposeTestCheckFunc( 27 | testAccDataSourceNomadAclPolicyExists(resourceName), 28 | resource.TestCheckResourceAttr( 29 | resourceName, "name", policyName), 30 | resource.TestCheckResourceAttr( 31 | resourceName, "description", "Test ACL Policy"), 32 | resource.TestCheckResourceAttrSet(resourceName, "rules"), 33 | ), 34 | }, 35 | }, 36 | }) 37 | } 38 | 39 | func testAccNomadAclPolicyConfig(name string) string { 40 | return ` 41 | resource "nomad_acl_policy" "test" { 42 | name = "` + name + `" 43 | description = "Test ACL Policy" 44 | rules_hcl = < **Warning:** this resource will store the sensitive value placed in 14 | `config.oidc_client_secret` in the Terraform's state file. Take care to 15 | [protect your state file](/docs/state/sensitive-data.html). 16 | 17 | ## Example Usage 18 | 19 | Creating an ALC Binding Rule associated to an ACL Auth Method also created and 20 | managed by Terraform: 21 | 22 | ```hcl 23 | resource "nomad_acl_auth_method" "my_nomad_acl_auth_method" { 24 | name = "my-nomad-acl-auth-method" 25 | type = "OIDC" 26 | token_locality = "global" 27 | max_token_ttl = "10m0s" 28 | default = true 29 | 30 | config { 31 | oidc_discovery_url = "https://uk.auth0.com/" 32 | oidc_client_id = "someclientid" 33 | oidc_client_secret = "someclientsecret-t" 34 | bound_audiences = ["someclientid"] 35 | allowed_redirect_uris = [ 36 | "http://localhost:4649/oidc/callback", 37 | "http://localhost:4646/ui/settings/tokens", 38 | ] 39 | list_claim_mappings = { 40 | "http://nomad.internal/roles" : "roles" 41 | } 42 | } 43 | } 44 | 45 | resource "nomad_acl_binding_rule" "my_nomad_acl_binding_rule" { 46 | description = "engineering rule" 47 | auth_method = nomad_acl_auth_method.my_nomad_acl_auth_method.name 48 | selector = "engineering in list.roles" 49 | bind_type = "role" 50 | bind_name = "engineering-read-only" 51 | } 52 | ``` 53 | 54 | ## Argument Reference 55 | 56 | The following arguments are supported: 57 | 58 | - `description` `(string: "")` - Description for this ACL binding rule. 59 | 60 | - `auth_method` `(string: )` - Name of the auth method for which this 61 | rule applies to. 62 | 63 | - `selector` `(string: "")` - A boolean expression that matches against verified 64 | identity attributes returned from the auth method during login. 65 | 66 | - `bind_type` `(string: )` - Adjusts how this binding rule is applied 67 | at login time. Valid values are `role`, `policy`, and `management`. 68 | 69 | - `bind_name` `(string: )` - Target of the binding. If `bind_type` is 70 | `role` or `policy` then `bind_name` is required. If `bind_type` is 71 | `management` than `bind_name` must not be defined. 72 | -------------------------------------------------------------------------------- /nomad/data_source_scaling_policy.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "strings" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | "github.com/hashicorp/terraform-provider-nomad/nomad/helper" 13 | ) 14 | 15 | func dataSourceScalingPolicy() *schema.Resource { 16 | return &schema.Resource{ 17 | Read: scalingPolicyDataSourceRead, 18 | 19 | Schema: map[string]*schema.Schema{ 20 | "id": { 21 | Description: "The scaling policy ID.", 22 | Type: schema.TypeString, 23 | Required: true, 24 | }, 25 | "enabled": { 26 | Description: "Whether or not the scaling policy is enabled.", 27 | Type: schema.TypeBool, 28 | Computed: true, 29 | }, 30 | "type": { 31 | Description: "The scaling policy type.", 32 | Type: schema.TypeString, 33 | Computed: true, 34 | }, 35 | "min": { 36 | Description: "The minimum value defined in the scaling policy.", 37 | Type: schema.TypeInt, 38 | Computed: true, 39 | }, 40 | "max": { 41 | Description: "The maximum value defined in the scaling policy.", 42 | Type: schema.TypeInt, 43 | Computed: true, 44 | }, 45 | "policy": { 46 | Description: "The policy defined in the scaling policy as a JSON string.", 47 | Type: schema.TypeString, 48 | Computed: true, 49 | }, 50 | "target": { 51 | Description: "The scaling policy target.", 52 | Type: schema.TypeMap, 53 | Computed: true, 54 | }, 55 | }, 56 | } 57 | } 58 | 59 | func scalingPolicyDataSourceRead(d *schema.ResourceData, meta interface{}) error { 60 | client := meta.(ProviderConfig).client 61 | 62 | id := d.Get("id").(string) 63 | p, _, err := client.Scaling().GetPolicy(id, nil) 64 | if err != nil { 65 | if strings.Contains(err.Error(), "404") { 66 | d.SetId("") 67 | return nil 68 | } 69 | return fmt.Errorf("failed to get scaling policy %q: %v", id, err) 70 | } 71 | 72 | d.SetId(p.ID) 73 | 74 | sw := helper.NewStateWriter(d) 75 | sw.Set("enabled", p.Enabled) 76 | sw.Set("type", p.Type) 77 | sw.Set("min", p.Min) 78 | sw.Set("max", p.Max) 79 | sw.Set("target", p.Target) 80 | 81 | policyJSON, err := json.Marshal(p.Policy) 82 | if err != nil { 83 | return fmt.Errorf("failed to parse scaling policy %q: %v", id, err) 84 | } 85 | sw.Set("policy", string(policyJSON)) 86 | 87 | if sw.Error() != nil { 88 | return sw.Error() 89 | } 90 | 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /nomad/data_source_acl_policies.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | 10 | "github.com/hashicorp/nomad/api" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 13 | ) 14 | 15 | func dataSourceAclPolicies() *schema.Resource { 16 | return &schema.Resource{ 17 | Read: dataSourceAclPoliciesRead, 18 | 19 | Schema: map[string]*schema.Schema{ 20 | "prefix": { 21 | Description: "ACL Policy Name Prefix", 22 | Type: schema.TypeString, 23 | Optional: true, 24 | }, 25 | "policies": { 26 | Description: "ACL Policies", 27 | Type: schema.TypeList, 28 | Computed: true, 29 | Elem: &schema.Resource{ 30 | Schema: map[string]*schema.Schema{ 31 | "name": { 32 | Description: "ACL Policy Name", 33 | Type: schema.TypeString, 34 | Computed: true, 35 | }, 36 | "description": { 37 | Description: "ACL Policy Description", 38 | Type: schema.TypeString, 39 | Computed: true, 40 | }, 41 | }, 42 | }, 43 | }, 44 | }, 45 | } 46 | } 47 | 48 | func dataSourceAclPoliciesRead(d *schema.ResourceData, meta interface{}) error { 49 | providerConfig := meta.(ProviderConfig) 50 | client := providerConfig.client 51 | queryOpts := &api.QueryOptions{} 52 | if v, ok := d.GetOk("prefix"); ok && v.(string) != "" { 53 | queryOpts.Prefix = v.(string) 54 | } 55 | 56 | debugMsg := "[DEBUG] Getting ACL Policies" 57 | if queryOpts.Prefix != "" { 58 | debugMsg += fmt.Sprintf(" for prefix: %s", queryOpts.Prefix) 59 | } 60 | log.Print(debugMsg) 61 | 62 | policies, _, err := client.ACLPolicies().List(queryOpts) 63 | if err != nil { 64 | // As of Nomad 0.4.1, the API client returns an error for 404 65 | // rather than a nil result, so we must check this way. 66 | return fmt.Errorf("error getting ACL policies: %#v", err) 67 | } 68 | 69 | d.SetId(resource.UniqueId()) 70 | if err := d.Set("policies", flattenAclPolicies(policies)); err != nil { 71 | return fmt.Errorf("error setting policies: %#v", err) 72 | } 73 | 74 | return nil 75 | } 76 | 77 | func flattenAclPolicies(policies []*api.ACLPolicyListStub) []interface{} { 78 | output := make([]interface{}, 0, len(policies)) 79 | for _, policy := range policies { 80 | p := map[string]interface{}{ 81 | "name": policy.Name, 82 | "description": policy.Description, 83 | } 84 | output = append(output, p) 85 | } 86 | return output 87 | } 88 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Hi there, 2 | 3 | Thank you for opening an issue. Please note that we try to keep the Terraform issue tracker reserved for bug reports and feature requests. For general usage questions, please see: https://www.terraform.io/community.html. 4 | 5 | ### Terraform Version 6 | Run `terraform -v` to show the version. If you are not running the latest version of Terraform, please upgrade because your issue may have already been fixed. 7 | 8 | This command will also output the provider version, please include that as well. 9 | 10 | ### Nomad Version 11 | Run `nomad server members` in your target node to view which version of Nomad is running. Make sure to include the entire version output. 12 | 13 | ### Provider Configuration 14 | Which values are you setting in the provider configuration? 15 | 16 | ```hcl 17 | provider "nomad" { 18 | ... 19 | } 20 | ``` 21 | 22 | ### Environment Variables 23 | Do you have any Nomad specific environment variable set in the machine running Terraform? 24 | 25 | ```sh 26 | env | grep "NOMAD_" 27 | ``` 28 | 29 | ### Affected Resource(s) 30 | Please list the resources as a list, for example: 31 | - nomad_job 32 | - nomad_acl_policy 33 | 34 | If this issue appears to affect multiple resources, it may be an issue with Terraform's core, so please mention this. 35 | 36 | ### Terraform Configuration Files 37 | ```hcl 38 | # Copy-paste your Terraform configurations here - for large Terraform configs, 39 | # please use a service like Dropbox and share a link to the ZIP file. For 40 | # security, you can also encrypt the files using our GPG public key. 41 | ``` 42 | 43 | ### Debug Output 44 | Please provider a link to a GitHub Gist containing the complete debug output: https://www.terraform.io/docs/internals/debugging.html. Please do NOT paste the debug output in the issue; just paste a link to the Gist. 45 | 46 | ### Panic Output 47 | If Terraform produced a panic, please provide a link to a GitHub Gist containing the output of the `crash.log`. 48 | 49 | ### Expected Behavior 50 | What should have happened? 51 | 52 | ### Actual Behavior 53 | What actually happened? 54 | 55 | ### Steps to Reproduce 56 | Please list the steps required to reproduce the issue, for example: 57 | 1. `terraform apply` 58 | 59 | ### Important Factoids 60 | Are there anything atypical about your accounts that we should know? For example: Do you have [ACL](https://www.nomadproject.io/guides/security/acl.html) enabled? Multi-region deployment? 61 | 62 | ### References 63 | Are there any other GitHub issues (open or closed) or Pull Requests that should be linked here? For example: 64 | - GH-1234 65 | 66 | -------------------------------------------------------------------------------- /nomad/data_source_node_pools_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 12 | ) 13 | 14 | func TestDataSourceNodePools_basic(t *testing.T) { 15 | name := acctest.RandomWithPrefix("tf-nomad-test") 16 | resource.Test(t, resource.TestCase{ 17 | Providers: testProviders, 18 | PreCheck: func() { testAccPreCheck(t); testCheckMinVersion(t, "1.6.0-beta.1") }, 19 | Steps: []resource.TestStep{ 20 | { 21 | Config: testDataSourceNodePools_basic(name), 22 | Check: resource.ComposeTestCheckFunc( 23 | resource.TestCheckResourceAttr("data.nomad_node_pools.all_pools", "node_pools.#", "5"), 24 | resource.TestCheckResourceAttr("data.nomad_node_pools.prefix", "node_pools.#", "2"), 25 | resource.TestCheckResourceAttr("data.nomad_node_pools.filter", "node_pools.#", "2"), 26 | resource.TestCheckResourceAttr("data.nomad_node_pools.filter_with_prefix", "node_pools.#", "1"), 27 | ), 28 | }, 29 | }, 30 | CheckDestroy: testResourceNodePool_checkDestroy(name), 31 | }) 32 | } 33 | 34 | func testDataSourceNodePools_basic(prefix string) string { 35 | return fmt.Sprintf(` 36 | resource "nomad_node_pool" "basic" { 37 | name = "%[1]s-basic" 38 | description = "Terraform test node pool" 39 | 40 | meta = { 41 | test = "%[1]s" 42 | } 43 | } 44 | 45 | resource "nomad_node_pool" "simple" { 46 | name = "%[1]s-simple" 47 | } 48 | 49 | resource "nomad_node_pool" "different_prefix" { 50 | name = "other-%[1]s" 51 | 52 | meta = { 53 | test = "%[1]s" 54 | } 55 | } 56 | 57 | data "nomad_node_pools" "all_pools" { 58 | depends_on = [ 59 | nomad_node_pool.basic, 60 | nomad_node_pool.simple, 61 | nomad_node_pool.different_prefix, 62 | ] 63 | } 64 | 65 | data "nomad_node_pools" "prefix" { 66 | depends_on = [ 67 | nomad_node_pool.basic, 68 | nomad_node_pool.simple, 69 | nomad_node_pool.different_prefix, 70 | ] 71 | 72 | prefix = "%[1]s" 73 | } 74 | 75 | data "nomad_node_pools" "filter" { 76 | depends_on = [ 77 | nomad_node_pool.basic, 78 | nomad_node_pool.simple, 79 | nomad_node_pool.different_prefix, 80 | ] 81 | 82 | filter = "Meta.test == \"%[1]s\"" 83 | } 84 | 85 | data "nomad_node_pools" "filter_with_prefix" { 86 | depends_on = [ 87 | nomad_node_pool.basic, 88 | nomad_node_pool.simple, 89 | nomad_node_pool.different_prefix, 90 | ] 91 | 92 | prefix = "%[1]s" 93 | filter = "Meta.test == \"%[1]s\"" 94 | } 95 | `, prefix) 96 | } 97 | -------------------------------------------------------------------------------- /nomad/data_source_acl_roles.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/nomad/api" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | ) 12 | 13 | func dataSourceACLRoles() *schema.Resource { 14 | return &schema.Resource{ 15 | Read: aclRolesDataSourceRead, 16 | 17 | Schema: map[string]*schema.Schema{ 18 | "prefix": { 19 | Type: schema.TypeString, 20 | Optional: true, 21 | }, 22 | 23 | "acl_roles": { 24 | Type: schema.TypeList, 25 | Computed: true, 26 | Elem: &schema.Resource{ 27 | Schema: map[string]*schema.Schema{ 28 | "id": { 29 | Description: "The ACL Role unique identifier.", 30 | Computed: true, 31 | Type: schema.TypeString, 32 | }, 33 | "name": { 34 | Description: "Unique name of the ACL role.", 35 | Computed: true, 36 | Type: schema.TypeString, 37 | }, 38 | "description": { 39 | Description: "Description for the ACL role.", 40 | Computed: true, 41 | Type: schema.TypeString, 42 | }, 43 | "policies": { 44 | Description: "The ACL policies applied to the role.", 45 | Computed: true, 46 | Type: schema.TypeSet, 47 | Elem: &schema.Resource{ 48 | Schema: map[string]*schema.Schema{ 49 | "name": { 50 | Type: schema.TypeString, 51 | Required: true, 52 | Description: "The name of the ACL policy to link.", 53 | }, 54 | }, 55 | }, 56 | }, 57 | }, 58 | }, 59 | }, 60 | }, 61 | } 62 | } 63 | 64 | func aclRolesDataSourceRead(d *schema.ResourceData, meta interface{}) error { 65 | client := meta.(ProviderConfig).client 66 | 67 | qOpts := &api.QueryOptions{ 68 | Prefix: d.Get("prefix").(string), 69 | } 70 | aclRoles, _, err := client.ACLRoles().List(qOpts) 71 | if err != nil { 72 | return fmt.Errorf("failed to list ACL Roles: %v", err) 73 | } 74 | 75 | result := make([]map[string]interface{}, len(aclRoles)) 76 | for i, aclRole := range aclRoles { 77 | 78 | policies := make([]map[string]interface{}, len(aclRole.Policies)) 79 | for i, policyLink := range aclRole.Policies { 80 | policies[i] = map[string]interface{}{"name": policyLink.Name} 81 | } 82 | 83 | result[i] = map[string]interface{}{ 84 | "id": aclRole.ID, 85 | "name": aclRole.Name, 86 | "description": aclRole.Description, 87 | "policies": policies, 88 | } 89 | } 90 | 91 | d.SetId("nomad-roles") 92 | return d.Set("acl_roles", result) 93 | } 94 | -------------------------------------------------------------------------------- /website/docs/d/allocations.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_allocations" 4 | sidebar_current: "docs-nomad-datasource-allocations" 5 | description: |- 6 | Retrieve a list of allocations from Nomad. 7 | --- 8 | 9 | # nomad_allocations 10 | 11 | Retrieve a list of allocations from Nomad. 12 | 13 | ## Example Usage 14 | 15 | ```hcl 16 | data "nomad_allocations" "example" { 17 | filter = "JobID == \"example\"" 18 | } 19 | ``` 20 | 21 | ## Argument Reference 22 | 23 | The following arguments are supported: 24 | 25 | - `prefix` `(string: )` - Specifies a string to filter allocations 26 | based on an ID prefix. 27 | - `filter` `(string: )` - Specifies the 28 | [expression][nomad_api_filter] used to filter the results. 29 | - `namespace` `(string: )` - Specifies the namespace to search for 30 | allocations in. 31 | 32 | ## Attribute Reference 33 | 34 | The following attributes are exported: 35 | 36 | - `allocations` `(list of allocations)` - A list of allocations matching the 37 | search criteria. 38 | - `id` `(string)` - The ID of the allocation. 39 | - `eval_id` `(string)` - The ID of the evaluation that generated the allocation. 40 | - `name` `(string)` - The name of the allocation. 41 | - `namespace` `(string)` - The namespace the allocation belongs to. 42 | - `node_id` `(string)` - The ID of the node to which the allocation was scheduled. 43 | - `node_name` `(string)` - The ID of the node to which the allocation was scheduled. 44 | - `job_id` `(string)` - The ID of the job related to the allocation. 45 | - `job_type` `(string)` - The type of the job related to the allocation. 46 | - `job_version` `(int)` - The version of the job that generated the allocation. 47 | - `task_group` `(string)` - The job task group related to the allocation. 48 | - `desired_status` `(string)` - The current desired status of the allocation. 49 | - `client_status` `(string)` - The current client status of the allocation. 50 | - `followup_eval_id` `(string)` - The ID of the evaluation that succeeds the allocation evaluation. 51 | - `next_allocation` `(string)` - The ID of the allocation that succeeds the allocation. 52 | - `preempted_by_allocation` `(string)` - The ID of the allocation that preempted the allocation. 53 | - `create_index` `(int)` - The Raft index in which the allocation was created. 54 | - `modify_index` `(int)` - The Raft index in which the allocation was last modified. 55 | - `create_time` `(int)` - The timestamp of when the allocation was created. 56 | - `modify_time` `(int)` - The timestamp of when the allocation was last modified. 57 | 58 | [nomad_api_filter]: https://developer.hashicorp.com/nomad/api-docs/v1.6.x#filtering 59 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Hi there, 11 | 12 | Thank you for opening an issue. Please note that we try to keep the Terraform issue tracker reserved for bug reports and feature requests. For general usage questions, please see: https://www.terraform.io/community.html. 13 | 14 | ### Terraform Version 15 | Run `terraform -v` to show the version. If you are not running the latest version of Terraform, please upgrade because your issue may have already been fixed. 16 | 17 | This command will also output the provider version, please include that as well. 18 | 19 | ### Nomad Version 20 | Run `nomad server members` in your target node to view which version of Nomad is running. Make sure to include the entire version output. 21 | 22 | ### Provider Configuration 23 | Which values are you setting in the provider configuration? 24 | 25 | ```hcl 26 | provider "nomad" { 27 | ... 28 | } 29 | ``` 30 | 31 | ### Environment Variables 32 | Do you have any Nomad specific environment variable set in the machine running Terraform? 33 | 34 | ```sh 35 | env | grep "NOMAD_" 36 | ``` 37 | 38 | ### Affected Resource(s) 39 | Please list the resources as a list, for example: 40 | - nomad_job 41 | - nomad_acl_policy 42 | 43 | If this issue appears to affect multiple resources, it may be an issue with Terraform's core, so please mention this. 44 | 45 | ### Terraform Configuration Files 46 | ```hcl 47 | # Copy-paste your Terraform configurations here - for large Terraform configs, 48 | # please use a service like Dropbox and share a link to the ZIP file. For 49 | # security, you can also encrypt the files using our GPG public key. 50 | ``` 51 | 52 | ### Debug Output 53 | Please provider a link to a GitHub Gist containing the complete debug output: https://www.terraform.io/docs/internals/debugging.html. Please do NOT paste the debug output in the issue; just paste a link to the Gist. 54 | 55 | ### Panic Output 56 | If Terraform produced a panic, please provide a link to a GitHub Gist containing the output of the `crash.log`. 57 | 58 | ### Expected Behavior 59 | What should have happened? 60 | 61 | ### Actual Behavior 62 | What actually happened? 63 | 64 | ### Steps to Reproduce 65 | Please list the steps required to reproduce the issue, for example: 66 | 1. `terraform apply` 67 | 68 | ### Important Factoids 69 | Are there anything atypical about your accounts that we should know? For example: Do you have [ACL](https://www.nomadproject.io/guides/security/acl.html) enabled? Multi-region deployment? 70 | 71 | ### References 72 | Are there any other GitHub issues (open or closed) or Pull Requests that should be linked here? For example: 73 | - GH-1234 74 | -------------------------------------------------------------------------------- /nomad/data_source_namespace.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 10 | ) 11 | 12 | func dataSourceNamespace() *schema.Resource { 13 | return &schema.Resource{ 14 | Read: namespaceDataSourceRead, 15 | 16 | Schema: map[string]*schema.Schema{ 17 | "name": { 18 | Type: schema.TypeString, 19 | Required: true, 20 | }, 21 | "description": { 22 | Type: schema.TypeString, 23 | Computed: true, 24 | }, 25 | "quota": { 26 | Type: schema.TypeString, 27 | Computed: true, 28 | }, 29 | "meta": { 30 | Type: schema.TypeMap, 31 | Computed: true, 32 | Elem: &schema.Schema{ 33 | Type: schema.TypeString, 34 | }, 35 | }, 36 | "capabilities": { 37 | Type: schema.TypeSet, 38 | Computed: true, 39 | Elem: resourceNamespaceCapabilities(), 40 | }, 41 | "node_pool_config": { 42 | Type: schema.TypeList, 43 | Computed: true, 44 | Elem: &schema.Resource{ 45 | Schema: map[string]*schema.Schema{ 46 | "default": { 47 | Computed: true, 48 | Type: schema.TypeString, 49 | }, 50 | "allowed": { 51 | Computed: true, 52 | Type: schema.TypeSet, 53 | Elem: &schema.Schema{ 54 | Type: schema.TypeString, 55 | }, 56 | }, 57 | "denied": { 58 | Computed: true, 59 | Type: schema.TypeSet, 60 | Elem: &schema.Schema{ 61 | Type: schema.TypeString, 62 | }, 63 | }, 64 | }, 65 | }, 66 | }, 67 | }, 68 | } 69 | } 70 | 71 | func namespaceDataSourceRead(d *schema.ResourceData, meta interface{}) error { 72 | client := meta.(ProviderConfig).client 73 | 74 | name := d.Get("name").(string) 75 | ns, _, err := client.Namespaces().Info(name, nil) 76 | if err != nil { 77 | return fmt.Errorf("Failed to get information about %q: %v", name, err) 78 | } 79 | 80 | if err = d.Set("description", ns.Description); err != nil { 81 | return fmt.Errorf("Failed to set 'description': %v", err) 82 | } 83 | if err = d.Set("quota", ns.Quota); err != nil { 84 | return fmt.Errorf("Failed to set 'quota': %v", err) 85 | } 86 | if err = d.Set("meta", ns.Meta); err != nil { 87 | return fmt.Errorf("Failed to set 'meta': %v", err) 88 | } 89 | if err = d.Set("capabilities", flattenNamespaceCapabilities(ns.Capabilities)); err != nil { 90 | return fmt.Errorf("Failed to set 'capabilities': %v", err) 91 | } 92 | if err = d.Set("node_pool_config", flattenNamespaceNodePoolConfig(ns.NodePoolConfiguration)); err != nil { 93 | return fmt.Errorf("Failed to set 'node_pool_config': %v", err) 94 | } 95 | 96 | d.SetId(client.Address() + "/namespace/" + name) 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /nomad/data_source_node_pool.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 11 | "github.com/hashicorp/terraform-provider-nomad/nomad/helper" 12 | ) 13 | 14 | func dataSourceNodePool() *schema.Resource { 15 | return &schema.Resource{ 16 | Read: dataSourceNodePoolRead, 17 | 18 | Schema: map[string]*schema.Schema{ 19 | "name": { 20 | Description: "Unique name for this node pool.", 21 | Type: schema.TypeString, 22 | Required: true, 23 | }, 24 | "description": { 25 | Description: "Description for this node pool.", 26 | Type: schema.TypeString, 27 | Computed: true, 28 | }, 29 | "meta": { 30 | Description: "Metadata associated with the node pool", 31 | Type: schema.TypeMap, 32 | Elem: &schema.Schema{ 33 | Type: schema.TypeString, 34 | }, 35 | Computed: true, 36 | }, 37 | "scheduler_config": { 38 | Description: "Scheduler configuration for the node pool.", 39 | Type: schema.TypeSet, 40 | Elem: &schema.Resource{ 41 | Schema: map[string]*schema.Schema{ 42 | "scheduler_algorithm": { 43 | Description: "The scheduler algorithm to use in the node pool.", 44 | Type: schema.TypeString, 45 | Computed: true, 46 | }, 47 | 48 | // This field must be a string instead of a bool (and differ from 49 | // Nomad) in order to represent a tristate. 50 | // - "enabled": memory oversubscription is enabled in the pool. 51 | // - "disabled": memory oversubscription is disabled in the pool. 52 | // - "": the global memory oversubscription value is used. 53 | "memory_oversubscription": { 54 | Description: "If true, the node pool will have memory oversubscription enabled.", 55 | Type: schema.TypeString, 56 | Computed: true, 57 | }, 58 | }, 59 | }, 60 | Computed: true, 61 | }, 62 | }, 63 | } 64 | } 65 | 66 | func dataSourceNodePoolRead(d *schema.ResourceData, meta any) error { 67 | client := meta.(ProviderConfig).client 68 | 69 | name := d.Get("name").(string) 70 | log.Printf("[DEBUG] Reading node pool %q", name) 71 | pool, _, err := client.NodePools().Info(name, nil) 72 | if err != nil { 73 | return fmt.Errorf("error reading node pool %q: %w", name, err) 74 | } 75 | log.Printf("[DEBUG] Read node pool %q", name) 76 | 77 | sw := helper.NewStateWriter(d) 78 | sw.Set("name", pool.Name) 79 | sw.Set("description", pool.Description) 80 | sw.Set("meta", pool.Meta) 81 | sw.Set("scheduler_config", flattenNodePoolSchedulerConfiguration(pool.SchedulerConfiguration)) 82 | if err := sw.Error(); err != nil { 83 | return err 84 | } 85 | 86 | d.SetId(name) 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /nomad/data_source_acl_policies_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "regexp" 9 | "testing" 10 | 11 | "github.com/hashicorp/nomad/api" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 14 | ) 15 | 16 | func TestAccDataSourceNomadAclPolicies_Basic(t *testing.T) { 17 | dataSourceName := "data.nomad_acl_policies.test" 18 | numPolicies := 2 19 | 20 | resource.ParallelTest(t, resource.TestCase{ 21 | PreCheck: func() { testAccPreCheck(t) }, 22 | Providers: testProviders, 23 | Steps: []resource.TestStep{ 24 | { 25 | PreConfig: testAccCreateNomadAclPolicies(t, numPolicies), 26 | Config: testAccNomadAclPoliciesConfig("non-existent"), 27 | }, 28 | { 29 | Config: testAccNomadAclPoliciesConfig("tf-acc-test"), 30 | Check: resource.ComposeTestCheckFunc( 31 | resource.TestCheckResourceAttr(dataSourceName, "policies.#", "2"), 32 | resource.TestMatchResourceAttr(dataSourceName, "policies.0.name", regexp.MustCompile("tf-acc-test")), 33 | resource.TestMatchResourceAttr(dataSourceName, "policies.0.description", regexp.MustCompile("Terraform ACL Policy tf-acc-test")), 34 | resource.TestMatchResourceAttr(dataSourceName, "policies.1.name", regexp.MustCompile("tf-acc-test")), 35 | resource.TestMatchResourceAttr(dataSourceName, "policies.1.description", regexp.MustCompile("Terraform ACL Policy tf-acc-test")), 36 | ), 37 | }, 38 | }, 39 | }) 40 | // ACL Policy Resource Clean-up 41 | err := sweepACLPolicies() 42 | if err != nil { 43 | t.Error(err) 44 | } 45 | } 46 | 47 | func testAccNomadAclPoliciesConfig(prefix string) string { 48 | return fmt.Sprintf(` 49 | data "nomad_acl_policies" "test" { 50 | prefix = "%s" 51 | } 52 | `, prefix) 53 | } 54 | 55 | func testAccCreateNomadAclPolicies(t *testing.T, n int) func() { 56 | return func() { 57 | client := testProvider.Meta().(ProviderConfig).client 58 | for i := 0; i < n; i++ { 59 | rName := acctest.RandomWithPrefix("tf-acc-test") 60 | policy := api.ACLPolicy{ 61 | Name: rName, 62 | Description: fmt.Sprintf("Terraform ACL Policy %s", rName), 63 | Rules: ` 64 | namespace "default" { 65 | policy = "write" 66 | } 67 | `, 68 | } 69 | _, err := client.ACLPolicies().Upsert(&policy, nil) 70 | if err != nil { 71 | t.Fatalf("error inserting ACLPolicy %q: %s", policy.Name, err.Error()) 72 | } 73 | } 74 | } 75 | } 76 | 77 | func sweepACLPolicies() error { 78 | client := testProvider.Meta().(ProviderConfig).client 79 | policies, _, err := client.ACLPolicies().List(&api.QueryOptions{}) 80 | if err != nil { 81 | return err 82 | } 83 | for _, policy := range policies { 84 | _, err := client.ACLPolicies().Delete(policy.Name, nil) 85 | if err != nil { 86 | return err 87 | } 88 | } 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /website/docs/r/acl_token.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_acl_token" 4 | sidebar_current: "docs-nomad-resource-acl-token" 5 | description: |- 6 | Manages an ACL token in Nomad. 7 | --- 8 | 9 | # nomad_acl_token 10 | 11 | Manages an ACL token in Nomad. 12 | 13 | ~> **Warning:** this resource will store any tokens it creates in 14 | Terraform's state file. Take care to 15 | [protect your state file](/docs/state/sensitive-data.html). 16 | 17 | ## Example Usage 18 | 19 | Creating a token with limited policies: 20 | 21 | ```hcl 22 | resource "nomad_acl_token" "dakota" { 23 | name = "Dakota" 24 | type = "client" 25 | policies = ["dev", "qa"] 26 | } 27 | ``` 28 | 29 | Creating a global token that will be replicated to all regions: 30 | 31 | ```hcl 32 | resource "nomad_acl_token" "dakota" { 33 | name = "Dakota" 34 | type = "client" 35 | policies = ["dev", "qa"] 36 | global = true 37 | } 38 | ``` 39 | 40 | Creating a token with full access to the cluster: 41 | 42 | ```hcl 43 | resource "nomad_acl_token" "iman" { 44 | name = "Iman" 45 | type = "management" 46 | } 47 | ``` 48 | 49 | Accessing the token: 50 | 51 | ```hcl 52 | resource "nomad_acl_token" "token" { 53 | type = "client" 54 | policies = ["dev"] 55 | } 56 | 57 | output "nomad_token" { 58 | value = nomad_acl_token.token.secret_id 59 | } 60 | ``` 61 | 62 | ## Argument Reference 63 | 64 | The following arguments are supported: 65 | 66 | - `type` `(string: )` - The type of token this is. Use `client` 67 | for tokens that will have policies associated with them. Use `management` 68 | for tokens that can perform any action. 69 | 70 | - `name` `(string: "")` - A human-friendly name for this token. 71 | 72 | - `policies` `(set: [])` - A set of policy names to associate with this 73 | token. Must be set on `client`-type tokens, must not be set on 74 | `management`-type tokens. Policies do not need to exist before being 75 | used here. 76 | 77 | - `role` `(set: [])` - The list of roles attached to the token. Each entry has 78 | `name` and `id` attributes. It may be used multiple times. 79 | 80 | - `global` `(bool: false)` - Whether the token should be replicated to all 81 | regions, or if it will only be used in the region it was created in. 82 | 83 | - `expiration_ttl` `(string: "")` - Provides a TTL for the token in the form of 84 | a time duration such as `"5m"` or `"1h"`. 85 | 86 | In addition to the above arguments, the following attributes are exported and 87 | can be referenced: 88 | 89 | - `accessor_id` `(string)` - A non-sensitive identifier for this token that 90 | can be logged and shared safely without granting any access to the cluster. 91 | 92 | - `secret_id` `(string)` - The token value itself, which is presented for 93 | access to the cluster. 94 | 95 | - `create_time` `(string)` - The timestamp the token was created. 96 | 97 | - `expiration_time` `(string)` - The timestamp after which the token is 98 | considered expired and eligible for destruction. 99 | -------------------------------------------------------------------------------- /nomad/data_source_scaling_policies.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/nomad/api" 10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | ) 13 | 14 | func dataSourceScalingPolicies() *schema.Resource { 15 | return &schema.Resource{ 16 | Read: scalingPoliciesDataSourceRead, 17 | 18 | Schema: map[string]*schema.Schema{ 19 | "job_id": { 20 | Description: "Job ID to use to filter scaling policies.", 21 | Type: schema.TypeString, 22 | Optional: true, 23 | }, 24 | "type": { 25 | Description: "Scaling policy type used to filter scaling policies.", 26 | Type: schema.TypeString, 27 | Optional: true, 28 | }, 29 | "policies": { 30 | Description: "The list of policies that match the search criteria.", 31 | Type: schema.TypeList, 32 | Computed: true, 33 | Elem: &schema.Resource{ 34 | Schema: map[string]*schema.Schema{ 35 | "id": { 36 | Description: "The scaling policy ID.", 37 | Type: schema.TypeString, 38 | Computed: true, 39 | }, 40 | "enabled": { 41 | Description: "Whether or not the scaling policy is enabled.", 42 | Type: schema.TypeBool, 43 | Computed: true, 44 | }, 45 | "type": { 46 | Description: "The scaling policy type.", 47 | Type: schema.TypeString, 48 | Computed: true, 49 | }, 50 | "target": { 51 | Description: "The scaling policy target.", 52 | Type: schema.TypeMap, 53 | Computed: true, 54 | }, 55 | }, 56 | }, 57 | }, 58 | }, 59 | } 60 | } 61 | 62 | func scalingPoliciesDataSourceRead(d *schema.ResourceData, meta interface{}) error { 63 | client := meta.(ProviderConfig).client 64 | 65 | jobID := d.Get("job_id").(string) 66 | typeQuery := d.Get("type").(string) 67 | 68 | q := &api.QueryOptions{ 69 | Params: map[string]string{ 70 | "job": jobID, 71 | "type": typeQuery, 72 | }, 73 | } 74 | policies, _, err := client.Scaling().ListPolicies(q) 75 | if err != nil { 76 | return fmt.Errorf("failed to query scaling policies: %v", err) 77 | } 78 | 79 | d.SetId(resource.UniqueId()) 80 | 81 | if err := d.Set("policies", flattenScalingPolicies(policies)); err != nil { 82 | return fmt.Errorf("failed to set policies: %v", err) 83 | } 84 | 85 | return nil 86 | } 87 | 88 | func flattenScalingPolicies(policies []*api.ScalingPolicyListStub) []interface{} { 89 | out := make([]interface{}, 0, len(policies)) 90 | 91 | for _, policy := range policies { 92 | p := map[string]interface{}{ 93 | "id": policy.ID, 94 | "enabled": policy.Enabled, 95 | "type": policy.Type, 96 | "target": policy.Target, 97 | } 98 | out = append(out, p) 99 | } 100 | 101 | return out 102 | } 103 | -------------------------------------------------------------------------------- /website/docs/r/namespace.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_namespace" 4 | sidebar_current: "docs-nomad-resource-namespace" 5 | description: |- 6 | Provisions a namespace within a Nomad cluster. 7 | --- 8 | 9 | # nomad_namespace 10 | 11 | Provisions a namespace within a Nomad cluster. 12 | 13 | Nomad auto-generates a default namespace called `default`. This namespace 14 | cannot be removed, so destroying a `nomad_namespace` resource where 15 | `name = "default"` will cause the namespace to be reset to its default 16 | configuration. 17 | 18 | ## Example Usage 19 | 20 | Registering a namespace: 21 | 22 | ```hcl 23 | resource "nomad_namespace" "dev" { 24 | name = "dev" 25 | description = "Shared development environment." 26 | quota = "dev" 27 | meta = { 28 | owner = "John Doe" 29 | foo = "bar" 30 | } 31 | } 32 | ``` 33 | 34 | Registering a namespace with a quota: 35 | 36 | ```hcl 37 | resource "nomad_quota_specification" "web_team" { 38 | name = "web-team" 39 | description = "web team quota" 40 | 41 | limits { 42 | region = "global" 43 | 44 | region_limit { 45 | cpu = 1000 46 | memory_mb = 256 47 | } 48 | } 49 | } 50 | 51 | resource "nomad_namespace" "web" { 52 | name = "web" 53 | description = "Web team production environment." 54 | quota = nomad_quota_specification.web_team.name 55 | } 56 | ``` 57 | 58 | ## Argument Reference 59 | 60 | The following arguments are supported: 61 | 62 | - `name` `(string: )` - A unique name for the namespace. 63 | - `description` `(string: "")` - A description of the namespace. 64 | - `quota` `(string: "")` - A resource quota to attach to the namespace. 65 | - `meta` `(map[string]string: )` - Specifies arbitrary KV metadata to associate with the namespace. 66 | - `capabilities` `(block: )` - A block of capabilities for the namespace. Can't 67 | be repeated. See below for the structure of this block. 68 | - `node_pool_config` `(block: )` - A block with node pool configuration for the namespace (Nomad Enterprise only). 69 | 70 | 71 | ### `capabilities` blocks 72 | 73 | The `capabilities` block describes the capabilities of the namespace. It supports 74 | the following arguments: 75 | 76 | - `enabled_task_drivers` `([]string: )` - Task drivers enabled for the namespace. 77 | - `disabled_task_drivers` `([]string: )` - Task drivers disabled for the namespace. 78 | - `enabled_network_modes` `([]string: )` - Network modes enabled for the namespace. 79 | - `disabled_network_modes` `([]string: )` - Network modes disabled for the namespace. 80 | 81 | ### `node_pool_config` blocks 82 | 83 | The `node_pool_config` block describes the node pool configuration for the 84 | namespace. 85 | 86 | - `default` `(string: )` - The default node pool for jobs that don't define one. 87 | - `allowed` `([]string: )` - The list of node pools that are allowed to be used in this namespace. 88 | - `denied` `([]string: )` - The list of node pools that are not allowed to be used in this namespace. 89 | -------------------------------------------------------------------------------- /nomad/data_source_datacenters_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/google/go-cmp/cmp" 10 | "github.com/hashicorp/nomad/api" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 12 | ) 13 | 14 | func TestAccDataSourceDatacenters_Basic(t *testing.T) { 15 | dataSourceName := "data.nomad_datacenters.dcs" 16 | 17 | resource.ParallelTest(t, resource.TestCase{ 18 | Providers: testProviders, 19 | Steps: []resource.TestStep{ 20 | { 21 | Config: testResourceDataSourceDatacentersConfig, 22 | Check: resource.ComposeTestCheckFunc( 23 | resource.TestCheckResourceAttr(dataSourceName, "datacenters.#", "1"), 24 | resource.TestCheckResourceAttr(dataSourceName, "datacenters.0", "dc1"), 25 | ), 26 | }, 27 | }, 28 | }) 29 | } 30 | 31 | func TestFilterDatacenters(t *testing.T) { 32 | cases := []struct { 33 | name string 34 | nodes []*api.NodeListStub 35 | prefix string 36 | ignoreDown bool 37 | want []string 38 | }{ 39 | { 40 | name: "single datacenter", 41 | nodes: []*api.NodeListStub{ 42 | &api.NodeListStub{Datacenter: "dc1"}, 43 | }, 44 | want: []string{"dc1"}, 45 | }, 46 | { 47 | name: "multiple datacenters", 48 | nodes: []*api.NodeListStub{ 49 | &api.NodeListStub{Datacenter: "dc1"}, 50 | &api.NodeListStub{Datacenter: "dc2"}, 51 | }, 52 | want: []string{"dc1", "dc2"}, 53 | }, 54 | { 55 | name: "duplicate datacenter", 56 | nodes: []*api.NodeListStub{ 57 | &api.NodeListStub{Datacenter: "dc1"}, 58 | &api.NodeListStub{Datacenter: "dc1"}, 59 | &api.NodeListStub{Datacenter: "dc2"}, 60 | }, 61 | want: []string{"dc1", "dc2"}, 62 | }, 63 | { 64 | name: "filter down nodes", 65 | nodes: []*api.NodeListStub{ 66 | &api.NodeListStub{Datacenter: "dc1"}, 67 | &api.NodeListStub{Datacenter: "dc1"}, 68 | &api.NodeListStub{Datacenter: "dc2", Status: "down"}, 69 | }, 70 | ignoreDown: true, 71 | want: []string{"dc1"}, 72 | }, 73 | { 74 | name: "filter with prefix", 75 | nodes: []*api.NodeListStub{ 76 | &api.NodeListStub{Datacenter: "prod-1"}, 77 | &api.NodeListStub{Datacenter: "prod-2"}, 78 | &api.NodeListStub{Datacenter: "dev"}, 79 | }, 80 | prefix: "prod", 81 | want: []string{"prod-1", "prod-2"}, 82 | }, 83 | { 84 | name: "filter with prefix, empty result", 85 | nodes: []*api.NodeListStub{ 86 | &api.NodeListStub{Datacenter: "prod-1"}, 87 | &api.NodeListStub{Datacenter: "prod-2"}, 88 | &api.NodeListStub{Datacenter: "dev"}, 89 | }, 90 | prefix: "not-there", 91 | want: []string{}, 92 | }, 93 | { 94 | name: "empty list", 95 | nodes: []*api.NodeListStub{}, 96 | want: []string{}, 97 | }, 98 | } 99 | 100 | for _, c := range cases { 101 | t.Run(c.name, func(t *testing.T) { 102 | got := filterDatacenters(c.nodes, c.prefix, c.ignoreDown) 103 | 104 | if diff := cmp.Diff(got, c.want); diff != "" { 105 | t.Fatalf("datacenters mismatch (-want +got):\n%s", diff) 106 | } 107 | }) 108 | } 109 | } 110 | 111 | var testResourceDataSourceDatacentersConfig = ` 112 | data "nomad_datacenters" "dcs" {} 113 | ` 114 | -------------------------------------------------------------------------------- /website/docs/r/dynamic_host_volume_registration.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_dynamic_host_volume_registration" 4 | sidebar_current: "docs-nomad-resource-dynamic-host-volume-registration" 5 | description: |- 6 | Manages the lifecycle of registering and deregistering host volumes. 7 | --- 8 | 9 | # nomad_dynamic_host_volume_registration 10 | 11 | Registers a dynamic host volume in Nomad that has already been created. Note 12 | that Nomad supports two workflows for dynamic host volumes: create and 13 | register. Both resources result in the same data source with the same outputs. 14 | 15 | ## Argument Reference 16 | 17 | The following arguments are supported: 18 | 19 | - `capacity` `(string: )` - The size of a volume in bytes. Either the 20 | physical size of a disk or a quota, depending on the plugin. This field must 21 | be between the `capacity_min` and `capacity_max` values unless they are 22 | omitted. Accepts human-friendly suffixes such as `"100GiB"`. 23 | 24 | - `capability` `(block: )` - Option for validating the capability of a 25 | volume. Each capability block has the following attributes: 26 | * `access_mode` `(string)` - How the volume can be mounted by 27 | allocations. Refer to the [`access_mode`][] documentation for details. 28 | * `attachment_mode` `(string)` - The storage API that will be used by the 29 | volume. Refer to the [`attachment_mode`][] documentation. 30 | 31 | - `host_path` `(string)` - The path on disk where the volume exists. 32 | 33 | - `name` `(string: )` - The name of the volume, which is used as the 34 | [`volume.source`][volume_source] field in job specifications that claim this 35 | volume. Host volume names must be unique per node. Names are visible to any 36 | user with `node:read` ACL, even across namespaces, so they should not be 37 | treated as sensitive values. 38 | 39 | - `namespace` `(string: )` - The namespace of the volume. This field 40 | overrides the namespace provided by the `-namespace` flag or `NOMAD_NAMESPACE` 41 | environment variable. Defaults to `"default"` if unset. 42 | 43 | - `node_id` `(string: )` - A specific node where the volume is 44 | mounted. 45 | 46 | - `parameters` `(map: )` - A key-value map of strings 47 | passed directly to the plugin to configure the volume. The details of these 48 | parameters are specific to the plugin. 49 | 50 | ## Importing Dynamic Host Volumes 51 | 52 | Dynamic host volumes are imported using the pattern `@` . 53 | 54 | ```console 55 | $ terraform import nomad_dynamic_host_volume_registration.mysql mysql@my-namespace 56 | nomad_dynamic_host_volume_registration.mysql: Importing from ID "mysql@my-namespace"... 57 | nomad_dynamic_host_volume_registration.mysql: Import prepared! 58 | Prepared nomad_dynamic_host_volume_registration for import 59 | nomad_dynamic_host_volume_registration.mysql: Refreshing state... [id=mysql@my-namespace] 60 | 61 | Import successful! 62 | 63 | The resources that were imported are shown above. These resources are now in 64 | your Terraform state and will henceforth be managed by Terraform. 65 | ``` 66 | 67 | 68 | [`access_mode`]: /nomad/docs/other-specifications/volume/capability#access_mode 69 | [`attachment_mode`]: /nomad/docs/other-specifications/volume/capability#attachment_mode 70 | [volume_source]: /nomad/docs/job-specification/volume#source 71 | -------------------------------------------------------------------------------- /nomad/data_source_volumes.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strconv" 10 | 11 | "github.com/hashicorp/nomad/api" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" 14 | ) 15 | 16 | func dataSourceVolumes() *schema.Resource { 17 | return &schema.Resource{ 18 | Read: volumesDataSourceRead, 19 | 20 | Schema: map[string]*schema.Schema{ 21 | "type": { 22 | Type: schema.TypeString, 23 | Optional: true, 24 | Description: "Volume Type (currently only 'csi')", 25 | Default: "csi", 26 | Elem: &schema.Schema{ 27 | Type: schema.TypeString, 28 | ValidateFunc: validation.StringInSlice([]string{"csi"}, false), 29 | }, 30 | }, 31 | "node_id": { 32 | Type: schema.TypeString, 33 | Description: "Volume node filter", 34 | Optional: true, 35 | Elem: &schema.Schema{Type: schema.TypeString}, 36 | }, 37 | "plugin_id": { 38 | Type: schema.TypeString, 39 | Description: "Plugin ID filter", 40 | Optional: true, 41 | Elem: &schema.Schema{Type: schema.TypeString}, 42 | }, 43 | "namespace": { 44 | Description: "Volume namespace filter", 45 | Type: schema.TypeString, 46 | Optional: true, 47 | Default: "default", 48 | }, 49 | 50 | "volumes": { 51 | Description: "Volumes", 52 | Computed: true, 53 | Type: schema.TypeList, 54 | Elem: &schema.Schema{Type: schema.TypeMap}, 55 | }, 56 | }, 57 | } 58 | } 59 | 60 | func volumesDataSourceRead(d *schema.ResourceData, meta interface{}) error { 61 | client := meta.(ProviderConfig).client 62 | 63 | ns := d.Get("namespace").(string) 64 | if ns == "" { 65 | ns = "default" 66 | } 67 | q := &api.QueryOptions{ 68 | Namespace: ns, 69 | Params: make(map[string]string, 0), 70 | } 71 | if v, ok := d.GetOk("node_id"); ok && v != "" { 72 | q.Params["node_id"] = v.(string) 73 | } 74 | if v, ok := d.GetOk("plugin_id"); ok && v != "" { 75 | q.Params["plugin_id"] = v.(string) 76 | } 77 | 78 | log.Printf("[DEBUG] Reading list of volumes from Nomad") 79 | resp, _, err := client.CSIVolumes().List(q) 80 | if err != nil { 81 | return fmt.Errorf("error reading volumes from Nomad: %s", err) 82 | } 83 | volumes := make([]map[string]interface{}, 0, len(resp)) 84 | for _, v := range resp { 85 | volume := map[string]interface{}{ 86 | "id": v.ID, 87 | "namespace": v.Namespace, 88 | "name": v.Name, 89 | "external_id": v.ExternalID, 90 | "access_mode": v.AccessMode, 91 | "attachement_mode": v.AttachmentMode, 92 | "schedulable": strconv.FormatBool(v.Schedulable), 93 | "plugin_id": v.PluginID, 94 | "plugin_provider": v.Provider, 95 | "controller_required": strconv.FormatBool(v.ControllerRequired), 96 | "controllers_healthy": strconv.Itoa(v.ControllersHealthy), 97 | "controllers_expected": strconv.Itoa(v.ControllersExpected), 98 | "nodes_healthy": strconv.Itoa(v.NodesHealthy), 99 | "nodes_expected": strconv.Itoa(v.NodesExpected), 100 | } 101 | volumes = append(volumes, volume) 102 | } 103 | log.Printf("[DEBUG] Finished reading volumes from Nomad") 104 | d.SetId(client.Address() + "/v1/volumes") 105 | 106 | return d.Set("volumes", volumes) 107 | } 108 | -------------------------------------------------------------------------------- /nomad/data_source_acl_tokens.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "time" 9 | 10 | "github.com/hashicorp/nomad/api" 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 12 | ) 13 | 14 | func dataSourceACLTokens() *schema.Resource { 15 | return &schema.Resource{ 16 | Read: aclTokensDataSourceRead, 17 | 18 | Schema: map[string]*schema.Schema{ 19 | "prefix": { 20 | Type: schema.TypeString, 21 | Optional: true, 22 | }, 23 | 24 | "acl_tokens": { 25 | Type: schema.TypeList, 26 | Computed: true, 27 | Elem: &schema.Resource{ 28 | Schema: map[string]*schema.Schema{ 29 | "accessor_id": { 30 | Type: schema.TypeString, 31 | Computed: true, 32 | }, 33 | "name": { 34 | Type: schema.TypeString, 35 | Computed: true, 36 | }, 37 | "type": { 38 | Type: schema.TypeString, 39 | Computed: true, 40 | }, 41 | "policies": { 42 | Type: schema.TypeList, 43 | Computed: true, 44 | Elem: &schema.Schema{Type: schema.TypeString}, 45 | }, 46 | "roles": { 47 | Description: "The roles that are applied to the token.", 48 | Computed: true, 49 | Type: schema.TypeSet, 50 | Elem: &schema.Resource{ 51 | Schema: map[string]*schema.Schema{ 52 | "id": { 53 | Type: schema.TypeString, 54 | Computed: true, 55 | Description: "The ID of the ACL role.", 56 | }, 57 | "name": { 58 | Type: schema.TypeString, 59 | Computed: true, 60 | Description: "The name of the ACL role.", 61 | }, 62 | }, 63 | }, 64 | }, 65 | "global": { 66 | Type: schema.TypeBool, 67 | Computed: true, 68 | }, 69 | "create_time": { 70 | Type: schema.TypeString, 71 | Computed: true, 72 | }, 73 | "expiration_time": { 74 | Description: "The point after which a token is considered revoked and eligible for destruction.", 75 | Computed: true, 76 | Type: schema.TypeString, 77 | }, 78 | }, 79 | }, 80 | }, 81 | }, 82 | } 83 | } 84 | 85 | func aclTokensDataSourceRead(d *schema.ResourceData, meta interface{}) error { 86 | client := meta.(ProviderConfig).client 87 | 88 | qOpts := &api.QueryOptions{ 89 | Prefix: d.Get("prefix").(string), 90 | } 91 | tokens, _, err := client.ACLTokens().List(qOpts) 92 | if err != nil { 93 | return fmt.Errorf("error while getting the list of tokens: %v", err) 94 | } 95 | 96 | result := make([]map[string]interface{}, len(tokens)) 97 | for i, t := range tokens { 98 | 99 | var expirationTime string 100 | if t.ExpirationTime != nil { 101 | expirationTime = t.ExpirationTime.Format(time.RFC3339) 102 | } 103 | 104 | roles := make([]map[string]interface{}, len(t.Roles)) 105 | for i, roleLink := range t.Roles { 106 | roles[i] = map[string]interface{}{"id": roleLink.ID, "name": roleLink.Name} 107 | } 108 | 109 | result[i] = map[string]interface{}{ 110 | "accessor_id": t.AccessorID, 111 | "name": t.Name, 112 | "type": t.Type, 113 | "policies": t.Policies, 114 | "roles": roles, 115 | "global": t.Global, 116 | "create_time": t.CreateTime.String(), 117 | "expiration_time": expirationTime, 118 | } 119 | } 120 | 121 | d.SetId("nomad-tokens") 122 | return d.Set("acl_tokens", result) 123 | } 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nomad Terraform Provider 2 | ======================== 3 | 4 | - Website: https://registry.terraform.io/providers/hashicorp/nomad 5 | 6 | Maintainers 7 | ----------- 8 | 9 | This provider plugin is maintained by the Nomad team at [HashiCorp](https://www.hashicorp.com/). 10 | 11 | Requirements 12 | ------------ 13 | 14 | - [Terraform](https://www.terraform.io/downloads.html) 1.0.x 15 | - [Go](https://golang.org/doc/install) 1.24 (to build the provider plugin) 16 | 17 | Building The Provider 18 | --------------------- 19 | 20 | Clone repository to: `$GOPATH/src/github.com/hashicorp/terraform-provider-nomad` 21 | 22 | ```sh 23 | $ mkdir -p $GOPATH/src/github.com/hashicorp; cd $GOPATH/src/github.com/hashicorp 24 | $ git clone git@github.com:hashicorp/terraform-provider-nomad 25 | ``` 26 | 27 | Enter the provider directory and build the provider 28 | 29 | ```sh 30 | $ cd $GOPATH/src/github.com/hashicorp/terraform-provider-nomad 31 | $ make build 32 | ``` 33 | 34 | Using the provider 35 | ---------------------- 36 | 37 | To use a released provider in your Terraform environment, run 38 | [`terraform init`](https://www.terraform.io/docs/commands/init.html) and 39 | Terraform will automatically install the provider. To specify a particular 40 | provider version when installing released providers, see the [Terraform 41 | documentation on provider versioning](https://www.terraform.io/docs/configuration/providers.html#version-provider-versions). 42 | 43 | Refer to the section below for instructions on how to to use a custom-built 44 | version of the provider. 45 | 46 | For either installation method, documentation about the provider specific 47 | configuration options can be found on the 48 | [provider's website](https://www.terraform.io/docs/providers/nomad/). 49 | 50 | Developing the Provider 51 | --------------------------- 52 | 53 | If you wish to work on the provider, you'll first need 54 | [Go](http://www.golang.org) installed on your machine (version 1.20+ is 55 | *required*). You'll also need to correctly setup a 56 | [GOPATH](http://golang.org/doc/code.html#GOPATH), as well as adding 57 | `$GOPATH/bin` to your `$PATH`. 58 | 59 | To compile the provider, run `make build`. This will build the provider and put 60 | the provider binary in the `$GOPATH/bin` directory. 61 | 62 | ```sh 63 | $ make build 64 | ... 65 | $ $GOPATH/bin/terraform-provider-nomad 66 | ... 67 | ``` 68 | 69 | To use the provider binary in a local configuration, create a file called 70 | `.terraformrc` in your home directory and specify a [development 71 | override][tf_docs_dev_overrides] for the `nomad` provider. 72 | 73 | ```hcl 74 | provider_installation { 75 | dev_overrides { 76 | "hashicorp/nomad" = "/bin/" 77 | } 78 | } 79 | ``` 80 | 81 | In order to test the provider, you can simply run `make test`. 82 | 83 | ```sh 84 | $ make test 85 | ``` 86 | 87 | In order to run the full suite of Acceptance tests: 88 | 89 | 1. setup test environment 90 | ```sh 91 | nomad agent -dev -acl-enabled 92 | ``` 93 | 94 | 2. obtain a management token 95 | ```sh 96 | nomad acl bootstrap 97 | ``` 98 | 99 | 4. set nomad agent address (if differs from `http://localhost:4646`) and token secret ID and run tests 100 | ```sh 101 | NOMAD_TOKEN= NOMAD_ADDR=http://: make testacc 102 | ``` 103 | 104 | Acceptance tests expect fresh instance of nomad agent, so all steps must be 105 | performed every time tests are executed. 106 | 107 | *Note:* Acceptance tests create real resources, and may cost money to run. 108 | 109 | [tf_docs_dev_overrides]: https://developer.hashicorp.com/terraform/cli/config/config-file#development-overrides-for-provider-developers 110 | -------------------------------------------------------------------------------- /.github/workflows/jira-sync.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | on: 5 | issues: 6 | types: [labeled, opened, closed, deleted, reopened] 7 | issue_comment: 8 | types: [created] 9 | workflow_dispatch: 10 | 11 | name: Jira Issue Sync 12 | 13 | jobs: 14 | sync: 15 | runs-on: ubuntu-latest 16 | name: Jira Issue sync 17 | steps: 18 | - name: Login 19 | uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 20 | env: 21 | JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} 22 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} 23 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} 24 | 25 | - name: Set ticket type 26 | id: set-ticket-type 27 | run: | 28 | echo "TYPE=GH Issue" >> "$GITHUB_OUTPUT" 29 | 30 | - name: Create ticket if an issue is labeled with hcc/jira 31 | if: github.event.action == 'labeled' && github.event.label.name == 'hcc/jira' 32 | uses: tomhjp/gh-action-jira-create@3ed1789cad3521292e591a7cfa703215ec1348bf # v0.2.1 33 | with: 34 | project: NMD 35 | issuetype: "${{ steps.set-ticket-type.outputs.TYPE }}" 36 | summary: "${{ github.event.repository.name }} [${{ steps.set-ticket-type.outputs.TYPE }} #${{ github.event.issue.number }}]: ${{ github.event.issue.title }}" 37 | description: "${{ github.event.issue.body || github.event.pull_request.body }}\n\n_Created in GitHub by ${{ github.actor }}._" 38 | # customfield_10089 is "Issue Link" 39 | # customfield_10371 is "Source" (use JIRA API to retrieve) 40 | # customerfield_10091 is "Team (R&D) 41 | # customfield_10001 is Team (jira default teams?) 42 | extraFields: '{ "customfield_10089": "${{ github.event.issue.html_url || github.event.pull_request.html_url }}", 43 | "customfield_10001": "72e166fb-d26c-4a61-b0de-7a290d91708f", 44 | "components": [{ "name": "terraform-provider-nomad" }], 45 | "labels": ["community", "GitHub"] }' 46 | env: 47 | JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} 48 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} 49 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} 50 | 51 | - name: Search 52 | if: github.event.action != 'opened' 53 | id: search 54 | uses: tomhjp/gh-action-jira-search@04700b457f317c3e341ce90da5a3ff4ce058f2fa # v0.2.2 55 | with: 56 | # cf[10089] is Issue Link (use JIRA API to retrieve) 57 | jql: 'cf[10089] = "${{ github.event.issue.html_url || github.event.pull_request.html_url }}"' 58 | 59 | - name: Sync comment 60 | if: github.event.action == 'created' && steps.search.outputs.issue 61 | uses: tomhjp/gh-action-jira-comment@6eb6b9ead70221916b6badd118c24535ed220bd9 # v0.2.0 62 | with: 63 | issue: ${{ steps.search.outputs.issue }} 64 | comment: "${{ github.actor }} ${{ github.event.review.state || 'commented' }}:\n\n${{ github.event.comment.body || github.event.review.body }}\n\n${{ github.event.comment.html_url || github.event.review.html_url }}" 65 | 66 | - name: Close ticket 67 | if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue 68 | uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 69 | with: 70 | issue: ${{ steps.search.outputs.issue }} 71 | transition: "Closed" 72 | 73 | - name: Reopen ticket 74 | if: github.event.action == 'reopened' && steps.search.outputs.issue 75 | uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 76 | with: 77 | issue: ${{ steps.search.outputs.issue }} 78 | transition: "To Do" 79 | -------------------------------------------------------------------------------- /website/docs/d/job.html.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "nomad" 3 | page_title: "Nomad: nomad_job" 4 | sidebar_current: "docs-nomad-datasource-job" 5 | description: |- 6 | Get information on an job. 7 | --- 8 | 9 | # nomad_job 10 | 11 | Get information on a job ID. The aim of this datasource is to enable 12 | you to act on various settings and states of a particular job. 13 | 14 | An error is triggered if zero or more than one result is returned by the query. 15 | 16 | ## Example Usage 17 | 18 | Get the data about a snapshot: 19 | 20 | ```hcl 21 | data "nomad_job" "example" { 22 | job_id = "example" 23 | namespace = "dev" 24 | } 25 | ``` 26 | 27 | ## Argument Reference 28 | 29 | The following arguments are supported: 30 | 31 | * `job_id`: `(string)` ID of the job. 32 | * `namespace`: `(string)` Namespace of the specified job. 33 | 34 | ## Attributes Reference 35 | 36 | The following attributes are exported: 37 | 38 | * `name`: `(string)` Name of the job. 39 | * `type`: `(string)` Scheduler type used during job creation. 40 | * `version`: `(integer)` Version of the specified job. 41 | * `region`: `(string)` Region where the Nomad cluster resides. 42 | * `datacenters`: `(list of strings)` Datacenters allowed to run the specified job. 43 | * `status`: `(string)` Execution status of the specified job. 44 | * `status_description`: `(string)` Status description of the specified job. 45 | * `submit_time`: `(integer)` Job submission date. 46 | * `create_index`: `(integer)` Creation Index. 47 | * `modify_index`: `(integer)` Modification Index. 48 | * `job_modify_index`: `(integer)` Job modify index (used for version verification). 49 | * `stop`: `(boolean)` Job enabled status. 50 | * `priority`: `(integer)` Used for the prioritization of scheduling and resource access. 51 | * `parent_id`: `(string)` Job's parent ID. 52 | * `task_groups`: `(list of maps)` A list of of the job's task groups. 53 | * `placed_canaries`: `(string)` 54 | * `auto_revert`: `(boolean)` 55 | * `promoted`: `(boolean)` 56 | * `desired_canaries`: `(integer)` 57 | * `desired_total`: `(integer)` 58 | * `placed_alloc`: `(integer)` 59 | * `healthy_alloc`: `(integer)` 60 | * `unhealthy_alloc`: `(integer)` 61 | * `stable`: `(boolean)` Job stability status. 62 | * `all_at_once`: `(boolean)` If the scheduler can make partial placements on oversubscribed nodes. 63 | * `contraints`: `(list of maps)` Job constraints. 64 | * `ltarget`: `(string)` Attribute being constrained. 65 | * `rtarget`: `(string)` Constraint value. 66 | * `operand`: `(string)` Operator used to compare the attribute to the constraint. 67 | * `update_strategy`: `(list of maps)` Job's update strategy which controls rolling updates and canary deployments. 68 | * `stagger`: `(string)` Delay between migrating job allocations off cluster nodes marked for draining. 69 | * `max_parallel`: `(integer)` Number of task groups that can be updated at the same time. 70 | * `health_check`: `(string)` Type of mechanism in which allocations health is determined. 71 | * `min_healthy_time`: `(string)` Minimum time the job allocation must be in the healthy state. 72 | * `healthy_deadline`: `(string)` Deadline in which the allocation must be marked as healthy after which the allocation is automatically transitioned to unhealthy. 73 | * `auto_revert`: `(boolean)` Specifies if the job should auto-revert to the last stable job on deployment failure. 74 | * `canary`: `(integer)` Number of canary jobs that need to reach healthy status before unblocking rolling updates. 75 | * `periodic_config`: `(list of maps)` Job's periodic configuration (time based scheduling). 76 | * `enabled`: `(boolean)` If periodic scheduling is enabled for the specified job. 77 | * `spec`: `(string)` 78 | * `spec_type`: `(string)` 79 | * `prohibit_overlap`: `(boolean)` If the specified job should wait until previous instances of the job have completed. 80 | * `timezone`: `(string)` Time zone to evaluate the next launch interval against. 81 | -------------------------------------------------------------------------------- /nomad/data_source_deployments_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "regexp" 9 | "strconv" 10 | "testing" 11 | 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 14 | ) 15 | 16 | // Test will 17 | // * Create job with update stanza 18 | // * Pass config with same job spec and the deployments data source 19 | // * Check there is 1 deployment and Destroy after this step 20 | // * Pass just the data source config 21 | // * Verify deployment is cancelled 22 | func TestAccDataSourceDeployments(t *testing.T) { 23 | resource.Test(t, resource.TestCase{ 24 | Providers: testProviders, 25 | PreCheck: func() { testAccPreCheck(t) }, 26 | Steps: []resource.TestStep{ 27 | { 28 | Config: testAccCheckDataSourceNomadDeploymentsJobCfg, 29 | }, 30 | { 31 | Config: testAccCheckDataSourceNomadDeploymentsCfgWithJob, 32 | Check: func(s *terraform.State) error { 33 | rs, _ := s.RootModule().Resources["data.nomad_deployments.foobar"] 34 | is := rs.Primary 35 | v, ok := is.Attributes["deployments.#"] 36 | if !ok { 37 | return fmt.Errorf("Attribute '%s' not found", "deployments.#") 38 | } 39 | numDeployments, err := strconv.Atoi(v) 40 | if err != nil { 41 | return fmt.Errorf("received error parsing 'deployments.#': %v", err) 42 | } else if numDeployments < 1 { 43 | return fmt.Errorf("Attribute 'deployments.#' should be >= 1, got %v", v) 44 | } 45 | return nil 46 | }, 47 | Destroy: true, 48 | }, 49 | { 50 | Config: testAccCheckDataSourceNomadDeploymentsCfg, 51 | Check: func(s *terraform.State) error { 52 | re := regexp.MustCompile(`^deployments.(\d+).JobID$`) 53 | rs, _ := s.RootModule().Resources["data.nomad_deployments.foobar"] 54 | is := rs.Primary 55 | index := -1 56 | for k, v := range is.Attributes { 57 | // any match of this job should be fine, all deployments should be "cancelled" 58 | if submatch := re.FindStringSubmatch(k); submatch != nil && v == "foo_deploy" { 59 | index, _ = strconv.Atoi(submatch[1]) 60 | break 61 | } 62 | } 63 | if index < 0 { 64 | return fmt.Errorf("did not find expected deployment for job 'foo_deploy'") 65 | } 66 | statusAttr := fmt.Sprintf("deployments.%d.Status", index) 67 | if s, ok := is.Attributes[statusAttr]; !ok || s != "cancelled" { 68 | if !ok { 69 | return fmt.Errorf("did not find expected attributed '%v'", statusAttr) 70 | } 71 | return fmt.Errorf("'%v': expected 'cancelled', got '%v' to be 'cancelled'", statusAttr, s) 72 | } 73 | return nil 74 | }, 75 | }, 76 | }, 77 | 78 | // Somewhat-abuse CheckDestroy to actually do our cleanup... :/ 79 | CheckDestroy: testResourceJob_forceDestroyWithPurge("foo_deploy", "default"), 80 | }) 81 | } 82 | 83 | var testAccCheckDataSourceNomadDeploymentsJobCfg = ` 84 | resource "nomad_job" "foobar" { 85 | jobspec = <)` - The node pool of the node where the 83 | volume is mounted. 84 | 85 | - `parameters` `(map)` - A key-value map of strings 86 | passed directly to the plugin to configure the volume. The details of these 87 | parameters are specific to the plugin. 88 | 89 | - `plugin_id` `(string: )` - The ID of the [dynamic host volume 90 | plugin][dhv_plugin] that manages this volume. 91 | 92 | [`constraint`]: /nomad/docs/job-specification/constraint 93 | [node attribute]: /nomad/docs/runtime/interpolation#interpreted_node_vars 94 | [`access_mode`]: /nomad/docs/other-specifications/volume/capability#access_mode 95 | [`attachment_mode`]: /nomad/docs/other-specifications/volume/capability#attachment_mode 96 | [volume_source]: /nomad/docs/job-specification/volume#source 97 | -------------------------------------------------------------------------------- /nomad/data_source_node_pools.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/hashicorp/nomad/api" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 14 | ) 15 | 16 | func dataSourceNodePools() *schema.Resource { 17 | return &schema.Resource{ 18 | Read: dataSourceNodePoolsRead, 19 | 20 | Schema: map[string]*schema.Schema{ 21 | "prefix": { 22 | Description: "Specifies a string to filter node pools based on a name prefix.", 23 | Type: schema.TypeString, 24 | Optional: true, 25 | }, 26 | "filter": { 27 | Description: "Specifies the expression used to filter the results.", 28 | Type: schema.TypeString, 29 | Optional: true, 30 | }, 31 | "node_pools": { 32 | Description: "List of node pools returned", 33 | Type: schema.TypeList, 34 | Elem: &schema.Resource{ 35 | Schema: map[string]*schema.Schema{ 36 | "name": { 37 | Description: "Unique name for this node pool.", 38 | Type: schema.TypeString, 39 | Computed: true, 40 | }, 41 | "description": { 42 | Description: "Description for this node pool.", 43 | Type: schema.TypeString, 44 | Computed: true, 45 | }, 46 | "meta": { 47 | Description: "Metadata associated with the node pool", 48 | Type: schema.TypeMap, 49 | Elem: &schema.Schema{ 50 | Type: schema.TypeString, 51 | }, 52 | Computed: true, 53 | }, 54 | "scheduler_config": { 55 | Description: "Scheduler configuration for the node pool.", 56 | Type: schema.TypeSet, 57 | Elem: &schema.Resource{ 58 | Schema: map[string]*schema.Schema{ 59 | "scheduler_algorithm": { 60 | Description: "The scheduler algorithm to use in the node pool.", 61 | Type: schema.TypeString, 62 | Computed: true, 63 | }, 64 | 65 | // This field must be a string instead of a bool (and differ from 66 | // Nomad) in order to represent a tristate. 67 | // - "enabled": memory oversubscription is enabled in the pool. 68 | // - "disabled": memory oversubscription is disabled in the pool. 69 | // - "": the global memory oversubscription value is used. 70 | "memory_oversubscription": { 71 | Description: "If true, the node pool will have memory oversubscription enabled.", 72 | Type: schema.TypeString, 73 | Computed: true, 74 | }, 75 | }, 76 | }, 77 | Computed: true, 78 | }, 79 | }, 80 | }, 81 | Computed: true, 82 | }, 83 | }, 84 | } 85 | } 86 | 87 | func dataSourceNodePoolsRead(d *schema.ResourceData, meta any) error { 88 | client := meta.(ProviderConfig).client 89 | 90 | prefix := d.Get("prefix").(string) 91 | filter := d.Get("filter").(string) 92 | id := strconv.Itoa(schema.HashString(prefix + filter)) 93 | 94 | log.Printf("[DEBUG] Reading node pool list") 95 | resp, _, err := client.NodePools().List(&api.QueryOptions{ 96 | Prefix: prefix, 97 | Filter: filter, 98 | }) 99 | if err != nil { 100 | if strings.Contains(err.Error(), "404") { 101 | d.SetId("") 102 | return nil 103 | } 104 | return fmt.Errorf("error reading node pools: %w", err) 105 | } 106 | 107 | pools := make([]map[string]any, len(resp)) 108 | for i, p := range resp { 109 | pools[i] = map[string]any{ 110 | "name": p.Name, 111 | "description": p.Description, 112 | "meta": p.Meta, 113 | "scheduler_config": flattenNodePoolSchedulerConfiguration(p.SchedulerConfiguration), 114 | } 115 | } 116 | log.Printf("[DEBUG] Read node pool list") 117 | 118 | d.SetId(id) 119 | return d.Set("node_pools", pools) 120 | } 121 | -------------------------------------------------------------------------------- /nomad/data_source_acl_token.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strings" 10 | "time" 11 | 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 13 | ) 14 | 15 | func dataSourceACLToken() *schema.Resource { 16 | return &schema.Resource{ 17 | Read: dataSourceACLTokenRead, 18 | Schema: map[string]*schema.Schema{ 19 | "accessor_id": { 20 | Description: "Non-sensitive identifier for this token.", 21 | Required: true, 22 | Type: schema.TypeString, 23 | }, 24 | "secret_id": { 25 | Description: "The token value itself.", 26 | Computed: true, 27 | Sensitive: true, 28 | Type: schema.TypeString, 29 | }, 30 | "name": { 31 | Description: "Human-friendly name of the ACL token.", 32 | Computed: true, 33 | Type: schema.TypeString, 34 | }, 35 | "type": { 36 | Description: "The type of the token.", 37 | Computed: true, 38 | Type: schema.TypeString, 39 | }, 40 | "policies": { 41 | Description: "List of policy names associated with this token.", 42 | Computed: true, 43 | Type: schema.TypeSet, 44 | Elem: &schema.Schema{Type: schema.TypeString}, 45 | }, 46 | "roles": { 47 | Description: "The roles that are applied to the token.", 48 | Computed: true, 49 | Type: schema.TypeSet, 50 | Elem: &schema.Resource{ 51 | Schema: map[string]*schema.Schema{ 52 | "id": { 53 | Type: schema.TypeString, 54 | Computed: true, 55 | Description: "The ID of the ACL role.", 56 | }, 57 | "name": { 58 | Type: schema.TypeString, 59 | Computed: true, 60 | Description: "The name of the ACL role.", 61 | }, 62 | }, 63 | }, 64 | }, 65 | "global": { 66 | Description: "Whether the token is replicated to all regions, or if it will only be used in the region it was created.", 67 | Computed: true, 68 | Type: schema.TypeBool, 69 | }, 70 | "create_time": { 71 | Description: "Date and time the token was created.", 72 | Computed: true, 73 | Type: schema.TypeString, 74 | }, 75 | "expiration_ttl": { 76 | Description: "The expiration TTL for the token.", 77 | Computed: true, 78 | Type: schema.TypeString, 79 | }, 80 | "expiration_time": { 81 | Description: "The point after which a token is considered revoked and eligible for destruction.", 82 | Computed: true, 83 | Type: schema.TypeString, 84 | }, 85 | }, 86 | } 87 | } 88 | 89 | func dataSourceACLTokenRead(d *schema.ResourceData, meta interface{}) error { 90 | providerConfig := meta.(ProviderConfig) 91 | client := providerConfig.client 92 | accessor := d.Get("accessor_id").(string) 93 | 94 | // retrieve the token 95 | log.Printf("[DEBUG] Reading ACL Token %q", accessor) 96 | token, _, err := client.ACLTokens().Info(accessor, nil) 97 | if err != nil { 98 | 99 | // As of Nomad 0.4.1, the API client returns an error for 404 100 | // rather than a nil result, so we must check this way. 101 | if strings.Contains(err.Error(), "404") { 102 | d.SetId("") 103 | return nil 104 | } 105 | 106 | return fmt.Errorf("error reading ACL token %q: %s", accessor, err.Error()) 107 | } 108 | log.Printf("[DEBUG] Read ACL token %q", accessor) 109 | 110 | var expirationTime string 111 | if token.ExpirationTime != nil { 112 | expirationTime = token.ExpirationTime.Format(time.RFC3339) 113 | } 114 | 115 | roles := make([]map[string]interface{}, len(token.Roles)) 116 | for i, roleLink := range token.Roles { 117 | roles[i] = map[string]interface{}{"id": roleLink.ID, "name": roleLink.Name} 118 | } 119 | 120 | d.SetId(accessor) 121 | d.Set("name", token.Name) 122 | d.Set("type", token.Type) 123 | d.Set("policies", token.Policies) 124 | d.Set("roles", token.Roles) 125 | d.Set("secret_id", token.SecretID) 126 | d.Set("global", token.Global) 127 | d.Set("create_time", token.CreateTime.UTC().String()) 128 | d.Set("expiration_ttl", token.ExpirationTTL.String()) 129 | d.Set("expiration_time", expirationTime) 130 | 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hashicorp/terraform-provider-nomad 2 | 3 | go 1.25.4 4 | 5 | replace ( 6 | // Fix error tidying due to Nomad downstream dependencies and the recent 7 | // migration of the metrics library. 8 | github.com/armon/go-metrics => github.com/hashicorp/go-metrics v0.0.0-20230509193637-d9ca9af9f1f9 9 | // Set to the version used by Nomad 10 | // https://github.com/hashicorp/nomad/blob/v1.6.0-rc.1/go.mod#L74 11 | github.com/hashicorp/hcl/v2 => github.com/hashicorp/hcl/v2 v2.9.2-0.20220525143345-ab3cae0737bc 12 | ) 13 | 14 | require ( 15 | github.com/dustin/go-humanize v1.0.1 16 | github.com/google/go-cmp v0.7.0 17 | github.com/hashicorp/go-cleanhttp v0.5.2 18 | github.com/hashicorp/go-cty v1.5.0 19 | github.com/hashicorp/go-multierror v1.1.1 20 | github.com/hashicorp/go-version v1.8.0 21 | github.com/hashicorp/nomad v1.11.1 22 | github.com/hashicorp/nomad/api v0.0.0-20251111223019-f626dee323f3 23 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1 24 | github.com/shoenig/test v1.12.2 25 | github.com/stretchr/testify v1.11.1 26 | golang.org/x/exp v0.0.0-20250808145144-a408d31f581a 27 | ) 28 | 29 | require ( 30 | github.com/ProtonMail/go-crypto v1.1.6 // indirect 31 | github.com/agext/levenshtein v1.2.2 // indirect 32 | github.com/apparentlymart/go-cidr v1.1.0 // indirect 33 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 34 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 35 | github.com/bmatcuk/doublestar v1.1.5 // indirect 36 | github.com/cloudflare/circl v1.6.1 // indirect 37 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 38 | github.com/fatih/color v1.18.0 // indirect 39 | github.com/go-test/deep v1.1.1 // indirect 40 | github.com/golang/protobuf v1.5.4 // indirect 41 | github.com/google/uuid v1.6.0 // indirect 42 | github.com/gorilla/websocket v1.5.3 // indirect 43 | github.com/hashicorp/cronexpr v1.1.3 // indirect 44 | github.com/hashicorp/errwrap v1.1.0 // indirect 45 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 46 | github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 // indirect 47 | github.com/hashicorp/go-hclog v1.6.3 // indirect 48 | github.com/hashicorp/go-plugin v1.7.0 // indirect 49 | github.com/hashicorp/go-retryablehttp v0.7.8 // indirect 50 | github.com/hashicorp/go-rootcerts v1.0.2 // indirect 51 | github.com/hashicorp/go-uuid v1.0.3 // indirect 52 | github.com/hashicorp/hc-install v0.9.2 // indirect 53 | github.com/hashicorp/hcl/v2 v2.24.0 // indirect 54 | github.com/hashicorp/logutils v1.0.0 // indirect 55 | github.com/hashicorp/terraform-exec v0.23.1 // indirect 56 | github.com/hashicorp/terraform-json v0.27.1 // indirect 57 | github.com/hashicorp/terraform-plugin-go v0.29.0 // indirect 58 | github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect 59 | github.com/hashicorp/terraform-registry-address v0.4.0 // indirect 60 | github.com/hashicorp/terraform-svchost v0.1.1 // indirect 61 | github.com/hashicorp/yamux v0.1.2 // indirect 62 | github.com/mattn/go-colorable v0.1.14 // indirect 63 | github.com/mattn/go-isatty v0.0.20 // indirect 64 | github.com/mitchellh/copystructure v1.2.0 // indirect 65 | github.com/mitchellh/go-homedir v1.1.0 // indirect 66 | github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 // indirect 67 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 68 | github.com/mitchellh/mapstructure v1.5.0 // indirect 69 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 70 | github.com/oklog/run v1.1.0 // indirect 71 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 72 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 73 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 74 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 75 | github.com/zclconf/go-cty v1.17.0 // indirect 76 | github.com/zclconf/go-cty-yaml v1.1.0 // indirect 77 | golang.org/x/crypto v0.45.0 // indirect 78 | golang.org/x/mod v0.30.0 // indirect 79 | golang.org/x/net v0.47.0 // indirect 80 | golang.org/x/sys v0.38.0 // indirect 81 | golang.org/x/text v0.31.0 // indirect 82 | google.golang.org/appengine v1.6.8 // indirect 83 | google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect 84 | google.golang.org/grpc v1.77.0 // indirect 85 | google.golang.org/protobuf v1.36.10 // indirect 86 | gopkg.in/yaml.v3 v3.0.1 // indirect 87 | ) 88 | -------------------------------------------------------------------------------- /nomad/data_source_jwks.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | 10 | "crypto/rsa" 11 | "crypto/x509" 12 | "encoding/base64" 13 | "encoding/pem" 14 | "math/big" 15 | 16 | "github.com/hashicorp/nomad/api" 17 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" 18 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 19 | ) 20 | 21 | func dataSourceJWKS() *schema.Resource { 22 | return &schema.Resource{ 23 | Read: dataSourceJWKSRead, 24 | Schema: map[string]*schema.Schema{ 25 | "keys": { 26 | Description: "JSON Web Key Set (JWKS) public keys for validating workload identity JWTs", 27 | Type: schema.TypeList, 28 | Computed: true, 29 | Elem: &schema.Resource{ 30 | Schema: map[string]*schema.Schema{ 31 | "key_use": { 32 | Type: schema.TypeString, 33 | Computed: true, 34 | }, 35 | "key_type": { 36 | Type: schema.TypeString, 37 | Computed: true, 38 | }, 39 | "key_id": { 40 | Type: schema.TypeString, 41 | Computed: true, 42 | }, 43 | "algorithm": { 44 | Type: schema.TypeString, 45 | Computed: true, 46 | }, 47 | "modulus": { 48 | Type: schema.TypeString, 49 | Computed: true, 50 | }, 51 | "exponent": { 52 | Type: schema.TypeString, 53 | Computed: true, 54 | }, 55 | }, 56 | }, 57 | }, 58 | "pem_keys": { 59 | Description: "JWKS as a list of PEM keys", 60 | Type: schema.TypeList, 61 | Elem: &schema.Schema{Type: schema.TypeString}, 62 | Computed: true, 63 | }, 64 | }, 65 | } 66 | } 67 | 68 | type Key struct { 69 | KeyUse string `json:"use"` 70 | KeyType string `json:"kty"` 71 | KeyId string `json:"kid"` 72 | Algorithm string `json:"alg"` 73 | Modulus string `json:"n"` 74 | Exponent string `json:"e"` 75 | } 76 | 77 | func dataSourceJWKSRead(d *schema.ResourceData, meta any) error { 78 | client := meta.(ProviderConfig).client 79 | operator := client.Raw() 80 | queryOpts := &api.QueryOptions{} 81 | 82 | jwks := struct { 83 | Keys []Key `json:"keys"` 84 | }{} 85 | 86 | log.Printf("[DEBUG] Reading JWKS from Nomad") 87 | _, err := operator.Query("/.well-known/jwks.json", &jwks, queryOpts) 88 | 89 | if err != nil { 90 | return fmt.Errorf("error reading JWKS from Nomad: %s", err) 91 | } 92 | 93 | if len(jwks.Keys) == 0 { 94 | return fmt.Errorf("no keys found") 95 | } 96 | 97 | d.SetId(id.UniqueId()) 98 | if err := d.Set("keys", fromKeys(jwks.Keys)); err != nil { 99 | return fmt.Errorf("error setting JWKS: %#v", err) 100 | } 101 | 102 | pemKeys := make([]string, 0, len(jwks.Keys)) 103 | 104 | for _, key := range jwks.Keys { 105 | pemKey, err := keyToPem(key) 106 | if err != nil { 107 | return fmt.Errorf("Could not encode JWK as PEM: %s", err) 108 | } 109 | pemKeys = append(pemKeys, pemKey) 110 | } 111 | 112 | if err := d.Set("pem_keys", pemKeys); err != nil { 113 | return fmt.Errorf("error setting JWKS pemKeys: %s", err) 114 | } 115 | 116 | return nil 117 | } 118 | 119 | func keyToPem(key Key) (string, error) { 120 | 121 | // Nomad also supports EdDSA keys, but they are not used for OIDC so only 122 | // RSA keys should be listed in this endpoint. 123 | if key.KeyType != "RSA" { 124 | return "", fmt.Errorf("Key type not supported: %s", key.Algorithm) 125 | } 126 | modulus, err := base64.RawURLEncoding.DecodeString(key.Modulus) 127 | 128 | if err != nil { 129 | return "", fmt.Errorf("Could not decode modulus as base64 from JWK: %s", err) 130 | } 131 | 132 | exponent, err := base64.RawURLEncoding.DecodeString(key.Exponent) 133 | 134 | if err != nil { 135 | return "", fmt.Errorf("Could not decode exponent as base64 from JWK: %s", err) 136 | } 137 | 138 | modulusInt := new(big.Int) 139 | modulusInt.SetBytes(modulus) 140 | 141 | exponentInt := new(big.Int) 142 | exponentInt.SetBytes(exponent) 143 | 144 | publicKey := rsa.PublicKey{N: modulusInt, E: int(exponentInt.Uint64())} 145 | 146 | x509Cert, err := x509.MarshalPKIXPublicKey(&publicKey) 147 | 148 | if err != nil { 149 | return "", fmt.Errorf("Could not marshal JWK public key to X509 PKIX: %s", err) 150 | } 151 | 152 | x509CertEncoded := pem.EncodeToMemory( 153 | &pem.Block{ 154 | Type: "PUBLIC KEY", 155 | Bytes: x509Cert, 156 | }) 157 | 158 | return string(x509CertEncoded), nil 159 | } 160 | 161 | func fromKeys(keys []Key) []interface{} { 162 | output := make([]interface{}, 0, len(keys)) 163 | for _, key := range keys { 164 | p := map[string]interface{}{ 165 | "key_use": key.KeyUse, 166 | "key_type": key.KeyType, 167 | "key_id": key.KeyId, 168 | "algorithm": key.Algorithm, 169 | "modulus": key.Modulus, 170 | "exponent": key.Exponent, 171 | } 172 | output = append(output, p) 173 | } 174 | return output 175 | } 176 | -------------------------------------------------------------------------------- /nomad/datasource_nomad_plugin.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "strings" 10 | 11 | "github.com/hashicorp/nomad/api" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 14 | ) 15 | 16 | func dataSourcePlugin() *schema.Resource { 17 | return &schema.Resource{ 18 | Read: dataSourcePluginRead, 19 | Schema: map[string]*schema.Schema{ 20 | "plugin_id": { 21 | Description: "Plugin ID", 22 | Type: schema.TypeString, 23 | Required: true, 24 | }, 25 | "wait_for_registration": { 26 | Description: "Wait for the plugin to be registered in Noamd", 27 | Type: schema.TypeBool, 28 | Optional: true, 29 | Default: false, 30 | }, 31 | "wait_for_healthy": { 32 | Description: "Wait for to be backed by a specified number of controllers", 33 | Type: schema.TypeBool, 34 | Optional: true, 35 | Default: false, 36 | }, 37 | 38 | // computed attributes 39 | "plugin_provider": { 40 | Computed: true, 41 | Type: schema.TypeString, 42 | }, 43 | "plugin_provider_version": { 44 | Computed: true, 45 | Type: schema.TypeString, 46 | }, 47 | "controller_required": { 48 | Computed: true, 49 | Type: schema.TypeBool, 50 | }, 51 | "controllers_expected": { 52 | Computed: true, 53 | Type: schema.TypeInt, 54 | }, 55 | "controllers_healthy": { 56 | Computed: true, 57 | Type: schema.TypeInt, 58 | }, 59 | "nodes_healthy": { 60 | Computed: true, 61 | Type: schema.TypeInt, 62 | }, 63 | "nodes_expected": { 64 | Computed: true, 65 | Type: schema.TypeInt, 66 | }, 67 | "nodes": { 68 | Description: "Available nodes for this plugin", 69 | Computed: true, 70 | Type: schema.TypeList, 71 | Elem: &schema.Resource{ 72 | Schema: map[string]*schema.Schema{ 73 | "name": { 74 | Type: schema.TypeString, 75 | Computed: true, 76 | }, 77 | "healthy": { 78 | Type: schema.TypeBool, 79 | Computed: true, 80 | }, 81 | "healthy_description": { 82 | Type: schema.TypeString, 83 | Computed: true, 84 | }, 85 | }, 86 | }, 87 | }, 88 | }, 89 | } 90 | } 91 | 92 | func dataSourcePluginRead(d *schema.ResourceData, meta interface{}) error { 93 | providerConfig := meta.(ProviderConfig) 94 | client := providerConfig.client 95 | 96 | wait := d.Get("wait_for_registration").(bool) 97 | waitForHealthy := d.Get("wait_for_healthy").(bool) 98 | if wait || waitForHealthy { 99 | resource.Retry(d.Timeout(schema.TimeoutRead), func() *resource.RetryError { 100 | return getPluginInfo(client, d) 101 | }) 102 | } else { 103 | err := getPluginInfo(client, d) 104 | if err != nil { 105 | return err.Err 106 | } 107 | } 108 | 109 | return nil 110 | } 111 | 112 | func getPluginInfo(client *api.Client, d *schema.ResourceData) *resource.RetryError { 113 | id := d.Get("plugin_id").(string) 114 | waitForHealthy := d.Get("wait_for_healthy").(bool) 115 | 116 | log.Printf("[DEBUG] Getting plugin %q...", id) 117 | plugin, _, err := client.CSIPlugins().Info(id, nil) 118 | if err != nil { 119 | // As of Nomad 0.4.1, the API client returns an error for 404 120 | // rather than a nil result, so we must check this way. 121 | log.Printf("[ERROR] error checking for plugin: %#v", err) 122 | if strings.Contains(err.Error(), "404") { 123 | return resource.RetryableError(fmt.Errorf("plugin %q not found", id)) 124 | } 125 | return resource.NonRetryableError(fmt.Errorf("error checking for plugin: %#v", err)) 126 | } 127 | 128 | healthy := plugin.ControllersExpected == plugin.ControllersHealthy && 129 | plugin.NodesExpected == plugin.NodesHealthy 130 | 131 | if waitForHealthy && !healthy { 132 | log.Printf("[DEBUG] plugin %s not yet healthy: %d/%d controllers healthy %d/%d nodes healthy", 133 | id, 134 | plugin.ControllersHealthy, plugin.ControllersExpected, 135 | plugin.NodesHealthy, plugin.NodesExpected) 136 | 137 | return resource.RetryableError(fmt.Errorf("plugin %s not yet healthy", id)) 138 | } 139 | 140 | d.SetId(plugin.ID) 141 | d.Set("plugin_id", plugin.ID) 142 | d.Set("plugin_provider", plugin.Provider) 143 | d.Set("plugin_provider_version", plugin.Version) 144 | d.Set("controller_required", plugin.ControllerRequired) 145 | d.Set("controllers_expected", plugin.ControllersExpected) 146 | d.Set("controllers_healthy", plugin.ControllersHealthy) 147 | d.Set("nodes_expected", plugin.NodesExpected) 148 | d.Set("nodes_healthy", plugin.NodesHealthy) 149 | nodes := make([]map[string]interface{}, 0, len(plugin.Nodes)) 150 | for name, info := range plugin.Nodes { 151 | nodes = append(nodes, map[string]interface{}{ 152 | "name": name, 153 | "healthy": info.Healthy, 154 | "healthy_description": info.HealthDescription, 155 | }) 156 | } 157 | d.Set("nodes", nodes) 158 | return nil 159 | } 160 | -------------------------------------------------------------------------------- /nomad/resource_acl_role_test.go: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016, 2025 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package nomad 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" 13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 14 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 15 | ) 16 | 17 | func TestResourceACLRole(t *testing.T) { 18 | 19 | testResourceName := acctest.RandomWithPrefix("tf-nomad-test") 20 | testACLRoleNameUpdated := testResourceName + "-updated" 21 | 22 | resource.Test(t, resource.TestCase{ 23 | Providers: testProviders, 24 | PreCheck: func() { testAccPreCheck(t); testCheckMinVersion(t, "1.4.0-beta.1") }, 25 | Steps: []resource.TestStep{ 26 | { 27 | Config: testResourceACLRoleConfig(testResourceName, testResourceName), 28 | Check: testResourceACLRoleCheck(testResourceName), 29 | }, 30 | { 31 | Config: testResourceACLRoleConfig(testResourceName, testACLRoleNameUpdated), 32 | Check: testResourceACLRoleCheck(testACLRoleNameUpdated), 33 | }, 34 | }, 35 | CheckDestroy: resourceACLRoleCheckDestroy, 36 | }) 37 | } 38 | 39 | func testResourceACLRoleConfig(policyName, roleName string) string { 40 | return fmt.Sprintf(` 41 | resource "nomad_acl_policy" "test" { 42 | name = %q 43 | description = "A Terraform acctest ACL policy" 44 | rules_hcl = <