mirror of
https://github.com/hashicorp/terraform.git
synced 2026-06-08 16:35:25 -04:00
Merge branch 'main' into oss-backend-cloudsso-support
This commit is contained in:
commit
d4f50e9fb8
421 changed files with 13206 additions and 3666 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
|
|||
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
|
|
@ -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.
|
||||
|
|
|
|||
4
.github/workflows/build-terraform-cli.yml
vendored
4
.github/workflows/build-terraform-cli.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
.github/workflows/changelog.yml
vendored
2
.github/workflows/changelog.yml
vendored
|
|
@ -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: |
|
||||
|
|
|
|||
|
|
@ -12,4 +12,12 @@ binary {
|
|||
go_modules = true
|
||||
osv = true
|
||||
nvd = false
|
||||
}
|
||||
|
||||
triage {
|
||||
suppress {
|
||||
vulnerabilities = [
|
||||
"GHSA-p77j-4mvh-x3m3"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
CHANGELOG.md
59
CHANGELOG.md
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
14
LICENSE
|
|
@ -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
2
go.mod
|
|
@ -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
4
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
23
internal/backend/backendrun/const_variables.go
Normal file
23
internal/backend/backendrun/const_variables.go
Normal 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)
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue