Merge branch 'main' into oss-backend-cloudsso-support

This commit is contained in:
Peili Qian 2026-03-26 11:19:37 +08:00 committed by GitHub
commit d4f50e9fb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
421 changed files with 13206 additions and 3666 deletions

View file

@ -1,3 +1,4 @@
- [v1.15](https://github.com/hashicorp/terraform/blob/v1.15/CHANGELOG.md)
- [v1.14](https://github.com/hashicorp/terraform/blob/v1.14/CHANGELOG.md)
- [v1.13](https://github.com/hashicorp/terraform/blob/v1.13/CHANGELOG.md)
- [v1.12](https://github.com/hashicorp/terraform/blob/v1.12/CHANGELOG.md)

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: Fix crash when showing a cloud plan without having a cloud backend
time: 2025-10-09T14:46:45.59398+02:00
custom:
Issue: "37751"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: Allow filesystem functions to return inconsistent results when evaluated within provider configuration
time: 2025-11-03T11:20:34.913068-05:00
custom:
Issue: "37854"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'console and test: return explicit diagnostics when referencing resources that were not included in the most recent operation.'
time: 2025-09-24T11:04:16.860364+02:00
custom:
Issue: "37663"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'query: generate unique resource identifiers for results of expanded list resources'
time: 2025-09-26T11:33:18.241184+02:00
custom:
Issue: "37681"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'test: allow ephemeral outputs in root modules'
time: 2025-10-24T16:44:34.197847+02:00
custom:
Issue: "37813"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: Combinations of replace_triggered_by and -replace could result in some instances not being replaced
time: 2025-10-29T17:59:58.326396-04:00
custom:
Issue: "37833"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'providers lock: include providers required by terraform test'
time: 2025-10-31T14:49:15.121756+01:00
custom:
Issue: "37851"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: Allow filesystem functions to return inconsistent results when evaluated within provider configuration
time: 2025-11-03T11:20:34.913068-05:00
custom:
Issue: "37854"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'query: improve error handling for missing identity schemas'
time: 2025-11-04T12:23:22.096828+01:00
custom:
Issue: "37863"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'actions: make after_create & after_update actions run after the resource has applied'
time: 2025-11-24T15:00:00.316597+01:00
custom:
Issue: "37936"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'stacks: surface runtime issues with local values to user during plan'
time: 2025-12-08T17:02:59.971622+01:00
custom:
Issue: "37980"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: resource instance apply failures should not cause the resource instance state to be empty.
time: 2025-12-09T13:00:50.440436+01:00
custom:
Issue: "37981"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'stacks: change absolute paths in path.module/path.root to be relative, as documented'
time: 2025-12-09T23:00:00.316597+00:00
custom:
Issue: "37982"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: Fixes an issue where any warning diagnostics generated during terraform query execution failed to render in the cloud backend session
time: 2026-01-08T11:45:27.489784-08:00
custom:
Issue: "38040"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'Fixed an issue where terraform stacks validate was failing to resolve relative paths for modules'
time: 2026-01-16T10:12:53.854705-05:00
custom:
Issue: "38025"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: actions in modules without instances failed the plan graph
time: 2026-01-23T10:33:07.244665+01:00
custom:
Issue: "38089"

View file

@ -1,5 +0,0 @@
kind: BUGFIX
body: The CLI now summarizes the number of actions invoked during `terraform apply`, matching the plan output.
time: 2025-09-27T18:41:34.771437+02:00
custom:
Issue: "37689"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: '`terraform stacks` command support for `-help` flag'
time: 2025-09-19T11:52:53.923764-04:00
custom:
Issue: "37645"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: "query: support offline validation of query files via -query flag in the validate command"
time: 2025-09-25T15:12:37.198573+02:00
custom:
Issue: "37671"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: Updates to support the AWS European Sovereign Cloud
time: 2025-10-02T17:26:26.513708-04:00
custom:
Issue: "37721"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: Add component registry source resolution support to Terraform Stacks
time: 2025-12-04T12:58:48.622196-05:00
custom:
Issue: "37888"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'testing: File-level error diagnostics are now included in JUnit XML skipped test elements, ensuring CI/CD pipelines can detect validation failures'
time: 2025-10-24T04:29:00.000000Z
custom:
Issue: "37801"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: A refresh-only plan could result in a non-zero exit code with no changes
time: 2025-11-10T12:09:21.029489-05:00
custom:
Issue: "37406"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'cli: Fixed crash in `terraform show -json` when plan contains ephemeral resources with preconditions or postconditions'
time: 2025-11-12T03:38:30.000000-08:00
custom:
Issue: "37834"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'cli: Fixed `terraform init -json` to properly format all backend configuration messages as JSON instead of plain text'
time: 2025-11-19T10:30:00.000000Z
custom:
Issue: "37911"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: '`state show`: The `state show` command will now explicitly fail and return code 1 when it fails to render the named resources state'
time: 2025-11-21T18:30:45.571448Z
custom:
Issue: "37933"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'apply: Terraform will raise an explicit error if a plan file intended for one workspace is applied against another workspace'
time: 2025-12-01T11:49:50.360928Z
custom:
Issue: "37954"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'lifecycle: `replace_triggered_by` now reports an error when given an invalid attribute reference that does not exist in the target resource'
time: 2025-12-13T12:00:00.000000Z
custom:
Issue: "36740"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'backend: Fix nil pointer dereference crash during `terraform init` when the destination backend returns an error'
time: 2025-12-23T18:45:16.000000Z
custom:
Issue: "38027"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'stacks: send progress events if the plan fails for better UI integration'
time: 2026-01-05T17:06:48.252069+01:00
custom:
Issue: "38039"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'stacks: component instances should report no-op plan/apply. This solves a UI inconsistency with convergence destroy plans '
time: 2026-01-15T10:30:00.72402+01:00
custom:
Issue: "38049"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'backend/http: Return conflicting lock info from HTTP backend instead of the lock that failed to be taken'
time: 2026-02-10T11:39:30.00000-08:00
custom:
Issue: "38144"

View file

@ -1,5 +0,0 @@
kind: BUG FIXES
body: 'states: fixed a bug that caused Terraform to be unable to identify when two states had different output values. This may have caused issues in specific circumstances like backend migrations.'
time: 2026-02-14T12:00:00.000000+00:00
custom:
Issue: "38181"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: 'ssh-based provisioner (file + remote-exec): Re-enable support for PowerShell'
time: 2025-10-22T16:29:09.342697+01:00
custom:
Issue: "37794"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: terraform init log timestamps include millisecond precision
time: 2025-10-27T13:22:38.714891768-05:00
custom:
Issue: "37818"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: "init: skip dependencies declared in development override. This allows you to use `terraform init` with developer overrides and install dependencies that are not declared in the override file."
time: 2025-11-07T14:02:21.847382+01:00
custom:
Issue: "37884"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: "Terraform Test: Allow functions within mock blocks"
time: 2026-01-13T13:04:49.034917+01:00
custom:
Issue: "34672"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: improve detection of deprecated resource attributes / blocks
time: 2026-01-20T17:28:31.861321+01:00
custom:
Issue: "38077"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: Deprecation messages providers set on resources / blocks / attributes are now part of the deprecation warning
time: 2026-02-09T14:21:49.076863+01:00
custom:
Issue: "38135"

View file

@ -1,5 +0,0 @@
kind: ENHANCEMENTS
body: Include which attribute paths are marked as sensitive in list_start JSON logs
time: 2026-02-19T14:33:04.832615-05:00
custom:
Issue: "38197"

View file

@ -1,5 +0,0 @@
kind: NEW FEATURES
body: We now produce builds for Windows ARM64
time: 2025-09-26T16:41:34.771437+02:00
custom:
Issue: "32719"

View file

@ -1,5 +0,0 @@
kind: NEW FEATURES
body: You can set a `deprecated` attribute on variable and output blocks to indicate that they are deprecated. This will produce warnings when passing in a value for a deprecated variable or when referencing a deprecated output.
time: 2025-12-05T17:14:18.623477+01:00
custom:
Issue: "38001"

View file

@ -1,5 +0,0 @@
kind: NEW FEATURES
body: 'backend/s3: Support authentication via `aws login`'
time: 2026-01-08T10:59:19.249387-05:00
custom:
Issue: "37976"

View file

@ -1,5 +0,0 @@
kind: NEW FEATURES
body: "validate: The validate command now checks the `backend` block. This ensures the backend type exists, that all required attributes are present, and that the backend's own validation logic passes."
time: 2026-02-12T10:42:40.333849Z
custom:
Issue: "38021"

View file

@ -1,5 +0,0 @@
kind: NEW FEATURES
body: '`convert` function, which allows for precise inline type conversions'
time: 2026-02-12T18:14:01.356478-05:00
custom:
Issue: "38160"

View file

@ -1,5 +0,0 @@
kind: UPGRADE NOTES
body: 'backend/s3: The `AWS_USE_FIPS_ENDPOINT` and `AWS_USE_DUALSTACK_ENDPOINT` environment variables now only respect `true` or `false` values, aligning with the AWS SDK for Go. This replaces the previous behavior which treated any non-empty value as `true`.'
time: 2026-01-07T15:45:15.958679-05:00
custom:
Issue: "37601"

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: BUSL-1.1
changesDir: .changes
unreleasedDir: v1.15
unreleasedDir: v1.16
versionFooterPath: version_footer.tpl.md
changelogPath: CHANGELOG.md
versionExt: md

View file

@ -31,9 +31,10 @@ label to enable the backport bot.
-->
1.15.x
1.16.x
<!-- heimdall_github_prtemplate:grc-pci_dss-2024-01-05 -->
## Rollback Plan
- [ ] If a change needs to be reverted, we will roll out an update to the code within 7 days.

View file

@ -85,13 +85,13 @@ jobs:
echo "RPM_PACKAGE=$(basename out/*.rpm)" >> $GITHUB_ENV
echo "DEB_PACKAGE=$(basename out/*.deb)" >> $GITHUB_ENV
- if: ${{ inputs.goos == 'linux' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.RPM_PACKAGE }}
path: out/${{ env.RPM_PACKAGE }}
if-no-files-found: error
- if: ${{ inputs.goos == 'linux' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.DEB_PACKAGE }}
path: out/${{ env.DEB_PACKAGE }}

View file

@ -87,7 +87,7 @@ jobs:
version: ${{ needs.get-product-version.outputs.product-version }}
product: ${{ env.PKG_NAME }}
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: metadata.json
path: ${{ steps.generate-metadata-file.outputs.filepath }}
@ -258,7 +258,7 @@ jobs:
fail-on-cache-miss: true
enableCrossOsArchive: true
- name: "Download Terraform CLI package"
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
id: clipkg
with:
name: terraform_${{env.version}}_${{ env.os }}_${{ env.arch }}.zip
@ -269,7 +269,7 @@ jobs:
unzip "${{ needs.e2etest-build.outputs.e2e-cache-path }}/terraform-e2etest_${{ env.os }}_${{ env.arch }}.zip"
unzip "./terraform_${{env.version}}_${{ env.os }}_${{ env.arch }}.zip"
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
if: ${{ contains(matrix.goarch, 'arm') }}
with:
platforms: all
@ -307,7 +307,7 @@ jobs:
with:
go-version: ${{ needs.get-go-version.outputs.go-version }}
- name: Download Terraform CLI package
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
id: clipkg
with:
name: terraform_${{ env.version }}_linux_amd64.zip

View file

@ -35,7 +35,7 @@ jobs:
steps:
- name: "Changed files"
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: changelog
with:
filters: |

View file

@ -12,4 +12,12 @@ binary {
go_modules = true
osv = true
nvd = false
}
triage {
suppress {
vulnerabilities = [
"GHSA-p77j-4mvh-x3m3"
]
}
}
}

View file

@ -1,60 +1,4 @@
## 1.15.0 (Unreleased)
NEW FEATURES:
* We now produce builds for Windows ARM64 ([#32719](https://github.com/hashicorp/terraform/issues/32719))
* You can set a `deprecated` attribute on variable and output blocks to indicate that they are deprecated. This will produce warnings when passing in a value for a deprecated variable or when referencing a deprecated output. ([#38001](https://github.com/hashicorp/terraform/issues/38001))
* backend/s3: Support authentication via `aws login` ([#37967](https://github.com/hashicorp/terraform/issues/37967))
* validate: The validate command now checks the `backend` block. This ensures the backend type exists, that all required attributes are present, and that the backend's own validation logic passes. ([#38021](https://github.com/hashicorp/terraform/issues/38021))
ENHANCEMENTS:
* ssh-based provisioner (file + remote-exec): Re-enable support for PowerShell ([#37794](https://github.com/hashicorp/terraform/issues/37794))
* terraform init log timestamps include millisecond precision ([#37818](https://github.com/hashicorp/terraform/issues/37818))
* init: skip dependencies declared in development override. This allows you to use `terraform init` with developer overrides and install dependencies that are not declared in the override file. ([#37884](https://github.com/hashicorp/terraform/issues/37884))
* Terraform Test: Allow functions within mock blocks ([#34672](https://github.com/hashicorp/terraform/issues/34672))
* improve detection of deprecated resource attributes / blocks ([#38077](https://github.com/hashicorp/terraform/issues/38077))
BUG FIXES:
* testing: File-level error diagnostics are now included in JUnit XML skipped test elements, ensuring CI/CD pipelines can detect validation failures ([#37801](https://github.com/hashicorp/terraform/issues/37801))
* A refresh-only plan could result in a non-zero exit code with no changes ([#37406](https://github.com/hashicorp/terraform/issues/37406))
* cli: Fixed crash in `terraform show -json` when plan contains ephemeral resources with preconditions or postconditions ([#37834](https://github.com/hashicorp/terraform/issues/37834))
* cli: Fixed `terraform init -json` to properly format all backend configuration messages as JSON instead of plain text ([#37911](https://github.com/hashicorp/terraform/issues/37911))
* `state show`: The `state show` command will now explicitly fail and return code 1 when it fails to render the named resources state ([#37933](https://github.com/hashicorp/terraform/issues/37933))
* apply: Terraform will raise an explicit error if a plan file intended for one workspace is applied against another workspace ([#37954](https://github.com/hashicorp/terraform/issues/37954))
* lifecycle: `replace_triggered_by` now reports an error when given an invalid attribute reference that does not exist in the target resource ([#36740](https://github.com/hashicorp/terraform/issues/36740))
* backend: Fix nil pointer dereference crash during `terraform init` when the destination backend returns an error ([#38027](https://github.com/hashicorp/terraform/issues/38027))
* stacks: send progress events if the plan fails for better UI integration ([#38039](https://github.com/hashicorp/terraform/issues/38039))
* stacks: component instances should report no-op plan/apply. This solves a UI inconsistency with convergence destroy plans ([#38049](https://github.com/hashicorp/terraform/issues/38049))
* backend/http: Return conflicting lock info from HTTP backend instead of the lock that failed to be taken ([#38144](https://github.com/hashicorp/terraform/issues/38144))
* states: fixed a bug that caused Terraform to be unable to identify when two states had different output values. This may have caused issues in specific circumstances like backend migrations. ([#38181](https://github.com/hashicorp/terraform/issues/38181))
UPGRADE NOTES:
* backend/s3: The `AWS_USE_FIPS_ENDPOINT` and `AWS_USE_DUALSTACK_ENDPOINT` environment variables now only respect `true` or `false` values, aligning with the AWS SDK for Go. This replaces the previous behavior which treated any non-empty value as `true`. ([#37601](https://github.com/hashicorp/terraform/issues/37601))
## 1.16.0 (Unreleased)
EXPERIMENTS:
@ -71,6 +15,7 @@ Experiments are only enabled in alpha releases of Terraform CLI. The following f
For information on prior major and minor releases, refer to their changelogs:
- [v1.15](https://github.com/hashicorp/terraform/blob/v1.15/CHANGELOG.md)
- [v1.14](https://github.com/hashicorp/terraform/blob/v1.14/CHANGELOG.md)
- [v1.13](https://github.com/hashicorp/terraform/blob/v1.13/CHANGELOG.md)
- [v1.12](https://github.com/hashicorp/terraform/blob/v1.12/CHANGELOG.md)

View file

@ -32,3 +32,7 @@
builtin/provisioners/file @hashicorp/terraform-core
builtin/provisioners/local-exec @hashicorp/terraform-core
builtin/provisioners/remote-exec @hashicorp/terraform-core
# Actions
/internal/command/jsonplan/action_invocations.go @hashicorp/team-tf-actions @hashicorp/terraform-core
/internal/plans/action_invocation.go @hashicorp/team-tf-actions @hashicorp/terraform-core

14
LICENSE
View file

@ -3,22 +3,22 @@ License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
Parameters
Licensor: HashiCorp, Inc.
Licensor: International Business Machines Corporation (IBM)
Licensed Work: Terraform Version 1.6.0 or later. The Licensed Work is (c) 2024
HashiCorp, Inc.
IBM Corp.
Additional Use Grant: You may make production use of the Licensed Work, provided
Your use does not include offering the Licensed Work to third
parties on a hosted or embedded basis in order to compete with
HashiCorp's paid version(s) of the Licensed Work. For purposes
IBM Corp.'s paid version(s) of the Licensed Work. For purposes
of this license:
A "competitive offering" is a Product that is offered to third
parties on a paid basis, including through paid support
arrangements, that significantly overlaps with the capabilities
of HashiCorp's paid version(s) of the Licensed Work. If Your
of IBM Corp.'s paid version(s) of the Licensed Work. If Your
Product is not a competitive offering when You first make it
generally available, it will not become a competitive offering
later due to HashiCorp releasing a new version of the Licensed
later due to IBM Corp. releasing a new version of the Licensed
Work with additional capabilities. In addition, Products that
are not provided on a paid basis are not competitive.
@ -34,10 +34,10 @@ Additional Use Grant: You may make production use of the Licensed Work, provided
Hosting or using the Licensed Work(s) for internal purposes
within an organization is not considered a competitive
offering. HashiCorp considers your organization to include all
offering. IBM Corp. considers your organization to include all
of your affiliates under common control.
For binding interpretive guidance on using HashiCorp products
For binding interpretive guidance on using IBM Corp. products
under the Business Source License, please visit our FAQ.
(https://www.hashicorp.com/license-faq)
Change Date: Four years from the date the Licensed Work is published.

2
go.mod
View file

@ -63,7 +63,7 @@ require (
github.com/spf13/afero v1.15.0
github.com/xanzy/ssh-agent v0.3.3
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940
github.com/zclconf/go-cty-yaml v1.1.0
go.opentelemetry.io/contrib/exporters/autoexport v0.45.0

4
go.sum
View file

@ -750,8 +750,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -0,0 +1,23 @@
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package backendrun
import (
"context"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// ConstVariableSupplier is an optional interface that backends can implement
// to supply variable values from their remote storage. This is used to fetch
// const variable values that are needed during early configuration loading
// (e.g., for module source resolution), before a full operation is started.
type ConstVariableSupplier interface {
// FetchVariables retrieves Terraform variable values stored in the
// backend for the given workspace. Only variables that are relevant to
// Terraform (as opposed to environment variables or other categories)
// should be returned.
FetchVariables(ctx context.Context, workspace string) (map[string]arguments.UnparsedVariableValue, tfdiags.Diagnostics)
}

View file

@ -14,7 +14,6 @@ import (
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/configs/configload"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/plans"
@ -175,15 +174,6 @@ func (o *Operation) HasConfig() bool {
return o.ConfigLoader.IsConfigDir(o.ConfigDir)
}
// Config loads the configuration that the operation applies to, using the
// ConfigDir and ConfigLoader fields within the receiving operation.
func (o *Operation) Config() (*configs.Config, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
config, hclDiags := o.ConfigLoader.LoadConfig(o.ConfigDir)
diags = diags.Append(hclDiags)
return config, diags
}
// ReportResult is a helper for the common chore of setting the status of
// a running operation and showing any diagnostics produced during that
// operation.

View file

@ -147,6 +147,19 @@ func isDefinedAny(name string, maps ...terraform.InputValues) bool {
// that were successfully processed, allowing for careful analysis of the
// partial result.
func ParseVariableValues(vv map[string]arguments.UnparsedVariableValue, decls map[string]*configs.Variable) (terraform.InputValues, tfdiags.Diagnostics) {
return parseVariableValues(vv, decls, false)
}
// ParseConstVariableValues is like ParseVariableValues but only produces
// errors for missing const variables. Non-const required variables that are
// missing will still receive placeholder values but won't produce errors.
// This is used during early configuration loading (e.g. module installation)
// where only const variables are needed for module source resolution.
func ParseConstVariableValues(vv map[string]arguments.UnparsedVariableValue, decls map[string]*configs.Variable) (terraform.InputValues, tfdiags.Diagnostics) {
return parseVariableValues(vv, decls, true)
}
func parseVariableValues(vv map[string]arguments.UnparsedVariableValue, decls map[string]*configs.Variable, constOnly bool) (terraform.InputValues, tfdiags.Diagnostics) {
ret, diags := ParseDeclaredVariableValues(vv, decls)
undeclared, diagsUndeclared := ParseUndeclaredVariableValues(vv, decls)
@ -166,14 +179,20 @@ func ParseVariableValues(vv map[string]arguments.UnparsedVariableValue, decls ma
// specific error message which mentions -var and -var-file command
// line options, whereas the one in Terraform Core is more general
// due to supporting both root and child module variables.
if vc.Required() {
shouldError := vc.Required()
if constOnly {
shouldError = vc.Const && vc.Required()
}
if shouldError {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "No value for required variable",
Detail: fmt.Sprintf("The root module input variable %q is not set, and has no default value. Use a -var or -var-file command line argument to provide a value for this variable.", name),
Subject: vc.DeclRange.Ptr(),
})
}
if vc.Required() {
// We'll include a placeholder value anyway, just so that our
// result is complete for any calling code that wants to cautiously
// analyze it for diagnostic purposes. Since our diagnostics now
@ -200,3 +219,18 @@ func ParseVariableValues(vv map[string]arguments.UnparsedVariableValue, decls ma
return ret, diags
}
// HasUnsatisfiedConstVariables checks whether any const variables declared in
// the given module are required but not yet present in the provided variable
// values map. This is used to determine whether we need to fetch additional
// variable values from a backend before loading the full configuration.
func HasUnsatisfiedConstVariables(vv map[string]arguments.UnparsedVariableValue, decls map[string]*configs.Variable) bool {
for name, vc := range decls {
if vc.Const && vc.Required() {
if _, defined := vv[name]; !defined {
return true
}
}
}
return false
}

View file

@ -221,6 +221,196 @@ func TestUnparsedValue(t *testing.T) {
t.Errorf("wrong result\n%s", diff)
}
})
t.Run("ParseVariableValues constOnly", func(t *testing.T) {
vv := map[string]arguments.UnparsedVariableValue{
"declared1": testUnparsedVariableValue("5"),
}
decls := map[string]*configs.Variable{
"declared1": {
Name: "declared1",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseLiteral,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"missing_const_required": {
Name: "missing_const_required",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseLiteral,
Const: true,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 3, Column: 1, Byte: 0},
End: hcl.Pos{Line: 3, Column: 1, Byte: 0},
},
},
"missing_nonconst_required": {
Name: "missing_nonconst_required",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseLiteral,
Const: false,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 4, Column: 1, Byte: 0},
End: hcl.Pos{Line: 4, Column: 1, Byte: 0},
},
},
"missing_const_optional": {
Name: "missing_const_optional",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseLiteral,
Const: true,
Default: cty.StringVal("default"),
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 5, Column: 1, Byte: 0},
End: hcl.Pos{Line: 5, Column: 1, Byte: 0},
},
},
}
gotVals, diags := ParseConstVariableValues(vv, decls)
if got, want := len(diags), 1; got != want {
t.Fatalf("wrong number of diagnostics %d; want %d", got, want)
}
const missingRequired = `No value for required variable`
if got, want := diags[0].Description().Summary, missingRequired; got != want {
t.Fatalf("wrong summary for diagnostic 0\ngot: %s\nwant: %s", got, want)
}
if got, want := diags[0].Description().Detail, `"missing_const_required"`; !strings.Contains(got, want) {
t.Fatalf("wrong detail for diagnostic 0\ngot: %s\nmust contain: %s", got, want)
}
wantVals := terraform.InputValues{
"declared1": {
Value: cty.StringVal("5"),
SourceType: terraform.ValueFromNamedFile,
SourceRange: tfdiags.SourceRange{
Filename: "fake.tfvars",
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
},
},
"missing_const_required": {
Value: cty.DynamicVal,
SourceType: terraform.ValueFromConfig,
SourceRange: tfdiags.SourceRange{
Filename: "fake.tf",
Start: tfdiags.SourcePos{Line: 3, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 3, Column: 1, Byte: 0},
},
},
"missing_nonconst_required": {
Value: cty.DynamicVal,
SourceType: terraform.ValueFromConfig,
SourceRange: tfdiags.SourceRange{
Filename: "fake.tf",
Start: tfdiags.SourcePos{Line: 4, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 4, Column: 1, Byte: 0},
},
},
"missing_const_optional": {
Value: cty.NilVal,
SourceType: terraform.ValueFromConfig,
SourceRange: tfdiags.SourceRange{
Filename: "fake.tf",
Start: tfdiags.SourcePos{Line: 5, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 5, Column: 1, Byte: 0},
},
},
}
if diff := cmp.Diff(wantVals, gotVals, cmp.Comparer(cty.Value.RawEquals)); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
})
}
func TestHasUnsatisfiedConstVariables(t *testing.T) {
testCases := map[string]struct {
vv map[string]arguments.UnparsedVariableValue
decls map[string]*configs.Variable
want bool
}{
"no variables": {
vv: nil,
decls: map[string]*configs.Variable{},
want: false,
},
"no const variables": {
vv: nil,
decls: map[string]*configs.Variable{
"regular": {
Name: "regular",
},
},
want: false,
},
"const with default": {
vv: nil,
decls: map[string]*configs.Variable{
"has_default": {
Name: "has_default",
Const: true,
Default: cty.StringVal("default"),
},
},
want: false,
},
"const required and missing": {
vv: nil,
decls: map[string]*configs.Variable{
"required_const": {
Name: "required_const",
Const: true,
},
},
want: true,
},
"const required but provided": {
vv: map[string]arguments.UnparsedVariableValue{
"required_const": testUnparsedVariableValue("value"),
},
decls: map[string]*configs.Variable{
"required_const": {
Name: "required_const",
Const: true,
},
},
want: false,
},
"non-const required and missing": {
vv: nil,
decls: map[string]*configs.Variable{
"regular_required": {
Name: "regular_required",
},
},
want: false,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
got := HasUnsatisfiedConstVariables(tc.vv, tc.decls)
if got != tc.want {
t.Errorf("got %v, want %v", got, tc.want)
}
})
}
}
type testUnparsedVariableValue string

View file

@ -93,6 +93,13 @@ func Backend(name string) backend.InitFn {
return backends[name]
}
func BackendExists(name string) bool {
backendsLock.Lock()
defer backendsLock.Unlock()
_, ok := backends[name]
return ok
}
// Set sets a new backend in the list of backends. If f is nil then the
// backend will be removed from the map. If this backend already exists
// then it will be overwritten.

View file

@ -21,12 +21,12 @@ import (
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
"github.com/hashicorp/terraform/internal/tfdiags"
)
@ -364,7 +364,7 @@ func (s failingState) WriteState(state *states.State) error {
func testOperationApply(t *testing.T, configDir string) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
streams, done := terminal.StreamsForTesting(t)
view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams))

View file

@ -146,39 +146,11 @@ func (b *Local) localRun(op *backendrun.Operation) (*backendrun.LocalRun, *confi
func (b *Local) localRunDirect(op *backendrun.Operation, run *backendrun.LocalRun, coreOpts *terraform.ContextOpts, s statemgr.Full) (*backendrun.LocalRun, *configload.Snapshot, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
// Load the configuration using the caller-provided configuration loader.
config, configSnap, configDiags := op.ConfigLoader.LoadConfigWithSnapshot(op.ConfigDir)
rootMod, configDiags := op.ConfigLoader.LoadRootModule(op.ConfigDir)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, nil, diags
}
run.Config = config
if errs := config.VerifyDependencySelections(op.DependencyLocks); len(errs) > 0 {
var buf strings.Builder
for _, err := range errs {
fmt.Fprintf(&buf, "\n - %s", err.Error())
}
var suggestion string
switch {
case op.DependencyLocks == nil:
// If we get here then it suggests that there's a caller that we
// didn't yet update to populate DependencyLocks, which is a bug.
suggestion = "This run has no dependency lock information provided at all, which is a bug in Terraform; please report it!"
case op.DependencyLocks.Empty():
suggestion = "To make the initial dependency selections that will initialize the dependency lock file, run:\n terraform init"
default:
suggestion = "To update the locked dependency selections to match a changed configuration, run:\n terraform init -upgrade"
}
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Inconsistent dependency lock file",
fmt.Sprintf(
"The following dependency selections recorded in the lock file are inconsistent with the current configuration:%s\n\n%s",
buf.String(), suggestion,
),
))
}
var rawVariables map[string]arguments.UnparsedVariableValue
if op.AllowUnsetVariables {
@ -186,16 +158,16 @@ func (b *Local) localRunDirect(op *backendrun.Operation, run *backendrun.LocalRu
// but unset variables with unknown values to represent that they are
// placeholders for values the user would need to provide for other
// operations.
rawVariables = b.stubUnsetRequiredVariables(op.Variables, config.Module.Variables)
rawVariables = b.stubUnsetRequiredVariables(op.Variables, rootMod.Variables)
} else {
// If interactive input is enabled, we might gather some more variable
// values through interactive prompts.
// TODO: Need to route the operation context through into here, so that
// the interactive prompts can be sensitive to its timeouts/etc.
rawVariables = b.interactiveCollectVariables(context.TODO(), op.Variables, config.Module.Variables, op.UIIn)
rawVariables = b.interactiveCollectVariables(context.TODO(), op.Variables, rootMod.Variables, op.UIIn)
}
variables, varDiags := backendrun.ParseVariableValues(rawVariables, config.Module.Variables)
variables, varDiags := backendrun.ParseVariableValues(rawVariables, rootMod.Variables)
diags = diags.Append(varDiags)
if diags.HasErrors() {
return nil, nil, diags
@ -224,6 +196,52 @@ func (b *Local) localRunDirect(op *backendrun.Operation, run *backendrun.LocalRu
return nil, nil, diags
}
run.Core = tfCtx
walkerSnapshot, configSnap := op.ConfigLoader.ModuleWalkerSnapshot()
config, buildDiags := terraform.BuildConfigWithGraph(
rootMod,
walkerSnapshot,
variables,
configs.MockDataLoaderFunc(op.ConfigLoader.LoadExternalMockData),
)
diags = diags.Append(buildDiags)
if buildDiags.HasErrors() {
return nil, nil, diags
}
run.Config = config
snapDiags := op.ConfigLoader.AddRootModuleToSnapshot(configSnap, op.ConfigDir)
diags = diags.Append(snapDiags)
if snapDiags.HasErrors() {
return nil, nil, diags
}
if errs := config.VerifyDependencySelections(op.DependencyLocks); len(errs) > 0 {
var buf strings.Builder
for _, err := range errs {
fmt.Fprintf(&buf, "\n - %s", err.Error())
}
var suggestion string
switch {
case op.DependencyLocks == nil:
// If we get here then it suggests that there's a caller that we
// didn't yet update to populate DependencyLocks, which is a bug.
suggestion = "This run has no dependency lock information provided at all, which is a bug in Terraform; please report it!"
case op.DependencyLocks.Empty():
suggestion = "To make the initial dependency selections that will initialize the dependency lock file, run:\n terraform init"
default:
suggestion = "To update the locked dependency selections to match a changed configuration, run:\n terraform init -upgrade"
}
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Inconsistent dependency lock file",
fmt.Sprintf(
"The following dependency selections recorded in the lock file are inconsistent with the current configuration:%s\n\n%s",
buf.String(), suggestion,
),
))
}
return run, configSnap, diags
}
@ -235,6 +253,7 @@ func (b *Local) localRunForPlanFile(op *backendrun.Operation, pf *planfile.Reade
// A plan file has a snapshot of configuration embedded inside it, which
// is used instead of whatever configuration might be already present
// in the filesystem.
//TODO why not use pf.ReadConfig?
snap, err := pf.ReadConfigSnapshot()
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
@ -246,32 +265,16 @@ func (b *Local) localRunForPlanFile(op *backendrun.Operation, pf *planfile.Reade
}
loader := configload.NewLoaderFromSnapshot(snap)
loader.AllowLanguageExperiments(op.ConfigLoader.AllowsLanguageExperiments())
config, configDiags := loader.LoadConfig(snap.Modules[""].Dir)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, snap, diags
rootMod, rootDiags := loader.LoadRootModule(snap.Modules[""].Dir)
diags = diags.Append(rootDiags)
if rootDiags.HasErrors() {
return nil, nil, diags
}
run.Config = config
// NOTE: We're intentionally comparing the current locks with the
// configuration snapshot, rather than the lock snapshot in the plan file,
// because it's the current locks which dictate our plugin selections
// in coreOpts below. However, we'll also separately check that the
// plan file has identical locked plugins below, and thus we're effectively
// checking consistency with both here.
if errs := config.VerifyDependencySelections(op.DependencyLocks); len(errs) > 0 {
var buf strings.Builder
for _, err := range errs {
fmt.Fprintf(&buf, "\n - %s", err.Error())
}
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Inconsistent dependency lock file",
fmt.Sprintf(
"The following dependency selections recorded in the lock file are inconsistent with the configuration in the saved plan:%s\n\nA saved plan can be applied only to the same configuration it was created from. Create a new plan from the updated configuration.",
buf.String(),
),
))
variables, varDiags := backendrun.ParseVariableValues(op.Variables, rootMod.Variables)
diags = diags.Append(varDiags)
if diags.HasErrors() {
return nil, nil, diags
}
// This check is an important complement to the check above: the locked
@ -359,6 +362,40 @@ func (b *Local) localRunForPlanFile(op *backendrun.Operation, pf *planfile.Reade
return nil, nil, diags
}
run.Core = tfCtx
config, buildDiags := terraform.BuildConfigWithGraph(
rootMod,
loader.ModuleWalker(),
variables,
configs.MockDataLoaderFunc(loader.LoadExternalMockData),
)
diags = diags.Append(buildDiags)
if buildDiags.HasErrors() {
return nil, nil, diags
}
run.Config = config
// NOTE: We're intentionally comparing the current locks with the
// configuration snapshot, rather than the lock snapshot in the plan file,
// because it's the current locks which dictate our plugin selections
// in coreOpts below. However, we'll also separately check that the
// plan file has identical locked plugins below, and thus we're effectively
// checking consistency with both here.
if errs := config.VerifyDependencySelections(op.DependencyLocks); len(errs) > 0 {
var buf strings.Builder
for _, err := range errs {
fmt.Fprintf(&buf, "\n - %s", err.Error())
}
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Inconsistent dependency lock file",
fmt.Sprintf(
"The following dependency selections recorded in the lock file are inconsistent with the configuration in the saved plan:%s\n\nA saved plan can be applied only to the same configuration it was created from. Create a new plan from the updated configuration.",
buf.String(),
),
))
}
return run, snap, diags
}
@ -459,8 +496,8 @@ func (b *Local) interactiveCollectVariables(ctx context.Context, existing map[st
func (b *Local) stubUnsetRequiredVariables(existing map[string]arguments.UnparsedVariableValue, vcs map[string]*configs.Variable) map[string]arguments.UnparsedVariableValue {
var missing bool // Do we need to add anything?
for name, vc := range vcs {
if !vc.Required() {
continue // We only stub required variables
if !vc.Required() || vc.Const {
continue // We only stub non-const required variables
}
if _, exists := existing[name]; !exists {
missing = true
@ -475,7 +512,7 @@ func (b *Local) stubUnsetRequiredVariables(existing map[string]arguments.Unparse
maps.Copy(ret, existing) // don't use clone here, so we can return a non-nil map
for name, vc := range vcs {
if !vc.Required() {
if !vc.Required() || vc.Const {
continue
}
if _, exists := existing[name]; !exists {

View file

@ -19,7 +19,6 @@ import (
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs/configload"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/plans/planfile"
"github.com/hashicorp/terraform/internal/schemarepo"
@ -27,6 +26,7 @@ import (
"github.com/hashicorp/terraform/internal/states/statefile"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
"github.com/hashicorp/terraform/internal/tfdiags"
)
@ -34,7 +34,7 @@ func TestLocalRun(t *testing.T) {
configDir := "./testdata/empty"
b := TestLocal(t)
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
streams, _ := terminal.StreamsForTesting(t)
@ -65,7 +65,7 @@ func TestLocalRun_error(t *testing.T) {
// should then cause LocalRun to return with the state unlocked.
b.Backend = backendWithStateStorageThatFailsRefresh{}
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
streams, _ := terminal.StreamsForTesting(t)
@ -92,7 +92,7 @@ func TestLocalRun_cloudPlan(t *testing.T) {
configDir := "./testdata/apply"
b := TestLocal(t)
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
planPath := "./testdata/plan-bookmark/bookmark.json"
@ -127,7 +127,7 @@ func TestLocalRun_stalePlan(t *testing.T) {
configDir := "./testdata/apply"
b := TestLocal(t)
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
// Write an empty state file with serial 3

View file

@ -20,13 +20,13 @@ import (
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/plans/planfile"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
)
func TestLocal_planBasic(t *testing.T) {
@ -726,7 +726,7 @@ func TestLocal_planOutPathNoChange(t *testing.T) {
func testOperationPlan(t *testing.T, configDir string) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
streams, done := terminal.StreamsForTesting(t)
view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams))

View file

@ -16,11 +16,11 @@ import (
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
"github.com/zclconf/go-cty/cty"
)
@ -267,7 +267,7 @@ func TestLocal_refreshEmptyState(t *testing.T) {
func testOperationRefresh(t *testing.T, configDir string) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
streams, done := terminal.StreamsForTesting(t)
view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams))

View file

@ -9,7 +9,7 @@ require (
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000
github.com/jackofallops/giovanni v0.28.0
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
)
require (

View file

@ -219,8 +219,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -6,7 +6,7 @@ require (
github.com/hashicorp/consul/api v1.32.1
github.com/hashicorp/consul/sdk v0.16.1
github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
)
require (

View file

@ -288,8 +288,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -43,7 +43,7 @@ require (
github.com/mozillazg/go-httpheader v0.3.0 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/zclconf/go-cty v1.16.3 // indirect
github.com/zclconf/go-cty v1.18.0 // indirect
github.com/zclconf/go-cty-yaml v1.1.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/mod v0.30.0 // indirect

View file

@ -179,8 +179,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -7,7 +7,7 @@ require (
cloud.google.com/go/storage v1.30.1
github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000
github.com/mitchellh/go-homedir v1.1.0
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
golang.org/x/oauth2 v0.30.0
google.golang.org/api v0.155.0
google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3

View file

@ -194,8 +194,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -5,7 +5,7 @@ go 1.25.7
require (
github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000
github.com/mitchellh/go-homedir v1.1.0
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
k8s.io/api v0.33.0
k8s.io/apimachinery v0.33.0
k8s.io/client-go v0.33.0

View file

@ -218,8 +218,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -8,7 +8,7 @@ require (
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000
github.com/oracle/oci-go-sdk/v65 v65.89.1
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
)
require (

View file

@ -168,8 +168,8 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -50,7 +50,7 @@ require (
github.com/satori/go.uuid v1.2.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/zclconf/go-cty v1.16.3 // indirect
github.com/zclconf/go-cty v1.18.0 // indirect
github.com/zclconf/go-cty-yaml v1.1.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/mod v0.30.0 // indirect

View file

@ -198,8 +198,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -7,7 +7,7 @@ require (
github.com/hashicorp/hcl/v2 v2.24.0
github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000
github.com/lib/pq v1.10.3
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
)
require (

View file

@ -152,8 +152,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -15,7 +15,7 @@ require (
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/hcl/v2 v2.24.0
github.com/hashicorp/terraform v0.0.0-00010101000000-000000000000
github.com/zclconf/go-cty v1.16.3
github.com/zclconf/go-cty v1.18.0
)
require (

View file

@ -175,8 +175,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.18.0 h1:pJ8+HNI4gFoyRNqVE37wWbJWVw43BZczFo7KUoRczaA=
github.com/zclconf/go-cty v1.18.0/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=

View file

@ -25,12 +25,12 @@ import (
"github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/plans/planfile"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
tfversion "github.com/hashicorp/terraform/version"
)
@ -43,7 +43,7 @@ func testOperationApply(t *testing.T, configDir string) (*backendrun.Operation,
func testOperationApplyWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
streams, done := terminal.StreamsForTesting(t)
view := views.NewView(streams)

View file

@ -249,11 +249,8 @@ func (b *Remote) waitForRun(stopCtx, cancelCtx context.Context, op *backendrun.O
// remote system's responsibility to do final validation of the input.
func (b *Remote) hasExplicitVariableValues(op *backendrun.Operation) bool {
// Load the configuration using the caller-provided configuration loader.
config, _, configDiags := op.ConfigLoader.LoadConfigWithSnapshot(op.ConfigDir)
config, configDiags := op.ConfigLoader.LoadRootModule(op.ConfigDir)
if configDiags.HasErrors() {
// If we can't load the configuration then we'll assume no explicit
// variable values just to let the remote operation start and let
// the remote system return the same set of configuration errors.
return false
}
@ -262,7 +259,7 @@ func (b *Remote) hasExplicitVariableValues(op *backendrun.Operation) bool {
// goal here is just to make a best effort count of how many variable
// values are coming from -var or -var-file CLI arguments so that we can
// hint the user that those are not supported for remote operations.
variables, _ := backendrun.ParseVariableValues(op.Variables, config.Module.Variables)
variables, _ := backendrun.ParseVariableValues(op.Variables, config.Variables)
// Check for explicitly-defined (-var and -var-file) variables, which the
// remote backend does not support. All other source types are okay,

View file

@ -81,19 +81,18 @@ func (b *Remote) LocalRun(op *backendrun.Operation) (*backendrun.LocalRun, state
ret.InputState = stateMgr.State()
log.Printf("[TRACE] backend/remote: loading configuration for the current working directory")
config, configDiags := op.ConfigLoader.LoadConfig(op.ConfigDir)
rootMod, configDiags := op.ConfigLoader.LoadRootModule(op.ConfigDir)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, nil, diags
}
ret.Config = config
if op.AllowUnsetVariables {
// If we're not going to use the variables in an operation we'll be
// more lax about them, stubbing out any unset ones as unknown.
// This gives us enough information to produce a consistent context,
// but not enough information to run a real operation (plan, apply, etc)
ret.PlanOpts.SetVariables = stubAllVariables(op.Variables, config.Module.Variables)
ret.PlanOpts.SetVariables = stubAllVariables(op.Variables, rootMod.Variables)
} else {
// The underlying API expects us to use the opaque workspace id to request
// variables, so we'll need to look that up using our organization name
@ -136,7 +135,7 @@ func (b *Remote) LocalRun(op *backendrun.Operation) (*backendrun.LocalRun, state
}
if op.Variables != nil {
variables, varDiags := backendrun.ParseVariableValues(op.Variables, config.Module.Variables)
variables, varDiags := backendrun.ParseVariableValues(op.Variables, rootMod.Variables)
diags = diags.Append(varDiags)
if diags.HasErrors() {
return nil, nil, diags
@ -148,6 +147,24 @@ func (b *Remote) LocalRun(op *backendrun.Operation) (*backendrun.LocalRun, state
tfCtx, ctxDiags := terraform.NewContext(&opts)
diags = diags.Append(ctxDiags)
ret.Core = tfCtx
if diags.HasErrors() {
return nil, nil, diags
}
log.Printf("[TRACE] backend/remote: building configuration for the current working directory")
config, buildDiags := terraform.BuildConfigWithGraph(
rootMod,
op.ConfigLoader.ModuleWalker(),
ret.PlanOpts.SetVariables,
configs.MockDataLoaderFunc(op.ConfigLoader.LoadExternalMockData),
)
diags = diags.Append(buildDiags)
if diags.HasErrors() {
return nil, nil, diags
}
ret.Config = config
log.Printf("[TRACE] backend/remote: finished building terraform.Context")

View file

@ -17,10 +17,10 @@ import (
"github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
"github.com/hashicorp/terraform/internal/tfdiags"
)
@ -187,7 +187,7 @@ func TestRemoteContextWithVars(t *testing.T) {
b, bCleanup := testBackendDefault(t)
defer bCleanup()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
workspaceID, err := b.getRemoteWorkspaceID(context.Background(), backend.DefaultStateName)
@ -410,7 +410,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
b, bCleanup := testBackendDefault(t)
defer bCleanup()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
workspaceID, err := b.getRemoteWorkspaceID(context.Background(), backend.DefaultStateName)

View file

@ -24,12 +24,12 @@ import (
"github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/plans/planfile"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
)
func testOperationPlan(t *testing.T, configDir string) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
@ -41,7 +41,7 @@ func testOperationPlan(t *testing.T, configDir string) (*backendrun.Operation, f
func testOperationPlanWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
streams, done := terminal.StreamsForTesting(t)
view := views.NewView(streams)

View file

@ -87,6 +87,27 @@ func NewState(config *configs.Config) *State {
}
}
// RegisterModule registers all checkable objects declared in the given module
// configuration that are not already known to this State.
//
// This supports incremental config discovery, such as during init walks where
// child modules are loaded step by step rather than all at once.
func (c *State) RegisterModule(cfg *configs.Config) {
c.mu.Lock()
defer c.mu.Unlock()
// Collect statuses for the new module (non-recursively — children will
// register themselves when they are loaded).
fresh := addrs.MakeMap[addrs.ConfigCheckable, *configCheckableState]()
collectInitialStatuses(fresh, cfg)
for _, elem := range fresh.Elems {
if !c.statuses.Has(elem.Key) {
c.statuses.Put(elem.Key, elem.Value)
}
}
}
// ConfigHasChecks returns true if and only if the given address refers to
// a configuration object that this State object is expecting to recieve
// statuses for.

View file

@ -1,38 +1,23 @@
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package checks
package checks_test
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configload"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/checks"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
)
func TestChecksHappyPath(t *testing.T) {
const fixtureDir = "testdata/happypath"
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := initwd.NewModuleInstaller(loader.ModulesDir(), loader, nil)
_, instDiags := inst.InstallModules(context.Background(), fixtureDir, "tests", true, false, initwd.ModuleInstallHooksImpl{})
if instDiags.HasErrors() {
t.Fatal(instDiags.Err())
}
if err := loader.RefreshModules(); err != nil {
t.Fatalf("failed to refresh modules after installation: %s", err)
}
/////////////////////////////////////////////////////////////////////////
cfg, hclDiags := loader.LoadConfig(fixtureDir)
if hclDiags.HasErrors() {
t.Fatalf("invalid configuration: %s", hclDiags.Error())
}
cfg, _, configCleanup := tftesting.MustLoadConfigForTests(t, fixtureDir, "tests")
t.Cleanup(configCleanup)
resourceA := addrs.Resource{
Mode: addrs.ManagedResourceMode,
@ -90,36 +75,36 @@ func TestChecksHappyPath(t *testing.T) {
/////////////////////////////////////////////////////////////////////////
checks := NewState(cfg)
state := checks.NewState(cfg)
missing := 0
if addr := resourceA; !checks.ConfigHasChecks(addr) {
if addr := resourceA; !state.ConfigHasChecks(addr) {
t.Errorf("checks not detected for %s", addr)
missing++
}
if addr := resourceB; !checks.ConfigHasChecks(addr) {
if addr := resourceB; !state.ConfigHasChecks(addr) {
t.Errorf("checks not detected for %s", addr)
missing++
}
if addr := resourceC; !checks.ConfigHasChecks(addr) {
if addr := resourceC; !state.ConfigHasChecks(addr) {
t.Errorf("checks not detected for %s", addr)
missing++
}
if addr := rootOutput; !checks.ConfigHasChecks(addr) {
if addr := rootOutput; !state.ConfigHasChecks(addr) {
t.Errorf("checks not detected for %s", addr)
missing++
}
if addr := childOutput; !checks.ConfigHasChecks(addr) {
if addr := childOutput; !state.ConfigHasChecks(addr) {
t.Errorf("checks not detected for %s", addr)
missing++
}
if addr := resourceNoChecks; checks.ConfigHasChecks(addr) {
if addr := resourceNoChecks; state.ConfigHasChecks(addr) {
t.Errorf("checks detected for %s, even though it has none", addr)
}
if addr := resourceNonExist; checks.ConfigHasChecks(addr) {
if addr := resourceNonExist; state.ConfigHasChecks(addr) {
t.Errorf("checks detected for %s, even though it doesn't exist", addr)
}
if addr := checkBlock; !checks.ConfigHasChecks(addr) {
if addr := checkBlock; !state.ConfigHasChecks(addr) {
t.Errorf("checks not detected for %s", addr)
missing++
}
@ -140,13 +125,13 @@ func TestChecksHappyPath(t *testing.T) {
childOutput,
checkBlock,
)
gotConfigAddrs := checks.AllConfigAddrs()
gotConfigAddrs := state.AllConfigAddrs()
if diff := cmp.Diff(wantConfigAddrs, gotConfigAddrs); diff != "" {
t.Errorf("wrong detected config addresses\n%s", diff)
}
for _, configAddr := range gotConfigAddrs {
if got, want := checks.AggregateCheckStatus(configAddr), StatusUnknown; got != want {
if got, want := state.AggregateCheckStatus(configAddr), checks.StatusUnknown; got != want {
t.Errorf("incorrect initial aggregate check status for %s: %s, but want %s", configAddr, got, want)
}
}
@ -170,26 +155,26 @@ func TestChecksHappyPath(t *testing.T) {
childOutputInst := childOutput.OutputValue.Absolute(moduleChildInst)
checkBlockInst := checkBlock.Check.Absolute(addrs.RootModuleInstance)
checks.ReportCheckableObjects(resourceA, addrs.MakeSet[addrs.Checkable](resourceInstA))
checks.ReportCheckResult(resourceInstA, addrs.ResourcePrecondition, 0, StatusPass)
checks.ReportCheckResult(resourceInstA, addrs.ResourcePrecondition, 1, StatusPass)
checks.ReportCheckResult(resourceInstA, addrs.ResourcePostcondition, 0, StatusPass)
state.ReportCheckableObjects(resourceA, addrs.MakeSet[addrs.Checkable](resourceInstA))
state.ReportCheckResult(resourceInstA, addrs.ResourcePrecondition, 0, checks.StatusPass)
state.ReportCheckResult(resourceInstA, addrs.ResourcePrecondition, 1, checks.StatusPass)
state.ReportCheckResult(resourceInstA, addrs.ResourcePostcondition, 0, checks.StatusPass)
checks.ReportCheckableObjects(resourceB, addrs.MakeSet[addrs.Checkable](resourceInstB))
checks.ReportCheckResult(resourceInstB, addrs.ResourcePrecondition, 0, StatusPass)
state.ReportCheckableObjects(resourceB, addrs.MakeSet[addrs.Checkable](resourceInstB))
state.ReportCheckResult(resourceInstB, addrs.ResourcePrecondition, 0, checks.StatusPass)
checks.ReportCheckableObjects(resourceC, addrs.MakeSet[addrs.Checkable](resourceInstC0, resourceInstC1))
checks.ReportCheckResult(resourceInstC0, addrs.ResourcePostcondition, 0, StatusPass)
checks.ReportCheckResult(resourceInstC1, addrs.ResourcePostcondition, 0, StatusPass)
state.ReportCheckableObjects(resourceC, addrs.MakeSet[addrs.Checkable](resourceInstC0, resourceInstC1))
state.ReportCheckResult(resourceInstC0, addrs.ResourcePostcondition, 0, checks.StatusPass)
state.ReportCheckResult(resourceInstC1, addrs.ResourcePostcondition, 0, checks.StatusPass)
checks.ReportCheckableObjects(childOutput, addrs.MakeSet[addrs.Checkable](childOutputInst))
checks.ReportCheckResult(childOutputInst, addrs.OutputPrecondition, 0, StatusPass)
state.ReportCheckableObjects(childOutput, addrs.MakeSet[addrs.Checkable](childOutputInst))
state.ReportCheckResult(childOutputInst, addrs.OutputPrecondition, 0, checks.StatusPass)
checks.ReportCheckableObjects(rootOutput, addrs.MakeSet[addrs.Checkable](rootOutputInst))
checks.ReportCheckResult(rootOutputInst, addrs.OutputPrecondition, 0, StatusPass)
state.ReportCheckableObjects(rootOutput, addrs.MakeSet[addrs.Checkable](rootOutputInst))
state.ReportCheckResult(rootOutputInst, addrs.OutputPrecondition, 0, checks.StatusPass)
checks.ReportCheckableObjects(checkBlock, addrs.MakeSet[addrs.Checkable](checkBlockInst))
checks.ReportCheckResult(checkBlockInst, addrs.CheckAssertion, 0, StatusPass)
state.ReportCheckableObjects(checkBlock, addrs.MakeSet[addrs.Checkable](checkBlockInst))
state.ReportCheckResult(checkBlockInst, addrs.CheckAssertion, 0, checks.StatusPass)
/////////////////////////////////////////////////////////////////////////
@ -198,9 +183,9 @@ func TestChecksHappyPath(t *testing.T) {
{
configCount := 0
for _, configAddr := range checks.AllConfigAddrs() {
for _, configAddr := range state.AllConfigAddrs() {
configCount++
if got, want := checks.AggregateCheckStatus(configAddr), StatusPass; got != want {
if got, want := state.AggregateCheckStatus(configAddr), checks.StatusPass; got != want {
t.Errorf("incorrect final aggregate check status for %s: %s, but want %s", configAddr, got, want)
}
}
@ -220,7 +205,7 @@ func TestChecksHappyPath(t *testing.T) {
checkBlockInst,
)
for _, addr := range objAddrs {
if got, want := checks.ObjectCheckStatus(addr), StatusPass; got != want {
if got, want := state.ObjectCheckStatus(addr), checks.StatusPass; got != want {
t.Errorf("incorrect final check status for object %s: %s, but want %s", addr, got, want)
}
}

View file

@ -123,6 +123,7 @@ type Cloud struct {
var _ backend.Backend = (*Cloud)(nil)
var _ backendrun.OperationsBackend = (*Cloud)(nil)
var _ backendrun.Local = (*Cloud)(nil)
var _ backendrun.ConstVariableSupplier = (*Cloud)(nil)
// New creates a new initialized cloud backend.
func New(services *disco.Disco) *Cloud {

View file

@ -28,12 +28,12 @@ import (
"github.com/hashicorp/terraform/internal/command/jsonformat"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/plans/planfile"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
tfversion "github.com/hashicorp/terraform/version"
)
@ -46,7 +46,7 @@ func testOperationApply(t *testing.T, configDir string) (*backendrun.Operation,
func testOperationApplyWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
streams, done := terminal.StreamsForTesting(t)
view := views.NewView(streams)

View file

@ -654,12 +654,12 @@ in order to capture the filesystem context the remote workspace expects:
}
func (b *Cloud) parseRunVariables(op *backendrun.Operation) ([]*tfe.RunVariable, error) {
config, _, configDiags := op.ConfigLoader.LoadConfigWithSnapshot(op.ConfigDir)
config, configDiags := op.ConfigLoader.LoadRootModule(op.ConfigDir)
if configDiags.HasErrors() {
return nil, fmt.Errorf("error loading config with snapshot: %w", configDiags.Errs()[0])
}
variables, varDiags := ParseCloudRunVariables(op.Variables, config.Module.Variables)
variables, varDiags := ParseCloudRunVariables(op.Variables, config.Variables)
if varDiags.HasErrors() {
return nil, varDiags.Err()

View file

@ -79,75 +79,70 @@ func (b *Cloud) LocalRun(op *backendrun.Operation) (*backendrun.LocalRun, statem
log.Printf("[TRACE] cloud: retrieving remote state snapshot for workspace %q", remoteWorkspaceName)
ret.InputState = stateMgr.State()
log.Printf("[TRACE] cloud: loading configuration for the current working directory")
config, configDiags := op.ConfigLoader.LoadConfig(op.ConfigDir)
log.Printf("[TRACE] cloud: loading root module for the current working directory")
rootMod, configDiags := op.ConfigLoader.LoadRootModule(op.ConfigDir)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, nil, diags
}
ret.Config = config
if op.AllowUnsetVariables {
// If we're not going to use the variables in an operation we'll be
// more lax about them, stubbing out any unset ones as unknown.
// This gives us enough information to produce a consistent context,
// but not enough information to run a real operation (plan, apply, etc)
ret.PlanOpts.SetVariables = stubAllVariables(op.Variables, config.Module.Variables)
} else {
// The underlying API expects us to use the opaque workspace id to request
// variables, so we'll need to look that up using our organization name
// and workspace name.
remoteWorkspaceID, err := b.getRemoteWorkspaceID(context.Background(), op.Workspace)
if err != nil {
diags = diags.Append(fmt.Errorf("error finding remote workspace: %w", err))
return nil, nil, diags
// If we're not going to use the variables in an operation we'll be
// more lax about them, stubbing out any unset ones as unknown.
// This gives us enough information to produce a consistent context,
// but not enough information to run a real operation (plan, apply, etc).
//
// However, const variables must always be resolved since they're
// needed during early configuration loading (e.g. module sources).
// We fetch backend variables so const vars can be satisfied.
fetchedVars, fetchDiags := b.FetchVariables(context.Background(), op.Workspace)
diags = diags.Append(fetchDiags)
if fetchDiags.HasErrors() {
return nil, nil, diags
}
if len(fetchedVars) > 0 {
if op.Variables == nil {
op.Variables = make(map[string]arguments.UnparsedVariableValue)
}
w, err := b.fetchWorkspace(context.Background(), b.Organization, op.Workspace)
if err != nil {
diags = diags.Append(fmt.Errorf("error loading workspace: %w", err))
return nil, nil, diags
}
if isLocalExecutionMode(w.ExecutionMode) {
log.Printf("[TRACE] skipping retrieving variables from workspace %s/%s (%s), workspace is in Local Execution mode", remoteWorkspaceName, b.Organization, remoteWorkspaceID)
} else {
log.Printf("[TRACE] cloud: retrieving variables from workspace %s/%s (%s)", remoteWorkspaceName, b.Organization, remoteWorkspaceID)
tfeVariables, err := b.client.Variables.ListAll(context.Background(), remoteWorkspaceID, nil)
if err != nil && err != tfe.ErrResourceNotFound {
diags = diags.Append(fmt.Errorf("error loading variables: %w", err))
return nil, nil, diags
for k, v := range fetchedVars {
if _, ok := op.Variables[k]; !ok {
op.Variables[k] = v
}
if tfeVariables != nil {
if op.Variables == nil {
op.Variables = make(map[string]arguments.UnparsedVariableValue)
}
for _, v := range tfeVariables.Items {
if v.Category == tfe.CategoryTerraform {
if _, ok := op.Variables[v.Key]; !ok {
op.Variables[v.Key] = &remoteStoredVariableValue{
definition: v,
}
}
}
}
}
}
if op.Variables != nil {
variables, varDiags := backendrun.ParseVariableValues(op.Variables, config.Module.Variables)
diags = diags.Append(varDiags)
if diags.HasErrors() {
return nil, nil, diags
}
ret.PlanOpts.SetVariables = variables
}
}
var variables terraform.InputValues
var varDiags tfdiags.Diagnostics
if op.AllowUnsetVariables {
variables, varDiags = backendrun.ParseConstVariableValues(op.Variables, rootMod.Variables)
} else {
variables, varDiags = backendrun.ParseVariableValues(op.Variables, rootMod.Variables)
}
diags = diags.Append(varDiags)
if diags.HasErrors() {
return nil, nil, diags
}
ret.PlanOpts.SetVariables = variables
tfCtx, ctxDiags := terraform.NewContext(&opts)
diags = diags.Append(ctxDiags)
ret.Core = tfCtx
if diags.HasErrors() {
return nil, nil, diags
}
log.Printf("[TRACE] cloud: building configuration for the current working directory")
config, buildDiags := terraform.BuildConfigWithGraph(
rootMod,
op.ConfigLoader.ModuleWalker(),
ret.PlanOpts.SetVariables,
configs.MockDataLoaderFunc(op.ConfigLoader.LoadExternalMockData),
)
diags = diags.Append(buildDiags)
if diags.HasErrors() {
return nil, nil, diags
}
ret.Config = config
log.Printf("[TRACE] cloud: finished building terraform.Context")
@ -185,31 +180,47 @@ func (b *Cloud) getRemoteWorkspaceID(ctx context.Context, localWorkspaceName str
return remoteWorkspace.ID, nil
}
func stubAllVariables(vv map[string]arguments.UnparsedVariableValue, decls map[string]*configs.Variable) terraform.InputValues {
ret := make(terraform.InputValues, len(decls))
// FetchVariables implements backendrun.ConstVariableSupplier by retrieving
// Terraform variables from the HCP Terraform or Terraform Enterprise workspace.
func (b *Cloud) FetchVariables(ctx context.Context, workspace string) (map[string]arguments.UnparsedVariableValue, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
for name, cfg := range decls {
raw, exists := vv[name]
if !exists {
ret[name] = &terraform.InputValue{
Value: cty.UnknownVal(cfg.Type),
SourceType: terraform.ValueFromConfig,
}
continue
}
val, diags := raw.ParseVariableValue(cfg.ParsingMode)
if diags.HasErrors() {
ret[name] = &terraform.InputValue{
Value: cty.UnknownVal(cfg.Type),
SourceType: terraform.ValueFromConfig,
}
continue
}
ret[name] = val
remoteWorkspaceID, err := b.getRemoteWorkspaceID(ctx, workspace)
if err != nil {
diags = diags.Append(fmt.Errorf("error finding remote workspace: %w", err))
return nil, diags
}
return ret
w, err := b.fetchWorkspace(ctx, b.Organization, workspace)
if err != nil {
diags = diags.Append(fmt.Errorf("error loading workspace: %w", err))
return nil, diags
}
if isLocalExecutionMode(w.ExecutionMode) {
log.Printf("[TRACE] cloud: skipping variable fetch for workspace %s/%s (%s), workspace is in Local Execution mode", b.getRemoteWorkspaceName(workspace), b.Organization, remoteWorkspaceID)
return nil, nil
}
log.Printf("[TRACE] cloud: retrieving variables from workspace %s/%s (%s)", b.getRemoteWorkspaceName(workspace), b.Organization, remoteWorkspaceID)
tfeVariables, err := b.client.Variables.ListAll(ctx, remoteWorkspaceID, nil)
if err != nil && err != tfe.ErrResourceNotFound {
diags = diags.Append(fmt.Errorf("error loading variables: %w", err))
return nil, diags
}
result := make(map[string]arguments.UnparsedVariableValue)
if tfeVariables != nil {
for _, v := range tfeVariables.Items {
if v.Category == tfe.CategoryTerraform {
result[v.Key] = &remoteStoredVariableValue{
definition: v,
}
}
}
}
return result, nil
}
// remoteStoredVariableValue is a backendrun.UnparsedVariableValue implementation

View file

@ -16,10 +16,10 @@ import (
"github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
tftesting "github.com/hashicorp/terraform/internal/terraform/testing"
"github.com/hashicorp/terraform/internal/tfdiags"
)
@ -186,7 +186,7 @@ func TestRemoteContextWithVars(t *testing.T) {
b, bCleanup := testBackendWithName(t)
defer bCleanup()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
workspaceID, err := b.getRemoteWorkspaceID(context.Background(), testBackendSingleWorkspaceName)
@ -409,7 +409,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
b, bCleanup := testBackendWithName(t)
defer bCleanup()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
_, configLoader, configCleanup := tftesting.MustLoadConfigForTests(t, configDir, "tests")
defer configCleanup()
workspaceID, err := b.getRemoteWorkspaceID(context.Background(), testBackendSingleWorkspaceName)

Some files were not shown because too many files have changed in this diff Show more