net.xml
default
EOF
sudo virsh net-destroy default
sudo virsh net-undefine default
sudo virsh net-create net.xml
- name: acceptance-test
env:
TERRAFORM_LIBVIRT_TEST_DOMAIN_TYPE: qemu
LIBVIRT_DEFAULT_URI: qemu:///system
CI: "true"
run: |
make testacc
================================================
FILE: .github/workflows/pull-request.yaml
================================================
name: check-dirty
on:
pull_request:
jobs:
check-dirty:
if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/'))
runs-on:
group: generic
env:
GOTOOLCHAIN: local
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- name: Go vulnerability check
run: make go-vulncheck
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: false
- name: dirty-check
run: |
make check-dirty
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
================================================
FILE: .github/workflows/release.yml
================================================
# This GitHub action can publish assets for release when a tag is created.
# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0).
#
# This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your
# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE`
# secret. If you would rather own your own GPG handling, please fork this action
# or use an alternative one for key handling.
#
# You will need to pass the `--batch` flag to `gpg` in your signing step
# in `goreleaser` to indicate this is being used in a non-interactive mode.
#
name: release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
goreleaser:
runs-on:
group: generic
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Unshallow
run: git fetch --prune --unshallow
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v6
id: import_gpg
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.PASSPHRASE }}
- name: release-notes
run: make release-notes
- name: Generate SBOM
run: make sbom
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --clean --release-notes=_out/RELEASE_NOTES.md
env:
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
# GitHub sets this automatically
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/slack-notify.yaml
================================================
name: slack-notify
"on":
workflow_run:
workflows:
- check-dirty
- acceptance-tests
- release
types:
- completed
jobs:
slack-notify:
runs-on:
group: generic
if: github.event.workflow_run.conclusion != 'skipped'
steps:
- name: Get PR number
id: get-pr-number
if: github.event.workflow_run.event == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
run: |
echo pull_request_number=$(gh pr view -R ${{ github.repository }} ${{ github.event.workflow_run.head_repository.owner.login }}:${{ github.event.workflow_run.head_branch }} --json number --jq .number) >> $GITHUB_OUTPUT
- name: Slack Notify
uses: slackapi/slack-github-action@v2
with:
method: chat.postMessage
payload: |
{
"channel": "proj-talos-maintainers",
"attachments": [
{
"color": "${{ github.event.workflow_run.conclusion == 'success' && '#2EB886' || github.event.workflow_run.conclusion == 'failure' && '#A30002' || '#FFCC00' }}",
"fallback": "test",
"blocks": [
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "${{ github.event.workflow_run.event == 'pull_request' && format('*Pull Request:* {0} (`{1}`)\n<{2}/pull/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, steps.get-pr-number.outputs.pull_request_number, github.event.workflow_run.display_title) || format('*Build:* {0} (`{1}`)\n<{2}/commit/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, github.sha, github.event.workflow_run.display_title) }}"
},
{
"type": "mrkdwn",
"text": "*Status:*\n`${{ github.event.workflow_run.conclusion }}`"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Author:*\n`${{ github.actor }}`"
},
{
"type": "mrkdwn",
"text": "*Event:*\n`${{ github.event.workflow_run.event }}`"
}
]
},
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Logs"
},
"url": "${{ github.event.workflow_run.html_url }}"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Commit"
},
"url": "${{ github.event.repository.html_url }}/commit/${{ github.sha }}"
}
]
}
]
}
]
}
token: ${{ secrets.SLACK_BOT_TOKEN }}
================================================
FILE: .gitignore
================================================
dist
terraform-provider-talos
_out
### Terraform ###
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
#*.tfvars
#*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
================================================
FILE: .golangci.yml
================================================
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2025-07-11T15:25:51Z by kres 9b39fa4b-dirty.
version: "2"
# options for analysis running
run:
modules-download-mode: readonly
issues-exit-code: 1
tests: true
# output configuration options
output:
formats:
text:
path: stdout
print-issued-lines: true
print-linter-name: true
path-prefix: ""
linters:
default: all
disable:
- exhaustruct
- err113
- forbidigo
- funcorder
- funlen
- gochecknoglobals
- gochecknoinits
- godox
- gomoddirectives
- gosec
- inamedparam
- ireturn
- mnd
- nestif
- nonamedreturns
- paralleltest
- tagalign
- tagliatelle
- thelper
- varnamelen
- wrapcheck
- testifylint # complains about our assert recorder and has a number of false positives for assert.Greater(t, thing, 1)
- protogetter # complains about us using Value field on typed spec, instead of GetValue which has a different signature
- perfsprint # complains about us using fmt.Sprintf in non-performance critical code, updating just kres took too long
- musttag # seems to be broken - goes into imported libraries and reports issues there
- nolintlint # gives false positives - disable until https://github.com/golangci/golangci-lint/issues/3228 is resolved
- wsl # replaced by wsl_v5
- noinlineerr
- embeddedstructfieldcheck # fighting in many places with fieldalignment
# all available settings of specific linters
settings:
cyclop:
# the maximal code complexity to report
max-complexity: 20
dogsled:
max-blank-identifiers: 2
dupl:
threshold: 150
errcheck:
check-type-assertions: true
check-blank: true
exhaustive:
default-signifies-exhaustive: false
gocognit:
min-complexity: 30
nestif:
min-complexity: 5
goconst:
min-len: 3
min-occurrences: 3
gocritic:
disabled-checks: [ ]
gocyclo:
min-complexity: 20
godot:
scope: declarations
gomodguard: { }
govet:
enable-all: true
lll:
line-length: 200
tab-width: 4
misspell:
locale: US
nakedret:
max-func-lines: 30
prealloc:
simple: true
range-loops: true # Report preallocation suggestions on range loops, true by default
for-loops: false # Report preallocation suggestions on for loops, false by default
revive:
rules:
- name: var-naming # Complains about package names like "common"
disabled: true
rowserrcheck: { }
testpackage: { }
unparam:
check-exported: false
unused:
local-variables-are-used: false
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
wsl:
strict-append: true
allow-assign-and-call: true
allow-multiline-assign: true
allow-trailing-comment: false
force-case-trailing-whitespace: 0
allow-separated-leading-comment: false
allow-cuddle-declarations: false
force-err-cuddling: false
depguard:
rules:
prevent_unmaintained_packages:
list-mode: lax # allow unless explicitly denied
files:
- $all
deny:
- pkg: io/ioutil
desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil"
test_kres_depguard_extra_rule_1:
deny:
- desc: Test rule 1
pkg: io/ioutil
files:
- test_1.go
list-mode: lax
test_kres_depguard_extra_rule_2:
deny:
- desc: Test rule 2
pkg: io/ioutil
files:
- test_2.go
list-mode: lax
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
issues:
max-issues-per-linter: 10
max-same-issues: 3
uniq-by-line: true
new: false
severity:
default: error
formatters:
enable:
- gci
- gofmt
- gofumpt
settings:
gci:
sections:
- standard
- default
- localmodule
gofmt:
simplify: true
gofumpt:
extra-rules: false
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
================================================
FILE: .goreleaser.yml
================================================
# Visit https://goreleaser.com for documentation on how to customize this
# behavior.
version: 2
before:
hooks:
# this is just an example and not a requirement for provider building/publishing
- go mod tidy
builds:
- env:
# goreleaser does not work with CGO, it could also complicate
# usage by users in CI/CD systems like Terraform Cloud where
# they are unable to install libraries.
- CGO_ENABLED=0
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
ldflags:
- '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
goos:
- freebsd
- windows
- linux
- darwin
goarch:
- amd64
- '386'
- arm
- arm64
ignore:
- goos: darwin
goarch: '386'
- goos: windows
goarch: arm
binary: '{{ .ProjectName }}_v{{ .Version }}'
archives:
- formats: ['zip']
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
checksum:
extra_files:
- glob: 'terraform-registry-manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
algorithm: sha256
signs:
- artifacts: checksum
args:
# if you are using this in a GitHub action or some other automated pipeline, you
# need to pass the batch flag to indicate its not interactive.
- "--batch"
- "--local-user"
- "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key
- "--output"
- "${signature}"
- "--detach-sign"
- "${artifact}"
release:
extra_files:
- glob: 'terraform-registry-manifest.json'
name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
- glob: '_out/sbom.*.json'
# If you want to manually examine the release before its live, uncomment this line:
# draft: true
changelog:
disable: false
================================================
FILE: .vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Talos Terraform Provider",
"type": "go",
"request": "launch",
"mode": "debug",
// this assumes your workspace is the root of the repo
"program": "${workspaceFolder}",
"env": {},
"args": [
"-debug",
]
}
]
}
================================================
FILE: CHANGELOG.md
================================================
## [terraform-provider-talos 0.11.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.11.0) (2026-04-27)
Welcome to the v0.11.0 release of terraform-provider-talos!
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### ephemeral resources
New ephemeral resources are added, please see docs.
### Component Updates
Talos sdk: v1.13.0
### Contributors
* Noel Georgi
* Mickaël Canévet
* Nikita COEUR
* Dennis Witt
* Nahue
* purajit
### Changes
20 commits
* [`1c68732`](https://github.com/siderolabs/terraform-provider-talos/commit/1c68732ad71c09fbb0c425bafc26002348daa9ed) release(v0.11.0): prepare release
* [`b5affe3`](https://github.com/siderolabs/terraform-provider-talos/commit/b5affe3b5259e9ca69492090a9a632203f34ad34) fix(docs): update talos_version description
* [`ce4ede8`](https://github.com/siderolabs/terraform-provider-talos/commit/ce4ede85cc3d8a6cc5285c86f5a98b464666cc9c) fix: handle unknown-whole-list config_patches on machine_configuration_apply
* [`000a3ec`](https://github.com/siderolabs/terraform-provider-talos/commit/000a3ec9946024587d4389e8c2a4463c6c53cf83) fix(ephemeral): detect machine_configuration_input_wo changes via hash
* [`032a5ac`](https://github.com/siderolabs/terraform-provider-talos/commit/032a5ac6c75bbcd6e2d0f099bbbf500651d224a7) chore: bump deps
* [`7b6a3e4`](https://github.com/siderolabs/terraform-provider-talos/commit/7b6a3e408457faa6c54b951791d6a8b3a7146d9c) fix: prevent empty resolved_apply_mode when reusing state
* [`f427339`](https://github.com/siderolabs/terraform-provider-talos/commit/f4273395931496053d5e5c70b7c78b3f9c7bf652) release(v0.11.0-beta.2): prepare release
* [`6ed1274`](https://github.com/siderolabs/terraform-provider-talos/commit/6ed1274cd7276b3f46465e2c7cc712ccb651c99f) feat: add sboms to release
* [`4729352`](https://github.com/siderolabs/terraform-provider-talos/commit/4729352f7228f1f5454b0999798b5443ab15685e) feat(ephemeral): rewrite talos_cluster_kubeconfig to generate from machine_secrets
* [`4992c4a`](https://github.com/siderolabs/terraform-provider-talos/commit/4992c4af6d23f3e0faf7fa9d58c5ccc46810ad2d) fix(ephemeral): rewrite talos_client_configuration to generate from machine_secrets
* [`4c8def5`](https://github.com/siderolabs/terraform-provider-talos/commit/4c8def55f9ae57e5fefcaef83aab6e7a90557c1d) release(v0.11.0-beta.1): prepare release
* [`01330a9`](https://github.com/siderolabs/terraform-provider-talos/commit/01330a9d19a2a27ed79fd78c6cd1623012f61d09) chore: update goreleaser schema
* [`1585aed`](https://github.com/siderolabs/terraform-provider-talos/commit/1585aed51fcde7e158d45afa0be78f084693d396) fix: remove unsupported windows/arm build target for Go 1.25
* [`d7bb719`](https://github.com/siderolabs/terraform-provider-talos/commit/d7bb7199c1a212431339d634b905c3b9b21d0a3f) release(v0.11.0-beta.0): prepare release
* [`09f6c83`](https://github.com/siderolabs/terraform-provider-talos/commit/09f6c838e7430de02f62a1b968f425e0b2be4e0d) fix: restore correct skip_kubernetes_checks behavior
* [`eaedcfd`](https://github.com/siderolabs/terraform-provider-talos/commit/eaedcfdda3e7e3193e3d23231673ef676f575ae5) chore: bump deps
* [`f4d673f`](https://github.com/siderolabs/terraform-provider-talos/commit/f4d673f0851f25c5e15ef8ac9bd6fc12310ab2d5) feat: add ephemeral resources to prevent secrets from leaking to state
* [`5f07e0f`](https://github.com/siderolabs/terraform-provider-talos/commit/5f07e0f6043687b2dc475aeb16d489968a2dd2c5) feat: add staged_if_needing_reboot apply mode for automatic reboot prevention
* [`c8e3b87`](https://github.com/siderolabs/terraform-provider-talos/commit/c8e3b87002418699bf33a2b437f7ff59aa6c70ad) feat: add exact_filters attribute to talos_image_factory_extensions_versions
* [`efe146e`](https://github.com/siderolabs/terraform-provider-talos/commit/efe146e512fb22dd1012849fce098256115961a4) fix: gracefully handle Unknown config_patches values
### Changes since v0.11.0-beta.2
6 commits
* [`1c68732`](https://github.com/siderolabs/terraform-provider-talos/commit/1c68732ad71c09fbb0c425bafc26002348daa9ed) release(v0.11.0): prepare release
* [`b5affe3`](https://github.com/siderolabs/terraform-provider-talos/commit/b5affe3b5259e9ca69492090a9a632203f34ad34) fix(docs): update talos_version description
* [`ce4ede8`](https://github.com/siderolabs/terraform-provider-talos/commit/ce4ede85cc3d8a6cc5285c86f5a98b464666cc9c) fix: handle unknown-whole-list config_patches on machine_configuration_apply
* [`000a3ec`](https://github.com/siderolabs/terraform-provider-talos/commit/000a3ec9946024587d4389e8c2a4463c6c53cf83) fix(ephemeral): detect machine_configuration_input_wo changes via hash
* [`032a5ac`](https://github.com/siderolabs/terraform-provider-talos/commit/032a5ac6c75bbcd6e2d0f099bbbf500651d224a7) chore: bump deps
* [`7b6a3e4`](https://github.com/siderolabs/terraform-provider-talos/commit/7b6a3e408457faa6c54b951791d6a8b3a7146d9c) fix: prevent empty resolved_apply_mode when reusing state
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-framework** v1.17.0 -> v1.19.0
* **github.com/hashicorp/terraform-plugin-go** v0.29.0 -> v0.31.0
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.38.1 -> v2.40.0
* **github.com/hashicorp/terraform-plugin-testing** v1.14.0 -> v1.15.0
* **github.com/siderolabs/crypto** v0.6.4 -> v0.6.5
* **github.com/siderolabs/image-factory** v0.9.0 -> v1.1.0
* **github.com/siderolabs/talos** v1.12.0 -> v1.13.0
* **github.com/siderolabs/talos/pkg/machinery** v1.12.0 -> v1.13.0
* **go.yaml.in/yaml/v4** v4.0.0-rc.3 -> v4.0.0-rc.4
* **golang.org/x/crypto** v0.50.0 **_new_**
* **golang.org/x/mod** v0.31.0 -> v0.35.0
* **k8s.io/client-go** v0.35.0 -> v0.35.4
Previous release can be found at [v0.10.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.10.1)
## [terraform-provider-talos 0.11.0-beta.2](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.11.0-beta.2) (2026-03-31)
Welcome to the v0.11.0-beta.2 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### ephemeral resources
New ephemeral resources are added, please see docs.
### Component Updates
Talos sdk: v1.13.0-beta.1
### Contributors
* Mickaël Canévet
* Noel Georgi
* Dennis Witt
* Nikita COEUR
* purajit
### Changes
13 commits
* [`6ed1274`](https://github.com/siderolabs/terraform-provider-talos/commit/6ed1274cd7276b3f46465e2c7cc712ccb651c99f) feat: add sboms to release
* [`4729352`](https://github.com/siderolabs/terraform-provider-talos/commit/4729352f7228f1f5454b0999798b5443ab15685e) feat(ephemeral): rewrite talos_cluster_kubeconfig to generate from machine_secrets
* [`4992c4a`](https://github.com/siderolabs/terraform-provider-talos/commit/4992c4af6d23f3e0faf7fa9d58c5ccc46810ad2d) fix(ephemeral): rewrite talos_client_configuration to generate from machine_secrets
* [`4c8def5`](https://github.com/siderolabs/terraform-provider-talos/commit/4c8def55f9ae57e5fefcaef83aab6e7a90557c1d) release(v0.11.0-beta.1): prepare release
* [`01330a9`](https://github.com/siderolabs/terraform-provider-talos/commit/01330a9d19a2a27ed79fd78c6cd1623012f61d09) chore: update goreleaser schema
* [`1585aed`](https://github.com/siderolabs/terraform-provider-talos/commit/1585aed51fcde7e158d45afa0be78f084693d396) fix: remove unsupported windows/arm build target for Go 1.25
* [`d7bb719`](https://github.com/siderolabs/terraform-provider-talos/commit/d7bb7199c1a212431339d634b905c3b9b21d0a3f) release(v0.11.0-beta.0): prepare release
* [`09f6c83`](https://github.com/siderolabs/terraform-provider-talos/commit/09f6c838e7430de02f62a1b968f425e0b2be4e0d) fix: restore correct skip_kubernetes_checks behavior
* [`eaedcfd`](https://github.com/siderolabs/terraform-provider-talos/commit/eaedcfdda3e7e3193e3d23231673ef676f575ae5) chore: bump deps
* [`f4d673f`](https://github.com/siderolabs/terraform-provider-talos/commit/f4d673f0851f25c5e15ef8ac9bd6fc12310ab2d5) feat: add ephemeral resources to prevent secrets from leaking to state
* [`5f07e0f`](https://github.com/siderolabs/terraform-provider-talos/commit/5f07e0f6043687b2dc475aeb16d489968a2dd2c5) feat: add staged_if_needing_reboot apply mode for automatic reboot prevention
* [`c8e3b87`](https://github.com/siderolabs/terraform-provider-talos/commit/c8e3b87002418699bf33a2b437f7ff59aa6c70ad) feat: add exact_filters attribute to talos_image_factory_extensions_versions
* [`efe146e`](https://github.com/siderolabs/terraform-provider-talos/commit/efe146e512fb22dd1012849fce098256115961a4) fix: gracefully handle Unknown config_patches values
### Changes since v0.11.0-beta.1
3 commits
* [`6ed1274`](https://github.com/siderolabs/terraform-provider-talos/commit/6ed1274cd7276b3f46465e2c7cc712ccb651c99f) feat: add sboms to release
* [`4729352`](https://github.com/siderolabs/terraform-provider-talos/commit/4729352f7228f1f5454b0999798b5443ab15685e) feat(ephemeral): rewrite talos_cluster_kubeconfig to generate from machine_secrets
* [`4992c4a`](https://github.com/siderolabs/terraform-provider-talos/commit/4992c4af6d23f3e0faf7fa9d58c5ccc46810ad2d) fix(ephemeral): rewrite talos_client_configuration to generate from machine_secrets
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-framework** v1.17.0 -> v1.19.0
* **github.com/hashicorp/terraform-plugin-go** v0.29.0 -> v0.31.0
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.38.1 -> v2.40.0
* **github.com/hashicorp/terraform-plugin-testing** v1.14.0 -> v1.15.0
* **github.com/siderolabs/image-factory** v0.9.0 -> v1.0.3
* **github.com/siderolabs/talos** v1.12.0 -> v1.13.0-beta.1
* **github.com/siderolabs/talos/pkg/machinery** v1.12.0 -> v1.13.0-beta.1
* **go.yaml.in/yaml/v4** v4.0.0-rc.3 -> v4.0.0-rc.4
* **golang.org/x/crypto** v0.49.0 **_new_**
* **golang.org/x/mod** v0.31.0 -> v0.34.0
* **k8s.io/client-go** v0.35.0 -> v0.35.3
Previous release can be found at [v0.10.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.10.1)
## [terraform-provider-talos 0.11.0-beta.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.11.0-beta.1) (2026-02-27)
Welcome to the v0.11.0-beta.1 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### ephemeral resources
New ephemeral resources are added, please see docs.
### Component Updates
Talos sdk: v1.13.0-alpha.1
### Contributors
* Mickaël Canévet
* Noel Georgi
* Dennis Witt
* Nikita COEUR
* purajit
### Changes
9 commits
* [`01330a9`](https://github.com/siderolabs/terraform-provider-talos/commit/01330a9d19a2a27ed79fd78c6cd1623012f61d09) chore: update goreleaser schema
* [`1585aed`](https://github.com/siderolabs/terraform-provider-talos/commit/1585aed51fcde7e158d45afa0be78f084693d396) fix: remove unsupported windows/arm build target for Go 1.25
* [`d7bb719`](https://github.com/siderolabs/terraform-provider-talos/commit/d7bb7199c1a212431339d634b905c3b9b21d0a3f) release(v0.11.0-beta.0): prepare release
* [`09f6c83`](https://github.com/siderolabs/terraform-provider-talos/commit/09f6c838e7430de02f62a1b968f425e0b2be4e0d) fix: restore correct skip_kubernetes_checks behavior
* [`eaedcfd`](https://github.com/siderolabs/terraform-provider-talos/commit/eaedcfdda3e7e3193e3d23231673ef676f575ae5) chore: bump deps
* [`f4d673f`](https://github.com/siderolabs/terraform-provider-talos/commit/f4d673f0851f25c5e15ef8ac9bd6fc12310ab2d5) feat: add ephemeral resources to prevent secrets from leaking to state
* [`5f07e0f`](https://github.com/siderolabs/terraform-provider-talos/commit/5f07e0f6043687b2dc475aeb16d489968a2dd2c5) feat: add staged_if_needing_reboot apply mode for automatic reboot prevention
* [`c8e3b87`](https://github.com/siderolabs/terraform-provider-talos/commit/c8e3b87002418699bf33a2b437f7ff59aa6c70ad) feat: add exact_filters attribute to talos_image_factory_extensions_versions
* [`efe146e`](https://github.com/siderolabs/terraform-provider-talos/commit/efe146e512fb22dd1012849fce098256115961a4) fix: gracefully handle Unknown config_patches values
### Changes since v0.11.0-beta.0
2 commits
* [`01330a9`](https://github.com/siderolabs/terraform-provider-talos/commit/01330a9d19a2a27ed79fd78c6cd1623012f61d09) chore: update goreleaser schema
* [`1585aed`](https://github.com/siderolabs/terraform-provider-talos/commit/1585aed51fcde7e158d45afa0be78f084693d396) fix: remove unsupported windows/arm build target for Go 1.25
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-go** v0.29.0 -> v0.30.0
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.38.1 -> v2.38.2
* **github.com/siderolabs/image-factory** v0.9.0 -> v1.0.3
* **github.com/siderolabs/talos** v1.12.0 -> v1.13.0-alpha.2
* **github.com/siderolabs/talos/pkg/machinery** v1.12.0 -> v1.13.0-alpha.2
* **go.yaml.in/yaml/v4** v4.0.0-rc.3 -> v4.0.0-rc.4
* **golang.org/x/mod** v0.31.0 -> v0.33.0
* **k8s.io/client-go** v0.35.0 -> v0.35.1
Previous release can be found at [v0.10.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.10.1)
## [terraform-provider-talos 0.11.0-beta.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.11.0-beta.0) (2026-02-26)
Welcome to the v0.11.0-beta.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### ephemeral resources
New ephemeral resources are added, please see docs.
### Component Updates
Talos sdk: v1.13.0-alpha.1
### Contributors
* Mickaël Canévet
* Dennis Witt
* Nikita COEUR
* Noel Georgi
* purajit
### Changes
6 commits
* [`09f6c83`](https://github.com/siderolabs/terraform-provider-talos/commit/09f6c838e7430de02f62a1b968f425e0b2be4e0d) fix: restore correct skip_kubernetes_checks behavior
* [`eaedcfd`](https://github.com/siderolabs/terraform-provider-talos/commit/eaedcfdda3e7e3193e3d23231673ef676f575ae5) chore: bump deps
* [`f4d673f`](https://github.com/siderolabs/terraform-provider-talos/commit/f4d673f0851f25c5e15ef8ac9bd6fc12310ab2d5) feat: add ephemeral resources to prevent secrets from leaking to state
* [`5f07e0f`](https://github.com/siderolabs/terraform-provider-talos/commit/5f07e0f6043687b2dc475aeb16d489968a2dd2c5) feat: add staged_if_needing_reboot apply mode for automatic reboot prevention
* [`c8e3b87`](https://github.com/siderolabs/terraform-provider-talos/commit/c8e3b87002418699bf33a2b437f7ff59aa6c70ad) feat: add exact_filters attribute to talos_image_factory_extensions_versions
* [`efe146e`](https://github.com/siderolabs/terraform-provider-talos/commit/efe146e512fb22dd1012849fce098256115961a4) fix: gracefully handle Unknown config_patches values
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-go** v0.29.0 -> v0.30.0
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.38.1 -> v2.38.2
* **github.com/siderolabs/image-factory** v0.9.0 -> v1.0.3
* **github.com/siderolabs/talos** v1.12.0 -> v1.13.0-alpha.2
* **github.com/siderolabs/talos/pkg/machinery** v1.12.0 -> v1.13.0-alpha.2
* **go.yaml.in/yaml/v4** v4.0.0-rc.3 -> v4.0.0-rc.4
* **golang.org/x/mod** v0.31.0 -> v0.33.0
* **k8s.io/client-go** v0.35.0 -> v0.35.1
Previous release can be found at [v0.10.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.10.1)
## [terraform-provider-talos 0.10.0-beta.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.10.0-beta.0) (2025-11-28)
Welcome to the v0.10.0-beta.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### config patches
JSON6502 patches are no longer supported, use strategic patches instead.
### Component Updates
Talos sdk: v1.12.0-beta.0
### Contributors
* Noel Georgi
### Changes
1 commit
* [`11063bc`](https://github.com/siderolabs/terraform-provider-talos/commit/11063bc25f97c7abb8ae9ceb099bbc523c8655b8) chore: bump deps
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-docs** v0.22.0 -> v0.24.0
* **github.com/hashicorp/terraform-plugin-framework** v1.15.1 -> v1.16.1
* **github.com/hashicorp/terraform-plugin-framework-timeouts** v0.5.0 -> v0.7.0
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.18.0 -> v0.19.0
* **github.com/hashicorp/terraform-plugin-go** v0.28.0 -> v0.29.0
* **github.com/hashicorp/terraform-plugin-log** v0.9.0 -> v0.10.0
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.37.0 -> v2.38.1
* **github.com/siderolabs/crypto** v0.6.3 -> v0.6.4
* **github.com/siderolabs/gen** v0.8.5 -> v0.8.6
* **github.com/siderolabs/image-factory** v0.8.3 -> v0.9.0
* **github.com/siderolabs/talos** v1.11.0 -> v1.12.0-beta.0
* **github.com/siderolabs/talos/pkg/machinery** v1.11.0 -> v1.12.0-beta.0
* **go.yaml.in/yaml/v4** v4.0.0-rc.3 **_new_**
* **golang.org/x/mod** v0.27.0 -> v0.30.0
* **k8s.io/client-go** v0.34.0 -> v0.35.0-alpha.3
Previous release can be found at [v0.9.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.9.0)
## [terraform-provider-talos 0.9.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.9.0-alpha.0) (2025-05-19)
Welcome to the v0.9.0-alpha.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### talos_machine_disks Data Source
The `talos_machine_disks` data source has been updated to use the better CEL expression language.
The resource attributes and selector have been updated to use the new syntax.
The user would need to update the data source in their configuration to use the new syntax.
The expression syntax is documented in the [CEL documentation](https://www.talos.dev/latest/talos-guides/configuration/disk-management/#disk-selector).
This also brings in consistency with how disks are reported in Talos.
### Component Updates
Talos sdk: v1.11.0-alpha.0
### Contributors
* Noel Georgi
* Halvdan Hoem Grelland
* obvionaoe
### Changes
7 commits
* [`4016c0c`](https://github.com/siderolabs/terraform-provider-talos/commit/4016c0cc890585c6343829b99d23a22c4fb4bb42) fix: secureboot installer urls for non-metal platform
* [`34f3f1e`](https://github.com/siderolabs/terraform-provider-talos/commit/34f3f1e74a5e7b65cc82a5c46b5929fbde530790) chore: simplify disk selector code
* [`93070aa`](https://github.com/siderolabs/terraform-provider-talos/commit/93070aaa166aa2ba81a3322bac2de4b9ef927319) feat: use CEL expression filters for `talos_machine_disks`
* [`f70e10e`](https://github.com/siderolabs/terraform-provider-talos/commit/f70e10e97d81a1b211c7d09dd3b04156ece70d1a) fix: allow talos_version to be used without a `v` prefix
* [`fa8002d`](https://github.com/siderolabs/terraform-provider-talos/commit/fa8002d47d35c558ae810e50f6ed0beaa759454a) chore: bump deps
* [`e76002d`](https://github.com/siderolabs/terraform-provider-talos/commit/e76002d6bf47b39d474cc5bc01c0b919afb20046) fix: factory installer urls
* [`44eec1c`](https://github.com/siderolabs/terraform-provider-talos/commit/44eec1cc87eeae3d5eaaf45ea742807eea32367a) fix: image factory examples and docs
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.17.0 -> v0.18.0
* **github.com/siderolabs/gen** v0.8.0 -> v0.8.1
* **github.com/siderolabs/image-factory** v0.6.9 -> v0.7.0
* **github.com/siderolabs/talos** v1.10.0 -> v1.11.0-alpha.0
* **github.com/siderolabs/talos/pkg/machinery** v1.10.0 -> v1.11.0-alpha.0
Previous release can be found at [v0.8.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.8.1)
## [terraform-provider-talos 0.8.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.8.0-alpha.0) (2025-04-18)
Welcome to the v0.8.0-alpha.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.10.0-beta.0
### Contributors
* Noel Georgi
* Andrey Smirnov
* Matt Willsher
### Changes
5 commits
* [`2cfcf88`](https://github.com/siderolabs/terraform-provider-talos/commit/2cfcf8802e61550ad7570aa8cbd44439700fd677) chore: bump deps
* [`46ab81c`](https://github.com/siderolabs/terraform-provider-talos/commit/46ab81ca8b799e5390cf398a4a65210d2425a41e) fix: drop talos<->k8s compatibility check
* [`69596f1`](https://github.com/siderolabs/terraform-provider-talos/commit/69596f1e4b74a1d0f95e663315724d9e8150b5b0) fix: skip if aggregator certs are nil
* [`d3214dc`](https://github.com/siderolabs/terraform-provider-talos/commit/d3214dc15659de2906c1aaf2912e77a90dfaca7b) refactor: pull platform metadata from Talos machinery
* [`5c0ff77`](https://github.com/siderolabs/terraform-provider-talos/commit/5c0ff772a72339827bbd63d3fe59c52fb73148de) feat: add secure boot support to non-metal image factory urls
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-docs** v0.20.1 -> v0.21.0
* **github.com/hashicorp/terraform-plugin-framework** v1.13.0 -> v1.14.1
* **github.com/hashicorp/terraform-plugin-framework-timeouts** v0.4.1 -> v0.5.0
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.16.0 -> v0.17.0
* **github.com/hashicorp/terraform-plugin-go** v0.25.0 -> v0.26.0
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.35.0 -> v2.36.1
* **github.com/hashicorp/terraform-plugin-testing** v1.11.0 -> v1.12.0
* **github.com/siderolabs/image-factory** v0.6.4 -> v0.6.8
* **github.com/siderolabs/talos** v1.9.2 -> v1.10.0-beta.0
* **github.com/siderolabs/talos/pkg/machinery** v1.9.2 -> v1.10.0-beta.0
* **golang.org/x/mod** v0.22.0 -> v0.24.0
* **k8s.io/client-go** v0.32.0 -> v0.33.0-rc.0
Previous release can be found at [v0.7.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.7.1)
## [terraform-provider-talos 0.8.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.8.0-alpha.0) (2025-04-18)
Welcome to the v0.8.0-alpha.0 release of terraform-provider-talos!
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.10.0-beta.0
### Contributors
* Noel Georgi
* Andrey Smirnov
* Matt Willsher
### Changes
5 commits
* [`2cfcf88`](https://github.com/siderolabs/terraform-provider-talos/commit/2cfcf8802e61550ad7570aa8cbd44439700fd677) chore: bump deps
* [`46ab81c`](https://github.com/siderolabs/terraform-provider-talos/commit/46ab81ca8b799e5390cf398a4a65210d2425a41e) fix: drop talos<->k8s compatibility check
* [`69596f1`](https://github.com/siderolabs/terraform-provider-talos/commit/69596f1e4b74a1d0f95e663315724d9e8150b5b0) fix: skip if aggregator certs are nil
* [`d3214dc`](https://github.com/siderolabs/terraform-provider-talos/commit/d3214dc15659de2906c1aaf2912e77a90dfaca7b) refactor: pull platform metadata from Talos machinery
* [`5c0ff77`](https://github.com/siderolabs/terraform-provider-talos/commit/5c0ff772a72339827bbd63d3fe59c52fb73148de) feat: add secure boot support to non-metal image factory urls
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-docs** v0.20.1 -> v0.21.0
* **github.com/hashicorp/terraform-plugin-framework** v1.13.0 -> v1.14.1
* **github.com/hashicorp/terraform-plugin-framework-timeouts** v0.4.1 -> v0.5.0
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.16.0 -> v0.17.0
* **github.com/hashicorp/terraform-plugin-go** v0.25.0 -> v0.26.0
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.35.0 -> v2.36.1
* **github.com/hashicorp/terraform-plugin-testing** v1.11.0 -> v1.12.0
* **github.com/siderolabs/image-factory** v0.6.4 -> v0.6.8
* **github.com/siderolabs/talos** v1.9.2 -> v1.10.0-beta.0
* **github.com/siderolabs/talos/pkg/machinery** v1.9.2 -> v1.10.0-beta.0
* **golang.org/x/mod** v0.22.0 -> v0.24.0
* **k8s.io/client-go** v0.32.0 -> v0.33.0-rc.0
Previous release can be found at [v0.7.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.7.1)
## [terraform-provider-talos 0.7.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.7.0-alpha.0) (2024-10-21)
Welcome to the v0.7.0-alpha.0 release of terraform-provider-talos!
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.9.0-alpha.0
### Contributors
* Noel Georgi
### Changes
1 commit
* [`beb5e2b`](https://github.com/siderolabs/terraform-provider-talos/commit/beb5e2b38466116da1d212dea33f5d7926655442) chore: bump deps
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.13.0 -> v0.14.0
* **github.com/siderolabs/talos** v1.8.1 -> v1.9.0-alpha.0
* **github.com/siderolabs/talos/pkg/machinery** v1.8.1 -> v1.9.0-alpha.0
Previous release can be found at [v0.6.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.1)
## [terraform-provider-talos 0.6.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.1) (2024-10-18)
Welcome to the v0.6.1 release of terraform-provider-talos!
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Talos Cluster Kubeconfig
The `talos_cluster_kubeconfig` resource now supports confiuring the certificate renewal check time.
### Component Updates
Talos sdk: v1.8.1
### Contributors
* Noel Georgi
### Changes
3 commits
* [`daddfb7`](https://github.com/siderolabs/terraform-provider-talos/commit/daddfb76cec78135ad62e8e46f4d965eb6eed52a) feat: configurable cert refresh time for kubeconfig
* [`96c9a85`](https://github.com/siderolabs/terraform-provider-talos/commit/96c9a857f9df8a28b7f32f1d0cf948a74a8acfcb) fix: resourceplanmodifiers for kubeconfig resource
* [`800573b`](https://github.com/siderolabs/terraform-provider-talos/commit/800573b841c901f562b8b2b151e58d86516944d0) fix: disks wipe on destroy
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-framework** v1.11.0 -> v1.12.0
* **github.com/hashicorp/terraform-plugin-go** v0.23.0 -> v0.24.0
* **github.com/siderolabs/crypto** v0.4.4 -> v0.5.0
* **github.com/siderolabs/go-blockdevice** v0.4.7 -> v0.4.8
* **github.com/siderolabs/talos** v1.8.0 -> v1.8.1
* **github.com/siderolabs/talos/pkg/machinery** v1.8.0 -> v1.8.1
Previous release can be found at [v0.6.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.0)
## [terraform-provider-talos 0.6.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.0) (2024-09-23)
Welcome to the v0.6.0 release of terraform-provider-talos!
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Image Factory
Support for querying info from Image Factory and registering schematics is now supported via new Terraform resources.
### Talos Cluster Health
`talos_cluster_health` data source now has a way to skip running the Kubernetes components health check by setting `skip_kubernetes_checks` to `true`.
### Talos Cluster Kubeconfig
`talos_cluster_kubeconfig` data source is now deprecated and will be removed in the next minor release.
Use `talos_cluster_kubeconfig` resource instead.
The `talos_cluster_kubeconfig` resource will regenerate kubernetes client config when the time to expiry is less than a month.
### Talos Machine Configuration Data Source
`talos_machine_configuration` data source now defaults to generating config with documentation and examples disabled.
To restore the previous behavior, set `docs` and `examples` attributes to `true`.
### Talos Machine Configuration Apply
`talos_machine_configuration_apply` resource now optionally supports resetting the machine back to maintenance mode.
### Talos Machine Secrets
`talos_machine_secrets` resource now regenerates client config when the time to expiry is less than a month.
### Component Updates
Talos sdk: v1.8.0-alpha.1
### Contributors
* Noel Georgi
* Bruno Bigras
* Hippie Hacker
### Changes
20 commits
* [`758df67`](https://github.com/siderolabs/terraform-provider-talos/commit/758df676873a53d0833b79f74f74c1d0cfbb1f12) docs: machine_configuration fix typo
* [`4e01809`](https://github.com/siderolabs/terraform-provider-talos/commit/4e0180983319098412faab94b18a97ca43cae192) release(v0.6.0-beta.0): prepare release
* [`8dcab7b`](https://github.com/siderolabs/terraform-provider-talos/commit/8dcab7bc766edf0b386c1a061a71f3541c75b56d) chore: bump deps
* [`3b59c4a`](https://github.com/siderolabs/terraform-provider-talos/commit/3b59c4a34fc3b1b824bf104981ac70ebc5e6e722) release(v0.6.0-alpha.2): prepare release
* [`858f7f3`](https://github.com/siderolabs/terraform-provider-talos/commit/858f7f3de9368604b8f18125420d6888606b21be) chore: bump deps
* [`7af49b3`](https://github.com/siderolabs/terraform-provider-talos/commit/7af49b315e4b37b7a165c6ef5d5dc2830dc00a6f) chore: better health checks from talos sdk
* [`063b7ae`](https://github.com/siderolabs/terraform-provider-talos/commit/063b7aeb9dc69782ec7cf541433f7b1eb92e4197) chore: default talos_machine_configuration docs and examples to false
* [`80c5534`](https://github.com/siderolabs/terraform-provider-talos/commit/80c5534fbd876506201f21b50c950ce053402a55) chore: support filtering stable version
* [`b9c7f5f`](https://github.com/siderolabs/terraform-provider-talos/commit/b9c7f5f8aedaca50a4e694e022cae456cd44d065) release(v0.6.0-alpha.1): prepare release
* [`92fcb70`](https://github.com/siderolabs/terraform-provider-talos/commit/92fcb70cd9c1d6cfb5bd3ea159e56fa493fe62db) feat: add `talos_image_factory_url` data source
* [`ffc8102`](https://github.com/siderolabs/terraform-provider-talos/commit/ffc8102dd7f7b0929dc54fa1f50afd9b2cfda6be) feat: add factory support
* [`d6c2a0b`](https://github.com/siderolabs/terraform-provider-talos/commit/d6c2a0bc939de41252d97f09b8e9dfcff3377450) chore: fix goreleaser config
* [`dcdcee6`](https://github.com/siderolabs/terraform-provider-talos/commit/dcdcee62f8d9429cc0416dfdb92dfc27391da0ee) release(v0.6.0-alpha.0): prepare release
* [`d962913`](https://github.com/siderolabs/terraform-provider-talos/commit/d9629133c03ef09d5a6136b0e6cc1624a7ef4c28) chore: reset options for `machine_configuration_apply` resource
* [`f26a591`](https://github.com/siderolabs/terraform-provider-talos/commit/f26a5911bdd243fd384a353de1c0140de256211c) chore: data source -> resource `talos_cluster_kubeconfig`
* [`78fd0d3`](https://github.com/siderolabs/terraform-provider-talos/commit/78fd0d369ff401fbd795609cbbc2275f7d108bb0) chore: ignore version prefix for `talos_version`
* [`11ae330`](https://github.com/siderolabs/terraform-provider-talos/commit/11ae33002bee7a3e319bc7f9ea7555ca3ebaa120) feat: support skipping k8s health checks
* [`0fe1a6f`](https://github.com/siderolabs/terraform-provider-talos/commit/0fe1a6fe8d4440f72d7c553cf961b0de7267404b) docs: update description of `talos_cluster_health`
* [`f6f1811`](https://github.com/siderolabs/terraform-provider-talos/commit/f6f1811e90c9eef91f562f1f8b15f78e984315a0) feat: regenerate talosconfig
* [`501c78e`](https://github.com/siderolabs/terraform-provider-talos/commit/501c78eb7403012c90229bfc24399d0e1603289b) chore: bump deps
### Changes since v0.6.0-beta.0
1 commit
* [`758df67`](https://github.com/siderolabs/terraform-provider-talos/commit/758df676873a53d0833b79f74f74c1d0cfbb1f12) docs: machine_configuration fix typo
### Dependency Changes
* **github.com/blang/semver/v4** v4.0.0 **_new_**
* **github.com/hashicorp/terraform-plugin-docs** v0.19.0 -> v0.19.4
* **github.com/hashicorp/terraform-plugin-framework** v1.7.0 -> v1.11.0
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.12.0 -> v0.13.0
* **github.com/hashicorp/terraform-plugin-go** v0.22.1 -> v0.23.0
* **github.com/hashicorp/terraform-plugin-log** v0.9.0 **_new_**
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.33.0 -> v2.34.0
* **github.com/hashicorp/terraform-plugin-testing** v1.7.0 -> v1.10.0
* **github.com/siderolabs/gen** v0.4.8 -> v0.5.0
* **github.com/siderolabs/image-factory** v0.5.0 **_new_**
* **github.com/siderolabs/talos** v1.8.0-beta.0 **_new_**
* **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> v1.8.0-beta.0
* **golang.org/x/mod** v0.17.0 -> v0.21.0
* **k8s.io/client-go** v0.29.3 -> v0.31.0
Previous release can be found at [v0.5.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.5.0)
## [terraform-provider-talos 0.6.0-beta.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.0-beta.0) (2024-09-10)
Welcome to the v0.6.0-beta.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Image Factory
Support for querying info from Image Factory and registering schematics is now supported via new Terraform resources.
### Talos Cluster Health
`talos_cluster_health` data source now has a way to skip running the Kubernetes components health check by setting `skip_kubernetes_checks` to `true`.
### Talos Cluster Kubeconfig
`talos_cluster_kubeconfig` data source is now deprecated and will be removed in the next minor release.
Use `talos_cluster_kubeconfig` resource instead.
The `talos_cluster_kubeconfig` resource will regenerate kubernetes client config when the time to expiry is less than a month.
### Talos Machine Configuration Data Source
`talos_machine_configuration` data source now defaults to generating config with documentation and examples disabled.
To restore the previous behavior, set `docs` and `examples` attributes to `true`.
### Talos Machine Configuration Apply
`talos_machine_configuration_apply` resource now optionally supports resetting the machine back to maintenance mode.
### Talos Machine Secrets
`talos_machine_secrets` resource now regenerates client config when the time to expiry is less than a month.
### Component Updates
Talos sdk: v1.8.0-alpha.1
### Contributors
* Noel Georgi
* Hippie Hacker
### Changes
18 commits
* [`8dcab7b`](https://github.com/siderolabs/terraform-provider-talos/commit/8dcab7bc766edf0b386c1a061a71f3541c75b56d) chore: bump deps
* [`3b59c4a`](https://github.com/siderolabs/terraform-provider-talos/commit/3b59c4a34fc3b1b824bf104981ac70ebc5e6e722) release(v0.6.0-alpha.2): prepare release
* [`858f7f3`](https://github.com/siderolabs/terraform-provider-talos/commit/858f7f3de9368604b8f18125420d6888606b21be) chore: bump deps
* [`7af49b3`](https://github.com/siderolabs/terraform-provider-talos/commit/7af49b315e4b37b7a165c6ef5d5dc2830dc00a6f) chore: better health checks from talos sdk
* [`063b7ae`](https://github.com/siderolabs/terraform-provider-talos/commit/063b7aeb9dc69782ec7cf541433f7b1eb92e4197) chore: default talos_machine_configuration docs and examples to false
* [`80c5534`](https://github.com/siderolabs/terraform-provider-talos/commit/80c5534fbd876506201f21b50c950ce053402a55) chore: support filtering stable version
* [`b9c7f5f`](https://github.com/siderolabs/terraform-provider-talos/commit/b9c7f5f8aedaca50a4e694e022cae456cd44d065) release(v0.6.0-alpha.1): prepare release
* [`92fcb70`](https://github.com/siderolabs/terraform-provider-talos/commit/92fcb70cd9c1d6cfb5bd3ea159e56fa493fe62db) feat: add `talos_image_factory_url` data source
* [`ffc8102`](https://github.com/siderolabs/terraform-provider-talos/commit/ffc8102dd7f7b0929dc54fa1f50afd9b2cfda6be) feat: add factory support
* [`d6c2a0b`](https://github.com/siderolabs/terraform-provider-talos/commit/d6c2a0bc939de41252d97f09b8e9dfcff3377450) chore: fix goreleaser config
* [`dcdcee6`](https://github.com/siderolabs/terraform-provider-talos/commit/dcdcee62f8d9429cc0416dfdb92dfc27391da0ee) release(v0.6.0-alpha.0): prepare release
* [`d962913`](https://github.com/siderolabs/terraform-provider-talos/commit/d9629133c03ef09d5a6136b0e6cc1624a7ef4c28) chore: reset options for `machine_configuration_apply` resource
* [`f26a591`](https://github.com/siderolabs/terraform-provider-talos/commit/f26a5911bdd243fd384a353de1c0140de256211c) chore: data source -> resource `talos_cluster_kubeconfig`
* [`78fd0d3`](https://github.com/siderolabs/terraform-provider-talos/commit/78fd0d369ff401fbd795609cbbc2275f7d108bb0) chore: ignore version prefix for `talos_version`
* [`11ae330`](https://github.com/siderolabs/terraform-provider-talos/commit/11ae33002bee7a3e319bc7f9ea7555ca3ebaa120) feat: support skipping k8s health checks
* [`0fe1a6f`](https://github.com/siderolabs/terraform-provider-talos/commit/0fe1a6fe8d4440f72d7c553cf961b0de7267404b) docs: update description of `talos_cluster_health`
* [`f6f1811`](https://github.com/siderolabs/terraform-provider-talos/commit/f6f1811e90c9eef91f562f1f8b15f78e984315a0) feat: regenerate talosconfig
* [`501c78e`](https://github.com/siderolabs/terraform-provider-talos/commit/501c78eb7403012c90229bfc24399d0e1603289b) chore: bump deps
### Changes since v0.6.0-alpha.2
1 commit
* [`8dcab7b`](https://github.com/siderolabs/terraform-provider-talos/commit/8dcab7bc766edf0b386c1a061a71f3541c75b56d) chore: bump deps
### Dependency Changes
* **github.com/blang/semver/v4** v4.0.0 **_new_**
* **github.com/hashicorp/terraform-plugin-docs** v0.19.0 -> v0.19.4
* **github.com/hashicorp/terraform-plugin-framework** v1.7.0 -> v1.11.0
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.12.0 -> v0.13.0
* **github.com/hashicorp/terraform-plugin-go** v0.22.1 -> v0.23.0
* **github.com/hashicorp/terraform-plugin-log** v0.9.0 **_new_**
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.33.0 -> v2.34.0
* **github.com/hashicorp/terraform-plugin-testing** v1.7.0 -> v1.10.0
* **github.com/siderolabs/gen** v0.4.8 -> v0.5.0
* **github.com/siderolabs/image-factory** v0.5.0 **_new_**
* **github.com/siderolabs/talos** v1.8.0-beta.0 **_new_**
* **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> v1.8.0-beta.0
* **golang.org/x/mod** v0.17.0 -> v0.21.0
* **k8s.io/client-go** v0.29.3 -> v0.31.0
Previous release can be found at [v0.5.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.5.0)
## [terraform-provider-talos 0.6.0-alpha.2](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.0-alpha.2) (2024-09-02)
Welcome to the v0.6.0-alpha.2 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Image Factory
Support for querying info from Image Factory and registering schematics is now supported via new Terraform resources.
### Talos Cluster Health
`talos_cluster_health` data source now has a way to skip running the Kubernetes components health check by setting `skip_kubernetes_checks` to `true`.
### Talos Cluster Kubeconfig
`talos_cluster_kubeconfig` data source is now deprecated and will be removed in the next minor release.
Use `talos_cluster_kubeconfig` resource instead.
The `talos_cluster_kubeconfig` resource will regenerate kubernetes client config when the time to expiry is less than a month.
### Talos Machine Configuration Data Source
`talos_machine_configuration` data source now defaults to generating config with documentation and examples disabled.
To restore the previous behavior, set `docs` and `examples` attributes to `true`.
### Talos Machine Configuration Apply
`talos_machine_configuration_apply` resource now optionally supports resetting the machine back to maintenance mode.
### Talos Machine Secrets
`talos_machine_secrets` resource now regenerates client config when the time to expiry is less than a month.
### Component Updates
Talos sdk: v1.8.0-alpha.1
### Contributors
* Noel Georgi
* Hippie Hacker
### Changes
16 commits
* [`858f7f3`](https://github.com/siderolabs/terraform-provider-talos/commit/858f7f3de9368604b8f18125420d6888606b21be) chore: bump deps
* [`7af49b3`](https://github.com/siderolabs/terraform-provider-talos/commit/7af49b315e4b37b7a165c6ef5d5dc2830dc00a6f) chore: better health checks from talos sdk
* [`063b7ae`](https://github.com/siderolabs/terraform-provider-talos/commit/063b7aeb9dc69782ec7cf541433f7b1eb92e4197) chore: default talos_machine_configuration docs and examples to false
* [`80c5534`](https://github.com/siderolabs/terraform-provider-talos/commit/80c5534fbd876506201f21b50c950ce053402a55) chore: support filtering stable version
* [`b9c7f5f`](https://github.com/siderolabs/terraform-provider-talos/commit/b9c7f5f8aedaca50a4e694e022cae456cd44d065) release(v0.6.0-alpha.1): prepare release
* [`92fcb70`](https://github.com/siderolabs/terraform-provider-talos/commit/92fcb70cd9c1d6cfb5bd3ea159e56fa493fe62db) feat: add `talos_image_factory_url` data source
* [`ffc8102`](https://github.com/siderolabs/terraform-provider-talos/commit/ffc8102dd7f7b0929dc54fa1f50afd9b2cfda6be) feat: add factory support
* [`d6c2a0b`](https://github.com/siderolabs/terraform-provider-talos/commit/d6c2a0bc939de41252d97f09b8e9dfcff3377450) chore: fix goreleaser config
* [`dcdcee6`](https://github.com/siderolabs/terraform-provider-talos/commit/dcdcee62f8d9429cc0416dfdb92dfc27391da0ee) release(v0.6.0-alpha.0): prepare release
* [`d962913`](https://github.com/siderolabs/terraform-provider-talos/commit/d9629133c03ef09d5a6136b0e6cc1624a7ef4c28) chore: reset options for `machine_configuration_apply` resource
* [`f26a591`](https://github.com/siderolabs/terraform-provider-talos/commit/f26a5911bdd243fd384a353de1c0140de256211c) chore: data source -> resource `talos_cluster_kubeconfig`
* [`78fd0d3`](https://github.com/siderolabs/terraform-provider-talos/commit/78fd0d369ff401fbd795609cbbc2275f7d108bb0) chore: ignore version prefix for `talos_version`
* [`11ae330`](https://github.com/siderolabs/terraform-provider-talos/commit/11ae33002bee7a3e319bc7f9ea7555ca3ebaa120) feat: support skipping k8s health checks
* [`0fe1a6f`](https://github.com/siderolabs/terraform-provider-talos/commit/0fe1a6fe8d4440f72d7c553cf961b0de7267404b) docs: update description of `talos_cluster_health`
* [`f6f1811`](https://github.com/siderolabs/terraform-provider-talos/commit/f6f1811e90c9eef91f562f1f8b15f78e984315a0) feat: regenerate talosconfig
* [`501c78e`](https://github.com/siderolabs/terraform-provider-talos/commit/501c78eb7403012c90229bfc24399d0e1603289b) chore: bump deps
### Changes since v0.6.0-alpha.1
4 commits
* [`858f7f3`](https://github.com/siderolabs/terraform-provider-talos/commit/858f7f3de9368604b8f18125420d6888606b21be) chore: bump deps
* [`7af49b3`](https://github.com/siderolabs/terraform-provider-talos/commit/7af49b315e4b37b7a165c6ef5d5dc2830dc00a6f) chore: better health checks from talos sdk
* [`063b7ae`](https://github.com/siderolabs/terraform-provider-talos/commit/063b7aeb9dc69782ec7cf541433f7b1eb92e4197) chore: default talos_machine_configuration docs and examples to false
* [`80c5534`](https://github.com/siderolabs/terraform-provider-talos/commit/80c5534fbd876506201f21b50c950ce053402a55) chore: support filtering stable version
### Dependency Changes
* **github.com/blang/semver/v4** v4.0.0 **_new_**
* **github.com/hashicorp/terraform-plugin-docs** v0.19.0 -> v0.19.4
* **github.com/hashicorp/terraform-plugin-framework** v1.7.0 -> v1.11.0
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.12.0 -> v0.13.0
* **github.com/hashicorp/terraform-plugin-go** v0.22.1 -> v0.23.0
* **github.com/hashicorp/terraform-plugin-log** v0.9.0 **_new_**
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.33.0 -> v2.34.0
* **github.com/hashicorp/terraform-plugin-testing** v1.7.0 -> v1.10.0
* **github.com/siderolabs/gen** v0.4.8 -> v0.5.0
* **github.com/siderolabs/image-factory** 9687413a9a85 **_new_**
* **github.com/siderolabs/talos** v1.8.0-alpha.2 **_new_**
* **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> v1.8.0-alpha.2
* **golang.org/x/mod** v0.17.0 -> v0.20.0
* **google.golang.org/grpc** v1.63.2 -> v1.66.0
* **k8s.io/client-go** v0.29.3 -> v0.31.0
Previous release can be found at [v0.5.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.5.0)
## [terraform-provider-talos 0.6.0-alpha.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.0-alpha.1) (2024-07-18)
Welcome to the v0.6.0-alpha.1 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Image Factory
Support for querying info from Image Factory and registering schematics is now supported via new Terraform resources.
### Talos Cluster Health
`talos_cluster_health` data source now has a way to skip running the Kubernetes components health check by setting `skip_kubernetes_checks` to `true`.
### Talos Cluster Kubeconfig
`talos_cluster_kubeconfig` data source is now deprecated and will be removed in the next minor release.
Use `talos_cluster_kubeconfig` resource instead.
The `talos_cluster_kubeconfig` resource will regenerate kubernetes client config when the time to expiry is less than a month.
### Talos Machine Configuration Apply
`talos_machine_configuration_apply` resource now optionally supports resetting the machine back to maintenance mode.
### Talos Machine Secrets
`talos_machine_secrets` resource now regenerates client config when the time to expiry is less than a month.
### Component Updates
Talos sdk: v1.8.0-alpha.1
### Contributors
* Noel Georgi
### Changes
11 commits
* [`92fcb70`](https://github.com/siderolabs/terraform-provider-talos/commit/92fcb70cd9c1d6cfb5bd3ea159e56fa493fe62db) feat: add `talos_image_factory_url` data source
* [`ffc8102`](https://github.com/siderolabs/terraform-provider-talos/commit/ffc8102dd7f7b0929dc54fa1f50afd9b2cfda6be) feat: add factory support
* [`d6c2a0b`](https://github.com/siderolabs/terraform-provider-talos/commit/d6c2a0bc939de41252d97f09b8e9dfcff3377450) chore: fix goreleaser config
* [`dcdcee6`](https://github.com/siderolabs/terraform-provider-talos/commit/dcdcee62f8d9429cc0416dfdb92dfc27391da0ee) release(v0.6.0-alpha.0): prepare release
* [`d962913`](https://github.com/siderolabs/terraform-provider-talos/commit/d9629133c03ef09d5a6136b0e6cc1624a7ef4c28) chore: reset options for `machine_configuration_apply` resource
* [`f26a591`](https://github.com/siderolabs/terraform-provider-talos/commit/f26a5911bdd243fd384a353de1c0140de256211c) chore: data source -> resource `talos_cluster_kubeconfig`
* [`78fd0d3`](https://github.com/siderolabs/terraform-provider-talos/commit/78fd0d369ff401fbd795609cbbc2275f7d108bb0) chore: ignore version prefix for `talos_version`
* [`11ae330`](https://github.com/siderolabs/terraform-provider-talos/commit/11ae33002bee7a3e319bc7f9ea7555ca3ebaa120) feat: support skipping k8s health checks
* [`0fe1a6f`](https://github.com/siderolabs/terraform-provider-talos/commit/0fe1a6fe8d4440f72d7c553cf961b0de7267404b) docs: update description of `talos_cluster_health`
* [`f6f1811`](https://github.com/siderolabs/terraform-provider-talos/commit/f6f1811e90c9eef91f562f1f8b15f78e984315a0) feat: regenerate talosconfig
* [`501c78e`](https://github.com/siderolabs/terraform-provider-talos/commit/501c78eb7403012c90229bfc24399d0e1603289b) chore: bump deps
### Changes since v0.6.0-alpha.0
2 commits
* [`92fcb70`](https://github.com/siderolabs/terraform-provider-talos/commit/92fcb70cd9c1d6cfb5bd3ea159e56fa493fe62db) feat: add `talos_image_factory_url` data source
* [`ffc8102`](https://github.com/siderolabs/terraform-provider-talos/commit/ffc8102dd7f7b0929dc54fa1f50afd9b2cfda6be) feat: add factory support
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-docs** v0.19.0 -> v0.19.4
* **github.com/hashicorp/terraform-plugin-framework** v1.7.0 -> v1.10.0
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.12.0 -> v0.13.0
* **github.com/hashicorp/terraform-plugin-go** v0.22.1 -> v0.23.0
* **github.com/hashicorp/terraform-plugin-log** v0.9.0 **_new_**
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.33.0 -> v2.34.0
* **github.com/hashicorp/terraform-plugin-testing** v1.7.0 -> v1.9.0
* **github.com/siderolabs/gen** v0.4.8 -> v0.5.0
* **github.com/siderolabs/image-factory** 8b4e0d9e9819 **_new_**
* **github.com/siderolabs/talos** 980f9ebc0725 **_new_**
* **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> v1.8.0-alpha.1
* **golang.org/x/mod** v0.17.0 -> v0.19.0
* **google.golang.org/grpc** v1.63.2 -> v1.65.0
* **k8s.io/client-go** v0.29.3 -> v0.31.0-beta.0
Previous release can be found at [v0.5.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.5.0)
## [terraform-provider-talos 0.6.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.6.0-alpha.0) (2024-07-11)
Welcome to the v0.6.0-alpha.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Talos Cluster Health
`talos_cluster_health` data source now has a way to skip running the Kubernetes components health check by setting `skip_kubernetes_checks` to `true`.
### Talos Cluster Kubeconfig
`talos_cluster_kubeconfig` data source is now deprecated and will be removed in the next minor release.
Use `talos_cluster_kubeconfig` resource instead.
The `talos_cluster_kubeconfig` resource will regenerate kubernetes client config when the time to expiry is less than a month.
### Talos Machine Configuration Apply
`talos_machine_configuration_apply` resource now optionally supports resetting the machine back to maintenance mode.
### Talos Machine Secrets
`talos_machine_secrets` resource now regenerates client config when the time to expiry is less than a month.
### Component Updates
Talos sdk: v1.8.0-alpha.1
### Contributors
* Noel Georgi
* Dmitriy Matrenichev
### Changes
8 commits
* [`1908d9e`](https://github.com/siderolabs/terraform-provider-talos/commit/1908d9e085d4f0d55aa8d55888a01ed7b52e8295) release(v0.6.0-alpha.0): prepare release
* [`d962913`](https://github.com/siderolabs/terraform-provider-talos/commit/d9629133c03ef09d5a6136b0e6cc1624a7ef4c28) chore: reset options for `machine_configuration_apply` resource
* [`f26a591`](https://github.com/siderolabs/terraform-provider-talos/commit/f26a5911bdd243fd384a353de1c0140de256211c) chore: data source -> resource `talos_cluster_kubeconfig`
* [`78fd0d3`](https://github.com/siderolabs/terraform-provider-talos/commit/78fd0d369ff401fbd795609cbbc2275f7d108bb0) chore: ignore version prefix for `talos_version`
* [`11ae330`](https://github.com/siderolabs/terraform-provider-talos/commit/11ae33002bee7a3e319bc7f9ea7555ca3ebaa120) feat: support skipping k8s health checks
* [`0fe1a6f`](https://github.com/siderolabs/terraform-provider-talos/commit/0fe1a6fe8d4440f72d7c553cf961b0de7267404b) docs: update description of `talos_cluster_health`
* [`f6f1811`](https://github.com/siderolabs/terraform-provider-talos/commit/f6f1811e90c9eef91f562f1f8b15f78e984315a0) feat: regenerate talosconfig
* [`501c78e`](https://github.com/siderolabs/terraform-provider-talos/commit/501c78eb7403012c90229bfc24399d0e1603289b) chore: bump deps
### Changes from siderolabs/gen
2 commits
* [`7654108`](https://github.com/siderolabs/gen/commit/7654108fe6ae15d4765584342709bc0bced6b3d6) chore: add hashtriemap implementation
* [`8485864`](https://github.com/siderolabs/gen/commit/84858640dc9c3032219380885283b995d4f2b0d1) chore: optimize maps.Values and maps.Keys
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-docs** v0.19.0 -> v0.19.4
* **github.com/hashicorp/terraform-plugin-framework** v1.7.0 -> v1.9.0
* **github.com/hashicorp/terraform-plugin-go** v0.22.1 -> v0.23.0
* **github.com/hashicorp/terraform-plugin-log** v0.9.0 **_new_**
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.33.0 -> v2.34.0
* **github.com/hashicorp/terraform-plugin-testing** v1.7.0 -> v1.8.0
* **github.com/siderolabs/gen** v0.4.8 -> v0.5.0
* **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> v1.8.0-alpha.1
* **golang.org/x/mod** v0.17.0 -> v0.19.0
* **google.golang.org/grpc** v1.63.2 -> v1.65.0
* **k8s.io/client-go** v0.29.3 -> v0.31.0-alpha.3
Previous release can be found at [v0.5.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.5.0)
## [terraform-provider-talos 0.4.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.4.0-alpha.0) (2023-08-30)
Welcome to the v0.4.0-alpha.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Talos Cluster Health data source
`talos_cluster_health` data source has been added and the `wait` parameter from the `talos_cluster_kubeconfig` data source is now deprecated.
### Component Updates
Talos sdk: v1.6.0-alpha.0
### Contributors
* Noel Georgi
* Dmitriy Matrenichev
### Changes
4 commits
* [`1c918e6`](https://github.com/siderolabs/terraform-provider-talos/commit/1c918e65b10764b1ed5286193ac661f3daec51d9) chore: add conform
* [`ed36726`](https://github.com/siderolabs/terraform-provider-talos/commit/ed3672669b20c7fd911088609952f8e036f38a1f) feat: add `talos_cluster_health` data source.
* [`5ac7183`](https://github.com/siderolabs/terraform-provider-talos/commit/5ac7183f33a425e17821f1a294a3133daa09e7fa) fix: node/endpoint were swapped for some resources.
* [`713ac46`](https://github.com/siderolabs/terraform-provider-talos/commit/713ac4686a3e00e135cab7ea533da7319522dddd) fix: creation of talos client
### Changes from siderolabs/gen
1 commit
* [`36a3ae3`](https://github.com/siderolabs/gen/commit/36a3ae312ce03876b2c961a1bcb4ef4c221593d7) feat: update module
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-framework** v1.3.4 -> v1.3.5
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.27.0 -> v2.28.0
* **github.com/siderolabs/gen** v0.4.5 -> v0.4.6
* **github.com/siderolabs/talos/pkg/machinery** v1.5.0 -> v1.6.0-alpha.0
* **k8s.io/client-go** v0.28.0 -> v0.28.1
Previous release can be found at [v0.3.2](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.3.2)
## [terraform-provider-talos 0.3.0-beta.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.3.0-beta.0) (2023-08-07)
Welcome to the 0.3.0-beta.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.5.0-beta.0
### Contributors
* Noel Georgi
* Ole-Martin Bratteng
* Spencer Smith
### Changes
5 commits
* [`3f02af3`](https://github.com/siderolabs/terraform-provider-talos/commit/3f02af32747ab97d56d274eecbb3cc12bdaa7d1c) feat: update to talos 1.5 sdk
* [`ff0e2ad`](https://github.com/siderolabs/terraform-provider-talos/commit/ff0e2adec13192716b9cf2180baa1bff2843387d) fix: ci failures due to TF state removal
* [`ee150ce`](https://github.com/siderolabs/terraform-provider-talos/commit/ee150ce9925aac49e324ba4909104cba5a9ad50e) docs: update link to contrib repo
* [`df4f876`](https://github.com/siderolabs/terraform-provider-talos/commit/df4f876ce18e8239bb1cabec7437a0f62ed1f5f7) docs: replace `type` with `machine_type`
* [`f6c8715`](https://github.com/siderolabs/terraform-provider-talos/commit/f6c871516635dbb402bfe24bd47759537c7fee46) chore: bump deps
### Dependency Changes
* **github.com/siderolabs/talos/pkg/machinery** v1.4.7 -> v1.5.0-beta.0
Previous release can be found at [v0.2.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.2.1)
## [terraform-provider-talos 0.2.0-alpha.2](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.2.0-alpha.2) (2023-04-14)
Welcome to the v0.2.0-alpha.2 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Data Sources
`talos_machine_disks` data source is added to list disks on a machine.
### Provider Changes
This version of the provider includes some breaking changes. Make sure to follow the provider upgrade guide at https://registry.terraform.io/providers/siderolabs/talos/latest/docs/guides/version-0.2-upgrade.html
### Component Updates
Talos sdk: v1.4.0-beta.1
### Contributors
* Andrey Smirnov
* Andrey Smirnov
* Artem Chernyshev
* Dmitriy Matrenichev
* Artem Chernyshev
* Noel Georgi
* Serge Logvinov
* Andrew Rynhard
* Andrew Rynhard
* Matt Zahorik
* Olli Janatuinen
* Seán C McCord
* Spencer Smith
### Changes
2 commits
* [`187e434`](https://github.com/siderolabs/terraform-provider-talos/commit/187e434235da1107eaefc1656147900fa8c1d082) feat: `talos_machine_disks` data source
* [`a29e1e7`](https://github.com/siderolabs/terraform-provider-talos/commit/a29e1e7894776a4c796712163e64084e73b1ffa7) fix: handle unknown types at plan time
### Changes from siderolabs/gen
9 commits
* [`214c1ef`](https://github.com/siderolabs/gen/commit/214c1efe795cf426e5ebcc48cb305bfc7a16fdb8) chore: set `slice.Filter` result slice cap to len
* [`8e89b1e`](https://github.com/siderolabs/gen/commit/8e89b1ede9f35ff4c18a41ee44a69259181c892b) feat: add GetOrCreate and GetOrCall methods
* [`7c7ccc3`](https://github.com/siderolabs/gen/commit/7c7ccc3d973621b2fa7adfef10241ecc1f7a644d) feat: introduce channel SendWithContext
* [`b3b6db8`](https://github.com/siderolabs/gen/commit/b3b6db858cb6ce46005edeb70776608e3f9bc402) fix: fix Copy documentation and implementation
* [`521f737`](https://github.com/siderolabs/gen/commit/521f7371f40556ddce7f730c8de5e1888e40b621) feat: add xerrors package which contains additions to the std errors
* [`726e066`](https://github.com/siderolabs/gen/commit/726e066dcb35c86f82866097bed806f22b936292) fix: rename tuples.go to pair.go and set proper package name
* [`d8d7d25`](https://github.com/siderolabs/gen/commit/d8d7d25ce9a588609c00cb798206a01a866bf7a6) chore: minor additions
* [`338a650`](https://github.com/siderolabs/gen/commit/338a65065f92eb6426a66c4a88a0cc02cc02e529) chore: add initial implementation and documentation
* [`4fd8667`](https://github.com/siderolabs/gen/commit/4fd866707052c792a6adccbc28efec5debdd18a8) Initial commit
### Changes from siderolabs/go-blockdevice
59 commits
* [`b4386f3`](https://github.com/siderolabs/go-blockdevice/commit/b4386f37510bc25e39b231fa587288ad0abf0b68) feat: make disk utils read subsystem information from the `/sys/block`
* [`8c7ea19`](https://github.com/siderolabs/go-blockdevice/commit/8c7ea1910b27e0660e3e1a6f98b9f7e24bc11ff0) fix: blockdevice size is reported by Linux in 512 blocks always
* [`e52e012`](https://github.com/siderolabs/go-blockdevice/commit/e52e012a6935a99a1b344a898f281cf7d6a78e69) feat: add ext4 filesystem detection logic
* [`694ac62`](https://github.com/siderolabs/go-blockdevice/commit/694ac62b3dcf995beea95a77659fdc6064b457b3) chore: update imports to siderolabs, rekres
* [`dcf6044`](https://github.com/siderolabs/go-blockdevice/commit/dcf6044c906b36f183e11b6553458c680126d1d9) chore: rekres and rename
* [`9c4af49`](https://github.com/siderolabs/go-blockdevice/commit/9c4af492cc17279f0281fcd271e7423be78442bb) fix: cryptsetup remove slot
* [`74ea471`](https://github.com/siderolabs/go-blockdevice/commit/74ea47109c4525bec139640fed6354ad3097f5fb) feat: add freebsd stubs
* [`9fa801c`](https://github.com/siderolabs/go-blockdevice/commit/9fa801cf4da184e3560b9a18ba43d13316f172f9) feat: add ReadOnly attribute to Disk
* [`fccee8b`](https://github.com/siderolabs/go-blockdevice/commit/fccee8bb082b105cb60db40cb01636efc3241b5f) chore: rekres the source, fix issues
* [`d9c3a27`](https://github.com/siderolabs/go-blockdevice/commit/d9c3a273886113e24809ef1e9930fc982318217d) feat: support probing FAT12/FAT16 filesystems
* [`b374eb4`](https://github.com/siderolabs/go-blockdevice/commit/b374eb48148dc92a82d8bf9540432bb8531f73f3) fix: align partition to 1M boundary by default
* [`ec428fe`](https://github.com/siderolabs/go-blockdevice/commit/ec428fed2ecd5a389833a88f8dc333762816db99) fix: lookup filesystem labels on the actual device path
* [`7b9de26`](https://github.com/siderolabs/go-blockdevice/commit/7b9de26bc6bc3d54b95bd8e8fb3aade4b45adc6c) feat: read symlink fullpath in block device list function
* [`6928ee4`](https://github.com/siderolabs/go-blockdevice/commit/6928ee43c3034549e32f000f8b7bc16a6ebb7ed4) refactor: rewrite GPT serialize/deserialize functions
* [`0c7e429`](https://github.com/siderolabs/go-blockdevice/commit/0c7e4296e01b3df815a935db3e30de6b9d4cc1d1) refactor: simplify middle endian functions
* [`15b182d`](https://github.com/siderolabs/go-blockdevice/commit/15b182db0cd233b163ed83d1724c7e28cf29d71a) fix: return partition table not exist when trying to read an empty dev
* [`b9517d5`](https://github.com/siderolabs/go-blockdevice/commit/b9517d51120d385f97b0026f99ce3c4782940c37) fix: resize partition
* [`70d2865`](https://github.com/siderolabs/go-blockdevice/commit/70d28650b398a14469cbb5356417355b0ba62956) fix: try to find cdrom disks
* [`667bf53`](https://github.com/siderolabs/go-blockdevice/commit/667bf539b99ac34b629a0103ef7a7278a5a5f35d) fix: revert gpt partition not found
* [`d7d4cdd`](https://github.com/siderolabs/go-blockdevice/commit/d7d4cdd7ac56c82caab19246b5decd59f12195eb) fix: gpt partition not found
* [`33afba3`](https://github.com/siderolabs/go-blockdevice/commit/33afba347c0dce38a436c46a0aac26d2f99427c1) fix: also open in readonly mode when running `All` lookup method
* [`e367f9d`](https://github.com/siderolabs/go-blockdevice/commit/e367f9dc7fa935f11672de0fdc8a89429285a07a) feat: make probe always open blockdevices in readonly mode
* [`d981156`](https://github.com/siderolabs/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows
* [`fe24303`](https://github.com/siderolabs/go-blockdevice/commit/fe2430349e9d734ce6dbf4e7b2e0f8a37bb22679) fix: perform correct PMBR partition calculations
* [`2ec0c3c`](https://github.com/siderolabs/go-blockdevice/commit/2ec0c3cc0ff5ff705ed5c910ca1bcd5d93c7b102) fix: preserve the PMBR bootable flag when opening GPT partition
* [`87816a8`](https://github.com/siderolabs/go-blockdevice/commit/87816a81cefc728cfe3cb221b476d8ed4b609fd8) feat: align partition to minimum I/O size
* [`c34b59f`](https://github.com/siderolabs/go-blockdevice/commit/c34b59fb33a7ad8be18bb19bc8c8d8294b4b3a78) feat: expose more encryption options in the LUKS module
* [`30c2bc3`](https://github.com/siderolabs/go-blockdevice/commit/30c2bc3cb62af52f0aea9ce347923b0649fb7928) feat: mark MBR bootable
* [`1292574`](https://github.com/siderolabs/go-blockdevice/commit/1292574643e06512255fb0f45107e0c296eb5a3b) fix: make disk type matcher parser case insensitive
* [`b77400e`](https://github.com/siderolabs/go-blockdevice/commit/b77400e0a7261bf25da77c1f28c2f393f367bfa9) fix: properly detect nvme and sd card disk types
* [`1d830a2`](https://github.com/siderolabs/go-blockdevice/commit/1d830a25f64f6fb96a1bedd800c0b40b107dc833) fix: revert mark the EFI partition in PMBR as bootable
* [`bec914f`](https://github.com/siderolabs/go-blockdevice/commit/bec914ffdda42abcfe642bc2cdfc9fcda56a74ee) fix: mark the EFI partition in PMBR as bootable
* [`776b37d`](https://github.com/siderolabs/go-blockdevice/commit/776b37d31de0781f098f5d9d1894fbea3f2dfa1d) feat: add options to probe disk by various sysblock parameters
* [`bb3ad73`](https://github.com/siderolabs/go-blockdevice/commit/bb3ad73f69836acc2785ec659435e24a531359e7) fix: align partition start to physical sector size
* [`8f976c2`](https://github.com/siderolabs/go-blockdevice/commit/8f976c2031108651738ebd4db69fb09758754a28) feat: replace exec.Command with go-cmd module
* [`1cf7f25`](https://github.com/siderolabs/go-blockdevice/commit/1cf7f252c38cf11ef07723de2debc27d1da6b520) fix: properly handle no child processes error from cmd.Wait
* [`04a9851`](https://github.com/siderolabs/go-blockdevice/commit/04a98510c07fe8477f598befbfe6eaec4f4b73a2) feat: implement luks encryption provider
* [`b0375e4`](https://github.com/siderolabs/go-blockdevice/commit/b0375e4267fdc6108bd9ff7a5dc97b80cd924b1d) feat: add an option to open block device with exclusive flock
* [`5a1c7f7`](https://github.com/siderolabs/go-blockdevice/commit/5a1c7f768e016c93f6c0be130ffeaf34109b5b4d) refactor: add devname into gpt.Partition, refactor probe package
* [`f2728a5`](https://github.com/siderolabs/go-blockdevice/commit/f2728a581972be977d863d5d9177a873b8f3fc7b) fix: keep contents of PMBR when writing it
* [`2878460`](https://github.com/siderolabs/go-blockdevice/commit/2878460b54e8b8c3846c6a882ca9e1472c8b6b3b) fix: write second copy of partition entries
* [`943b08b`](https://github.com/siderolabs/go-blockdevice/commit/943b08bc32a2156cffb23e92b8be9288de4a7421) fix: blockdevice reset should read partition table from disk
* [`5b4ee44`](https://github.com/siderolabs/go-blockdevice/commit/5b4ee44cfd434a03ec2d7167bcc56d0f164c3fa2) fix: ignore `/dev/ram` devices
* [`98754ec`](https://github.com/siderolabs/go-blockdevice/commit/98754ec2bb200acc9e9e573fa766754d60e25ff2) refactor: rewrite GPT library
* [`2a1baad`](https://github.com/siderolabs/go-blockdevice/commit/2a1baadffdf8c9b65355e9af6e744aeab838c9db) fix: correctly build paths for `mmcblk` devices
* [`8076344`](https://github.com/siderolabs/go-blockdevice/commit/8076344a95021f25ab5d1fbf5ea4fefc790f6c3c) fix: return proper disk size from GetDisks function
* [`8742133`](https://github.com/siderolabs/go-blockdevice/commit/874213371a3fb0925aab45cbba68a957e3319525) chore: add common method to list available disks using /sys/block
* [`c4b5833`](https://github.com/siderolabs/go-blockdevice/commit/c4b583363d63503ed7e4adb9a9fa64335f7e198d) feat: implement "fast" wipe
* [`b4e67d7`](https://github.com/siderolabs/go-blockdevice/commit/b4e67d73d70d8dc06aa2b4986622dcb854dfc40c) feat: return resize status from Resize() function
* [`ceae64e`](https://github.com/siderolabs/go-blockdevice/commit/ceae64edb3a591c6f6bbd75b1149d1cfe426dd8e) fix: sync kernel partition table incrementally
* [`2cb9516`](https://github.com/siderolabs/go-blockdevice/commit/2cb95165aa67b0b839863b5ad89920c3ac7e2c82) fix: return correct error value from blkpg functions
* [`cebe43d`](https://github.com/siderolabs/go-blockdevice/commit/cebe43d1fdc1e509437198e578faa9d5a804cc37) refactor: expose `InsertAt` method via interface
* [`c40dcd8`](https://github.com/siderolabs/go-blockdevice/commit/c40dcd80c50b41c1f2a60ea6aa9d5fb3d3b180a3) fix: properly inform kernel about partition deletion
* [`bb8ac5d`](https://github.com/siderolabs/go-blockdevice/commit/bb8ac5d6a25e279e16213f585dc8d02ba6ed645f) feat: implement disk wiping via several methods
* [`23fb7dc`](https://github.com/siderolabs/go-blockdevice/commit/23fb7dc755325cfe12e48c8e8e31bebab9ddc2bc) feat: expose partition name (label)
* [`ff3a821`](https://github.com/siderolabs/go-blockdevice/commit/ff3a8210be999b8bfb2019f19f8a8b50901c64cc) feat: implement 'InsertAt' method to insert partitions at any position
* [`3d1ce4f`](https://github.com/siderolabs/go-blockdevice/commit/3d1ce4fc859fa614a4c5c54a10c0f5f4fce38bb6) fix: calculate last lba of partition correctly
* [`b71540f`](https://github.com/siderolabs/go-blockdevice/commit/b71540f6c398e958bdb7c118396a736419f735d4) feat: copy initial version from talos-systems/talos
* [`ca3c078`](https://github.com/siderolabs/go-blockdevice/commit/ca3c078da95e6497c9d41667dc242e32682e517d) Initial commit
### Dependency Changes
* **github.com/dustin/go-humanize** v1.0.1 **_new_**
* **github.com/siderolabs/gen** v0.4.3 **_new_**
* **github.com/siderolabs/go-blockdevice** v0.4.4 **_new_**
* **k8s.io/client-go** v0.26.3 -> v0.27.0
Previous release can be found at [v0.2.0-alpha.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.2.0-alpha.1)
## [terraform-provider-talos 0.2.0-alpha.1](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.2.0-alpha.1) (2023-04-12)
Welcome to the v0.2.0-alpha.1 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Provider Changes
This version of the provider includes some breaking changes. Make sure to follow the provider upgrade guide at https://registry.terraform.io/providers/siderolabs/talos/latest/docs/guides/version-0.2-upgrade.html
### Component Updates
Talos sdk: v1.4.0-beta.1
### Contributors
* Noel Georgi
### Changes
1 commit
* [`96aeedd`](https://github.com/siderolabs/terraform-provider-talos/commit/96aeedd4bca46ced42bc98220e809940b773ce5d) docs: fix rendering of website
### Dependency Changes
* **github.com/siderolabs/talos/pkg/machinery** v1.4.0-beta.0 -> v1.4.0-beta.1
Previous release can be found at [v0.2.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.2.0-alpha.0)
## [terraform-provider-talos 0.2.0-alpha.0](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.2.0-alpha.0) (2023-04-10)
Welcome to the v0.2.0-alpha.0 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Provider Changes
This version of the provider includes some breaking changes. Make sure to follow the provider upgrade guide at https://registry.terraform.io/providers/siderolabs/talos/latest/docs/guides/version-0.2-upgrade
### Component Updates
Talos sdk: v1.4.0-beta.0
### Contributors
* Andrey Smirnov
* Noel Georgi
* Alexey Palazhchenko
* Andrey Smirnov
* Spencer Smith
* Andrew Rynhard
* Artem Chernyshev
* Robert Wunderer
* Serge Logvinov
### Changes
10 commits
* [`6b2182b`](https://github.com/siderolabs/terraform-provider-talos/commit/6b2182be4015eb21d936930cfa108159b3fa16f6) chore: ci cleanup
* [`ea07caa`](https://github.com/siderolabs/terraform-provider-talos/commit/ea07caa6f39d159b286db76c525ed7cb1892f1e6) feat: code cleanup and tests
* [`4e5c210`](https://github.com/siderolabs/terraform-provider-talos/commit/4e5c210c219ddeb0ccf68cd80cb2988ce0ab5ccd) feat: use new tf sdk
* [`d1438b7`](https://github.com/siderolabs/terraform-provider-talos/commit/d1438b71d94b0ee0d0f3f5feb12317344c6e2a3e) chore: bump talos machinery
* [`aed502e`](https://github.com/siderolabs/terraform-provider-talos/commit/aed502e51945bad2493c521672a52111fcd1eca6) fix: state update required two runs
* [`dc00baa`](https://github.com/siderolabs/terraform-provider-talos/commit/dc00baad8d85e2786789ea03ac8e35d0b613bd70) fix: force new talosconfig if endpoints or nodes change
* [`b7d84ba`](https://github.com/siderolabs/terraform-provider-talos/commit/b7d84ba4e297271b1599de20310640c4ebffccb9) chore: update registry index file, remove example
* [`158dbbd`](https://github.com/siderolabs/terraform-provider-talos/commit/158dbbde42326fed9ada2a97a2a25c625c0a783e) docs: re-word `talos_version`
* [`f3b4a5b`](https://github.com/siderolabs/terraform-provider-talos/commit/f3b4a5b24362c264a642e5bc5a28bba0e70a86fc) chore: bump deps
* [`d2b6df0`](https://github.com/siderolabs/terraform-provider-talos/commit/d2b6df03e44a9150445209d3bd118c5d4417547c) docs: clarify meaning of `talos_version` in `machine_configuration` resources
### Changes from siderolabs/crypto
27 commits
* [`c3225ee`](https://github.com/siderolabs/crypto/commit/c3225eee603a8d1218c67e1bfe33ddde7953ed74) feat: allow CSR template subject field to be overridden
* [`8570669`](https://github.com/siderolabs/crypto/commit/85706698dac8cddd0e9f41006bed059347d2ea26) chore: rename to siderolabs/crypto
* [`e9df1b8`](https://github.com/siderolabs/crypto/commit/e9df1b8ca74c6efdc7f72191e5d2613830162fd5) feat: add support for generating keys from RSA-SHA256 CAs
* [`510b0d2`](https://github.com/siderolabs/crypto/commit/510b0d2753a89170d0c0f60e052a66484997a5b2) chore: add json tags
* [`6fa2d93`](https://github.com/siderolabs/crypto/commit/6fa2d93d0382299d5471e0de8e831c923398aaa8) fix: deepcopy nil fields as `nil`
* [`9a63cba`](https://github.com/siderolabs/crypto/commit/9a63cba8dabd278f3080fa8c160613efc48c43f8) fix: add back support for generating ECDSA keys with P-256 and SHA512
* [`893bc66`](https://github.com/siderolabs/crypto/commit/893bc66e4716a4cb7d1d5e66b5660ffc01f22823) fix: use SHA256 for ECDSA-P256
* [`deec8d4`](https://github.com/siderolabs/crypto/commit/deec8d47700e10e3ea813bdce01377bd93c83367) chore: implement DeepCopy methods for PEMEncoded* types
* [`d3cb772`](https://github.com/siderolabs/crypto/commit/d3cb77220384b3a3119a6f3ddb1340bbc811f1d1) feat: make possible to change KeyUsage
* [`6bc5bb5`](https://github.com/siderolabs/crypto/commit/6bc5bb50c52767296a1b1cab6580e3fcf1358f34) chore: remove unused argument
* [`cd18ef6`](https://github.com/siderolabs/crypto/commit/cd18ef62eb9f65d8b6730a2eb73e47e629949e1b) feat: add support for several organizations
* [`97c888b`](https://github.com/siderolabs/crypto/commit/97c888b3924dd5ac70b8d30dd66b4370b5ab1edc) chore: add options to CSR
* [`7776057`](https://github.com/siderolabs/crypto/commit/7776057f5086157873f62f6a21ec23fa9fd86e05) chore: fix typos
* [`80df078`](https://github.com/siderolabs/crypto/commit/80df078327030af7e822668405bb4853c512bd7c) chore: remove named result parameters
* [`15bdd28`](https://github.com/siderolabs/crypto/commit/15bdd282b74ac406ab243853c1b50338a1bc29d0) chore: minor updates
* [`4f80b97`](https://github.com/siderolabs/crypto/commit/4f80b976b640d773fb025d981bf85bcc8190815b) fix: verify CSR signature before issuing a certificate
* [`39584f1`](https://github.com/siderolabs/crypto/commit/39584f1b6e54e9966db1f16369092b2215707134) feat: support for key/certificate types RSA, Ed25519, ECDSA
* [`cf75519`](https://github.com/siderolabs/crypto/commit/cf75519cab82bd1b128ae9b45107c6bb422bd96a) fix: function NewKeyPair should create certificate with proper subject
* [`751c95a`](https://github.com/siderolabs/crypto/commit/751c95aa9434832a74deb6884cff7c5fd785db0b) feat: add 'PEMEncodedKey' which allows to transport keys in YAML
* [`562c3b6`](https://github.com/siderolabs/crypto/commit/562c3b66f89866746c0ba47927c55f41afed0f7f) feat: add support for public RSA key in RSAKey
* [`bda0e9c`](https://github.com/siderolabs/crypto/commit/bda0e9c24e80c658333822e2002e0bc671ac53a3) feat: enable more conversions between encoded and raw versions
* [`e0dd56a`](https://github.com/siderolabs/crypto/commit/e0dd56ac47456f85c0b247999afa93fb87ebc78b) feat: add NotBefore option for x509 cert creation
* [`12a4897`](https://github.com/siderolabs/crypto/commit/12a489768a6bb2c13e16e54617139c980f99a658) feat: add support for SPKI fingerprint generation and matching
* [`d0c3eef`](https://github.com/siderolabs/crypto/commit/d0c3eef149ec9b713e7eca8c35a6214bd0a64bc4) fix: implement NewKeyPair
* [`196679e`](https://github.com/siderolabs/crypto/commit/196679e9ec77cb709db54879ddeddd4eaafaea01) feat: move `pkg/grpc/tls` from `github.com/talos-systems/talos` as `./tls`
* [`1ff6242`](https://github.com/siderolabs/crypto/commit/1ff6242c91bb298ceeb4acd65685cba952fe4178) chore: initial version as imported from talos-systems/talos
* [`835063e`](https://github.com/siderolabs/crypto/commit/835063e055b28a525038b826a6d80cbe76402414) chore: initial commit
### Dependency Changes
* **github.com/hashicorp/terraform-plugin-framework** v1.2.0 **_new_**
* **github.com/hashicorp/terraform-plugin-framework-timeouts** v0.3.1 **_new_**
* **github.com/hashicorp/terraform-plugin-framework-validators** v0.10.0 **_new_**
* **github.com/hashicorp/terraform-plugin-go** v0.15.0 **_new_**
* **github.com/hashicorp/terraform-plugin-sdk/v2** v2.25.0 -> v2.26.1
* **github.com/hashicorp/terraform-plugin-testing** v1.2.0 **_new_**
* **github.com/siderolabs/crypto** v0.4.0 **_new_**
* **github.com/siderolabs/talos/pkg/machinery** v1.3.6 -> v1.4.0-beta.0
* **golang.org/x/mod** v0.10.0 **_new_**
* **google.golang.org/grpc** v1.51.0 -> v1.54.0
* **k8s.io/client-go** v0.26.3 **_new_**
Previous release can be found at [v0.1.2](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.2)
## [terraform-provider-talos 0.1.0-alpha.12](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.12) (2022-12-16)
Welcome to the v0.1.0-alpha.12 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Fixes
Fixed an issue with the provider when using a secure Talos client.
### Contributors
* Noel Georgi
### Changes
1 commit
* [`3c80f59`](https://github.com/siderolabs/terraform-provider-talos/commit/3c80f59ad7a8514b5481c84ca0d210c62ea06bcc) fix: handling talos secure client
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.11](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.11)
## [terraform-provider-talos 0.1.0-alpha.11](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.11) (2022-12-09)
Welcome to the v0.1.0-alpha.11 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.2.7
### Contributors
### Changes
0 commit
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.10](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.10)
## [terraform-provider-talos 0.1.0-alpha.10](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.10) (2022-11-02)
Welcome to the v0.1.0-alpha.10 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.2.6
### Contributors
### Changes
0 commit
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.9](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.9)
## [terraform-provider-talos 0.1.0-alpha.9](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.9) (2022-10-18)
Welcome to the v0.1.0-alpha.9 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.2.5
### Contributors
### Changes
0 commit
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.8](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.8)
## [terraform-provider-talos 1.2.4](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v1.2.4) (2022-10-18)
Welcome to the v1.2.4 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Component Updates
Talos sdk: v1.2.4
### Contributors
* Dmitriy Matrenichev
* Noel Georgi
### Changes
1 commit
* [`1c7975d`](https://github.com/siderolabs/terraform-provider-talos/commit/1c7975d1392406fa10a1a225e426e005d699c73e) chore: move to `gen` go pkg
### Changes from siderolabs/gen
2 commits
* [`338a650`](https://github.com/siderolabs/gen/commit/338a65065f92eb6426a66c4a88a0cc02cc02e529) chore: add initial implementation and documentation
* [`4fd8667`](https://github.com/siderolabs/gen/commit/4fd866707052c792a6adccbc28efec5debdd18a8) Initial commit
### Dependency Changes
* **github.com/siderolabs/gen** v0.1.0 **_new_**
Previous release can be found at [v0.1.0-alpha.7](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.7)
## [terraform-provider-talos 0.1.0-alpha.7](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.7) (2022-09-20)
Welcome to the v0.1.0-alpha.7 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Contributors
* Noel Georgi
### Changes
1 commit
* [`79594a6`](https://github.com/siderolabs/terraform-provider-talos/commit/79594a60b231966f68be2e39c01990e209176d6b) chore: bump talos to v1.2.3
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.6](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.6)
## [terraform-provider-talos 0.1.0-alpha.6](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.6) (2022-09-15)
Welcome to the v0.1.0-alpha.6 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Talos Provider
The Talos provider now requires `endpoint` and `node` to be set for `talos_machine_configuration_apply`, `talos_machine_bootstrap`, `talos_cluster_kubeconfig` resources.
The `endpoints` and `nodes` arguments are removed for the above resources.
This release also fixes a bug when multiple endpoitns were specified in the Talos client config.
### Contributors
### Changes
0 commit
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.5](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.5)
## [terraform-provider-talos 0.1.0-alpha.5](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.5) (2022-09-15)
Welcome to the v0.1.0-alpha.5 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Talos Provider
The Talos provider now requires `endpoint` and `node` to be set for `talos_machine_configuration_apply`, `talos_machine_bootstrap`, `talos_cluster_kubeconfig` resources.
The `endpoints` and `nodes` arguments are removed for the above resources.
This release also fixes a bug when multiple endpoitns were specified in the Talos client config.
### Contributors
### Changes
0 commit
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.4](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.4)
## [terraform-provider-talos 0.1.0-alpha.4](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.4) (2022-09-15)
Welcome to the v0.1.0-alpha.4 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Talos Provider
The Talos provider now requires `endpoint` and `node` to be set for `talos_machine_configuration_apply`, `talos_machine_bootstrap`, `talos_cluster_kubeconfig` resources.
The `endpoints` and `nodes` arguments are removed for the above resources.
This release also fixes a bug when multiple endpoitns were specified in the Talos client config.
### Contributors
* Noel Georgi
* Nahuel Pastorale
### Changes
3 commits
* [`ea5a4ba`](https://github.com/siderolabs/terraform-provider-talos/commit/ea5a4bab1d86f20fc832fd4ffacf5978699a716e) release(v0.1.0-alpha.4): prepare release
* [`b9111db`](https://github.com/siderolabs/terraform-provider-talos/commit/b9111db42dade59000b26b313f7b7f0d5e6dc083) fix: client operations
* [`e2d04ac`](https://github.com/siderolabs/terraform-provider-talos/commit/e2d04acbf10eb092effa60a1cbc0eb27325d4b19) docs: fix wrong resource reference
### Dependency Changes
This release has no dependency changes
Previous release can be found at [v0.1.0-alpha.3](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.3)
## [terraform-provider-talos 0.1.0-alpha.3](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.3) (2022-09-08)
Welcome to the v0.1.0-alpha.3 release of terraform-provider-talos!
*This is a pre-release of terraform-provider-talos*
Please try out the release binaries and report any issues at
https://github.com/siderolabs/terraform-provider-talos/issues.
### Talos Provider
The Talos provider supports generating configs, applying them and bootstrap the nodes.
Resources supported:
* `talos_machine_secrets`
* `talos_client_configuration`
* `talos_machine_configuration_controlplane`
* `talos_machine_configuration_worker`
* `talos_machine_configuration_apply`
* `talos_machine_bootstrap`
* `talos_cluster_kubeconfig`
Data sources supported:
* `talos_client_configuration`
* `talos_cluster_kubeconfig`
Data sources will always create a diff and might be removed in a future release.
### Contributors
* Andrew Rynhard
* Andrey Smirnov
* Dmitriy Matrenichev
* Noel Georgi
### Changes
2 commits
* [`e29e302`](https://github.com/siderolabs/terraform-provider-talos/commit/e29e3028b0b5dc3c56757a7724d1400d4d98b3e8) chore: add release workflow
* [`4ecfd4f`](https://github.com/siderolabs/terraform-provider-talos/commit/4ecfd4f52353495a8304b0530d85a73b757861c9) feat: add new resources
### Changes from siderolabs/go-pointer
2 commits
* [`71ccdf0`](https://github.com/siderolabs/go-pointer/commit/71ccdf0d65330596f4def36da37625e4f362f2a9) chore: implement main functionality
* [`c1c3b23`](https://github.com/siderolabs/go-pointer/commit/c1c3b235d30cb0de97ed0645809f2b21af3b021e) Initial commit
### Dependency Changes
* **github.com/cosi-project/runtime** v0.1.1 **_new_**
* **github.com/siderolabs/go-pointer** v1.0.0 **_new_**
* **google.golang.org/grpc** v1.48.0 **_new_**
Previous release can be found at [v0.1.0-alpha.2](https://github.com/siderolabs/terraform-provider-talos/releases/tag/v0.1.0-alpha.2)
================================================
FILE: LICENSE
================================================
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
================================================
FILE: Makefile
================================================
TAG ?= $(shell git describe --tag --always --dirty)
ARTIFACTS ?= _out
ifneq ($(origin TESTS), undefined)
RUNARGS = -run='$(TESTS)'
endif
ifneq ($(origin CI), undefined)
RUNARGS += -parallel=3
RUNARGS += -timeout=40m
RUNARGS += -exec="sudo -E"
endif
.PHONY: generate
generate:
go generate ./pkg/talos
go generate
.PHONY: testacc
testacc:
# TF_CLI_CONFIG_FILE is set here to avoid using the user's .terraformrc file. Ref: https://github.com/hashicorp/terraform-plugin-sdk/issues/1171
TF_CLI_CONFIG_FILE="thisfiledoesnotexist" TF_ACC=1 go test -v -failfast -cover $(RUNARGS) ./...
.PHONY: check-dirty
check-dirty: generate ## Verifies that source tree is not dirty
@if test -n "`git status --porcelain`"; then echo "Source tree is dirty"; git status; exit 1 ; fi
build-debug:
go build -gcflags='all=-N -l'
install:
go install .
$(ARTIFACTS):
mkdir -p $(ARTIFACTS)
release-notes: $(ARTIFACTS)
@ARTIFACTS=$(ARTIFACTS) ./hack/release.sh $@ $(ARTIFACTS)/RELEASE_NOTES.md $(TAG)
go-vulncheck:
go tool -modfile tools/go.mod golang.org/x/vuln/cmd/govulncheck ./...
sbom: $(ARTIFACTS)
SYFT_FORMAT_PRETTY=1 SYFT_FORMAT_SPDX_JSON_DETERMINISTIC_UUID=1 go tool -modfile tools/go.mod github.com/anchore/syft/cmd/syft dir:. -o spdx-json > $(ARTIFACTS)/sbom.spdx.json
SYFT_FORMAT_PRETTY=1 SYFT_FORMAT_SPDX_JSON_DETERMINISTIC_UUID=1 go tool -modfile tools/go.mod github.com/anchore/syft/cmd/syft dir:. -o cyclonedx-json > $(ARTIFACTS)/sbom.cyclonedx.json
================================================
FILE: README.md
================================================
# terraform-provider-talos
## Debugging
In a bash shell, build a debug version of this provider binary:
```bash
make build-debug
```
In Visual Studio Code, [start the provider in a debug session](https://developer.hashicorp.com/terraform/plugin/debugging#starting-a-provider-in-debug-mode).
In a new bash shell, go into your terraform project directory, and run
terraform with `TF_REATTACH_PROVIDERS` set to the value printed in the VSCode debug windows.
================================================
FILE: docs/data-sources/client_configuration.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_client_configuration Data Source - talos"
subcategory: ""
description: |-
Generate client configuration for a Talos cluster
---
# talos_client_configuration (Data Source)
Generate client configuration for a Talos cluster
## Example Usage
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
```
## Schema
### Required
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `cluster_name` (String) The name of the cluster in the generated config
### Optional
- `endpoints` (List of String) endpoints to set in the generated config
- `nodes` (List of String) nodes to set in the generated config
### Read-Only
- `id` (String) The ID of this resource
- `talos_config` (String, Sensitive) The generated client configuration
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
================================================
FILE: docs/data-sources/cluster_health.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_cluster_health Data Source - talos"
subcategory: ""
description: |-
Waits for the Talos cluster to be healthy. Can be used as a dependency before running other operations on the cluster.
---
# talos_cluster_health (Data Source)
Waits for the Talos cluster to be healthy. Can be used as a dependency before running other operations on the cluster.
## Schema
### Required
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `control_plane_nodes` (List of String) List of control plane nodes to check for health.
- `endpoints` (List of String) endpoints to use for the health check client. Use at least one control plane endpoint.
### Optional
- `skip_kubernetes_checks` (Boolean) Skip Kubernetes component checks, this is useful to check if the nodes has finished booting up and kubelet is running. Default is false.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
- `worker_nodes` (List of String) List of worker nodes to check for health.
### Read-Only
- `id` (String) The ID of this resource.
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `timeouts`
Optional:
- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled.
================================================
FILE: docs/data-sources/cluster_kubeconfig.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_cluster_kubeconfig Data Source - talos"
subcategory: ""
description: |-
Retrieves the kubeconfig for a Talos cluster
---
# talos_cluster_kubeconfig (Data Source)
Retrieves the kubeconfig for a Talos cluster
## Example Usage
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
}
}
})
]
}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = "10.5.0.2"
client_configuration = talos_machine_secrets.this.client_configuration
}
data "talos_cluster_kubeconfig" "this" {
depends_on = [
talos_machine_bootstrap.this
]
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
}
```
## Schema
### Required
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `node` (String) controlplane node to retrieve the kubeconfig from
### Optional
- `endpoint` (String) endpoint to use for the talosclient. If not set, the node value will be used
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
- `wait` (Boolean, Deprecated) Wait for the kubernetes api to be available
### Read-Only
- `id` (String) The ID of this resource.
- `kubeconfig_raw` (String, Sensitive) The raw kubeconfig
- `kubernetes_client_configuration` (Attributes) The kubernetes client configuration (see [below for nested schema](#nestedatt--kubernetes_client_configuration))
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `timeouts`
Optional:
- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled.
### Nested Schema for `kubernetes_client_configuration`
Read-Only:
- `ca_certificate` (String) The kubernetes CA certificate
- `client_certificate` (String) The kubernetes client certificate
- `client_key` (String, Sensitive) The kubernetes client key
- `host` (String) The kubernetes host
================================================
FILE: docs/data-sources/image_factory_extensions_versions.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_image_factory_extensions_versions Data Source - talos"
subcategory: ""
description: |-
The image factory extensions versions data source provides a list of available extensions for a specific talos version from the image factory.
---
# talos_image_factory_extensions_versions (Data Source)
The image factory extensions versions data source provides a list of available extensions for a specific talos version from the image factory.
## Example Usage
```terraform
provider "talos" {}
data "talos_image_factory_extensions_versions" "this" {
# get the latest talos version
talos_version = "v1.7.5"
filters = {
names = [
"amdgpu",
"tailscale",
]
}
}
```
## Schema
### Required
- `talos_version` (String) The talos version to get extensions for.
### Optional
- `exact_filters` (Attributes) The filter to apply to the extensions list. (see [below for nested schema](#nestedatt--exact_filters))
- `filters` (Attributes) The filter to apply to the extensions list. (see [below for nested schema](#nestedatt--filters))
### Read-Only
- `extensions_info` (List of Object) The list of available extensions for the specified talos version. (see [below for nested schema](#nestedatt--extensions_info))
- `id` (String) The ID of this resource.
### Nested Schema for `exact_filters`
Optional:
- `names` (List of String) The exact name match of the extension to filter by.
### Nested Schema for `filters`
Optional:
- `names` (List of String) The name of the extension to filter by.
### Nested Schema for `extensions_info`
Read-Only:
- `author` (String)
- `description` (String)
- `digest` (String)
- `name` (String)
- `ref` (String)
================================================
FILE: docs/data-sources/image_factory_overlays_versions.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_image_factory_overlays_versions Data Source - talos"
subcategory: ""
description: |-
The image factory overlays versions data source provides a list of available overlays for a specific talos version from the image factory.
---
# talos_image_factory_overlays_versions (Data Source)
The image factory overlays versions data source provides a list of available overlays for a specific talos version from the image factory.
## Example Usage
```terraform
provider "talos" {}
data "talos_image_factory_overlays_versions" "this" {
# get the latest talos version
talos_version = "v1.7.5"
filters = {
name = "rock4cplus"
}
}
```
## Schema
### Required
- `talos_version` (String) The talos version to get overlays for.
### Optional
- `filters` (Attributes) The filter to apply to the overlays list. (see [below for nested schema](#nestedatt--filters))
### Read-Only
- `id` (String) The ID of this resource.
- `overlays_info` (List of Object) The list of available extensions for the specified talos version. (see [below for nested schema](#nestedatt--overlays_info))
### Nested Schema for `filters`
Optional:
- `name` (String) The name of the overlay to filter by.
### Nested Schema for `overlays_info`
Read-Only:
- `digest` (String)
- `image` (String)
- `name` (String)
- `ref` (String)
================================================
FILE: docs/data-sources/image_factory_urls.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_image_factory_urls Data Source - talos"
subcategory: ""
description: |-
Generates URLs for different assets supported by the Talos image factory.
---
# talos_image_factory_urls (Data Source)
Generates URLs for different assets supported by the Talos image factory.
## Example Usage
```terraform
data "talos_image_factory_urls" "this" {
talos_version = "v1.7.5"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "metal"
}
output "installer_image" {
value = data.talos_image_factory_urls.this.urls.installer
}
```
## Schema
### Required
- `schematic_id` (String) The schematic ID for which the URLs are generated.
- `talos_version` (String) The Talos version for which the URLs are generated.
### Optional
- `architecture` (String) The platform architecture for which the URLs are generated. Defaults to amd64.
- `platform` (String) The platform for which the URLs are generated.
#### Metal
- metal
#### Cloud Platforms
- aws
- gcp
- equinixMetal
- azure
- digital-ocean
- nocloud
- openstack
- vmware
- akamai
- cloudstack
- hcloud
- oracle
- upcloud
- vultr
- exoscale
- opennebula
- scaleway
- `sbc` (String) The SBC's (Single Board Copmuters) for which the url are generated.
#### Single Board Computers
- rpi_5
- rpi_generic
- revpi_generic
- bananapi_m64
- nanopi_r4s
- nanopi_r5s
- jetson_nano
- libretech_all_h3_cc_h5
- orangepi_r1_plus_lts
- pine64
- rock64
- rock4cplus
- rock4se
- rock5a
- rock5b
- rockpi_4
- rockpi_4c
- helios64
- turingrk1
- orangepi-5
- orangepi-5-plus
- rockpro64
- odroid-m1
- radxa-zero-3e
- rock3b
- orangepi-5-max
- rock5t
- friendlyelec-cm3588-nas
- rock5b-plus
### Read-Only
- `id` (String) The ID of this resource.
- `urls` (Attributes) The URLs for different assets supported by the Talos image factory. If the URL is not available for a specific asset, it will be an empty string. (see [below for nested schema](#nestedatt--urls))
### Nested Schema for `urls`
Read-Only:
- `disk_image` (String) The URL for the disk image.
- `disk_image_secureboot` (String) The URL for the disk image with secure boot.
- `initramfs` (String) The URL for the initramfs image.
- `installer` (String) The URL for the installer image.
- `installer_secureboot` (String) The URL for the installer image with secure boot.
- `iso` (String) The URL for the ISO image.
- `iso_secureboot` (String) The URL for the ISO image with secure boot.
- `kernel` (String) The URL for the kernel image.
- `kernel_command_line` (String) The URL for the kernel command line.
- `pxe` (String) The URL for the PXE image.
- `uki` (String) The URL for the UKI image.
================================================
FILE: docs/data-sources/image_factory_versions.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_image_factory_versions Data Source - talos"
subcategory: ""
description: |-
The image factory versions data source provides a list of available talos versions from the image factory.
---
# talos_image_factory_versions (Data Source)
The image factory versions data source provides a list of available talos versions from the image factory.
## Example Usage
```terraform
provider "talos" {}
data "talos_image_factory_versions" "this" {}
output "latest" {
value = element(data.talos_image_factory_versions.this.talos_versions, length(data.talos_image_factory_versions.this.talos_versions) - 1)
}
```
## Schema
### Optional
- `filters` (Attributes) The filter to apply to the overlays list. (see [below for nested schema](#nestedatt--filters))
### Read-Only
- `id` (String) The ID of this resource.
- `talos_versions` (List of String) The list of available talos versions.
### Nested Schema for `filters`
Optional:
- `stable_versions_only` (Boolean) If set to true, only stable versions will be returned. If set to false, all versions will be returned.
================================================
FILE: docs/data-sources/machine_configuration.md
================================================
---
page_title: "talos_machine_configuration Data Source - talos"
subcategory: ""
description: |-
Generate a machine configuration for a node type
---
# talos_machine_configuration (Data Source)
Generate a machine configuration for a node type
-> **Note:** It is recommended to set the optional `talos_version` attribute. Otherwise when using a new version of the provider with a new major version of the Talos SDK, new machineconfig features will be enabled by default which could cause unexpected behavior.
## Example Usage
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
```
## Schema
### Required
- `cluster_endpoint` (String) The endpoint of the talos kubernetes cluster
- `cluster_name` (String) The name of the talos kubernetes cluster
- `machine_secrets` (Attributes) The secrets for the talos cluster (see [below for nested schema](#nestedatt--machine_secrets))
- `machine_type` (String) The type of machine to generate the configuration for
### Optional
- `config_patches` (List of String) The list of config patches to apply to the generated configuration
- `docs` (Boolean) Whether to generate documentation for the generated configuration. Defaults to false
- `examples` (Boolean) Whether to generate examples for the generated configuration. Defaults to false
- `kubernetes_version` (String) The version of kubernetes to use
- `talos_version` (String) The Talos version contract used to generate the machine configuration. This does not control the installed Talos version. Use `config_patches` to set `machine.install.image` to the desired value. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`
### Read-Only
- `id` (String) The ID of this resource.
- `machine_configuration` (String, Sensitive) The generated machine configuration
### Nested Schema for `machine_secrets`
Required:
- `certs` (Attributes) The certs for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--certs))
- `cluster` (Attributes) The cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--cluster))
- `secrets` (Attributes) The secrets for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--secrets))
- `trustdinfo` (Attributes) The trustd info for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--trustdinfo))
### Nested Schema for `machine_secrets.certs`
Required:
- `etcd` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--etcd))
- `k8s` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s))
- `k8s_aggregator` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_aggregator))
- `k8s_serviceaccount` (Attributes) (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_serviceaccount))
- `os` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--os))
### Nested Schema for `machine_secrets.certs.etcd`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_aggregator`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_serviceaccount`
Required:
- `key` (String, Sensitive) The key for the k8s service account
### Nested Schema for `machine_secrets.certs.os`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.cluster`
Required:
- `id` (String) The cluster id
- `secret` (String, Sensitive) The cluster secret
### Nested Schema for `machine_secrets.secrets`
Required:
- `bootstrap_token` (String, Sensitive) The bootstrap token for the talos kubernetes cluster
- `secretbox_encryption_secret` (String, Sensitive) The secretbox encryption secret for the talos kubernetes cluster
Optional:
- `aescbc_encryption_secret` (String, Sensitive) The aescbc encryption secret for the talos kubernetes cluster
### Nested Schema for `machine_secrets.trustdinfo`
Required:
- `token` (String, Sensitive) The trustd token for the talos kubernetes cluster
================================================
FILE: docs/data-sources/machine_disks.md
================================================
---
page_title: "talos_machine_disks Data Source - talos"
subcategory: ""
description: |-
Generate a machine configuration for a node type
---
# talos_machine_disks (Data Source)
Generate a machine configuration for a node type
-> **Note:** Since Talos natively supports `.machine.install.diskSelector`, the `talos_machine_disks` data source maybe just used to query disk information that could be used elsewhere. It's recommended to use `machine.install.diskSelector` in Talos machine configuration.
## Example Usage
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_disks" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
selector = "disk.size > 6u * GB"
}
# for example, this could be used to pass in a list of disks to rook-ceph
output "nvme_disks" {
value = data.talos_machine_disks.this.disks.*.name
}
```
## Schema
### Required
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `node` (String) controlplane node to retrieve the kubeconfig from
### Optional
- `endpoint` (String) endpoint to use for the talosclient. If not set, the node value will be used
- `selector` (String) The CEL expression to filter the disks.
If not set, all disks will be returned.
See [CEL documentation](https://www.talos.dev/latest/talos-guides/configuration/disk-management/#disk-selector).
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
### Read-Only
- `disks` (Attributes List) The disks that match the filters (see [below for nested schema](#nestedatt--disks))
- `id` (String) The generated ID of this resource
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `timeouts`
Optional:
- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled.
### Nested Schema for `disks`
Read-Only:
- `bus_path` (String)
- `cdrom` (Boolean)
- `dev_path` (String)
- `io_size` (Number)
- `modalias` (String)
- `model` (String)
- `pretty_size` (String)
- `readonly` (Boolean)
- `rotational` (Boolean)
- `secondary_disks` (List of String)
- `sector_size` (Number)
- `serial` (String)
- `size` (Number)
- `sub_system` (String)
- `symlinks` (List of String)
- `transport` (String)
- `uuid` (String)
- `wwid` (String)
================================================
FILE: docs/ephemeral-resources/client_configuration.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_client_configuration Ephemeral Resource - talos"
subcategory: ""
description: |-
Generate client configuration for a Talos cluster from machine secrets. This is an ephemeral resource that does not persist secrets in Terraform state. The admin client certificate is generated with pinned timestamps so talos_config is byte-identical on every open as long as machine_secrets and not_before are unchanged.
---
# talos_client_configuration (Ephemeral Resource)
Generate client configuration for a Talos cluster from machine secrets. This is an ephemeral resource that does not persist secrets in Terraform state. The admin client certificate is generated with pinned timestamps so talos_config is byte-identical on every open as long as machine_secrets and not_before are unchanged.
## Schema
### Required
- `cluster_name` (String) The name of the cluster in the generated config
- `machine_secrets` (Attributes) The secrets for the talos cluster (see [below for nested schema](#nestedatt--machine_secrets))
### Optional
- `crt_ttl` (String) The lifetime of the generated admin client certificate as a Go duration string (e.g. "8760h" for 1 year, "87600h" for 10 years). Defaults to "87600h" (10 years). Only used when not_before is set; when not_before is omitted the cert uses the OS CA's NotAfter directly.
- `endpoints` (List of String) endpoints to set in the generated config
- `nodes` (List of String) nodes to set in the generated config
- `not_before` (String) RFC3339 timestamp to use as the NotBefore field of the generated admin client certificate. When set, the certificate validity starts at this time and ends at not_before + crt_ttl. Persist this value in a terraform_data resource so it is stable across plans and the generated talos_config is byte-identical on every open. When omitted, the certificate uses the OS CA's own NotBefore/NotAfter timestamps.
### Read-Only
- `client_configuration` (Attributes, Sensitive) The generated client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `talos_config` (String, Sensitive) The generated client configuration
### Nested Schema for `machine_secrets`
Required:
- `certs` (Attributes) The certs for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--certs))
- `cluster` (Attributes) The cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--cluster))
- `secrets` (Attributes) The secrets for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--secrets))
- `trustdinfo` (Attributes) The trustd info for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--trustdinfo))
### Nested Schema for `machine_secrets.certs`
Required:
- `etcd` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--etcd))
- `k8s` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s))
- `k8s_aggregator` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_aggregator))
- `k8s_serviceaccount` (Attributes) (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_serviceaccount))
- `os` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--os))
### Nested Schema for `machine_secrets.certs.etcd`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_aggregator`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_serviceaccount`
Required:
- `key` (String, Sensitive) The key for the k8s service account
### Nested Schema for `machine_secrets.certs.os`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.cluster`
Required:
- `id` (String) The cluster id
- `secret` (String, Sensitive) The cluster secret
### Nested Schema for `machine_secrets.secrets`
Required:
- `bootstrap_token` (String, Sensitive) The bootstrap token for the talos kubernetes cluster
- `secretbox_encryption_secret` (String, Sensitive) The secretbox encryption secret for the talos kubernetes cluster
Optional:
- `aescbc_encryption_secret` (String, Sensitive) The aescbc encryption secret for the talos kubernetes cluster
### Nested Schema for `machine_secrets.trustdinfo`
Required:
- `token` (String, Sensitive) The trustd token for the talos kubernetes cluster
### Nested Schema for `client_configuration`
Read-Only:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
================================================
FILE: docs/ephemeral-resources/cluster_health.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_cluster_health Ephemeral Resource - talos"
subcategory: ""
description: |-
Checks the health of a Talos cluster. This is an ephemeral resource that does not persist secrets in Terraform state.
---
# talos_cluster_health (Ephemeral Resource)
Checks the health of a Talos cluster. This is an ephemeral resource that does not persist secrets in Terraform state.
## Schema
### Required
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `control_plane_nodes` (List of String) List of control plane nodes to check for health.
- `endpoints` (List of String) endpoints to use for the health check client. Use at least one control plane endpoint.
### Optional
- `skip_kubernetes_checks` (Boolean) Skip Kubernetes component checks, this is useful to check if the nodes has finished booting up and kubelet is running. Default is false.
- `timeout` (String) Timeout for the health check. Defaults to 10m. Valid time units are 'ns', 'us' (or 'µs'), 'ms', 's', 'm', 'h'.
- `worker_nodes` (List of String) List of worker nodes to check for health.
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
================================================
FILE: docs/ephemeral-resources/cluster_kubeconfig.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_cluster_kubeconfig Ephemeral Resource - talos"
subcategory: ""
description: |-
Generate a kubeconfig for a Talos cluster from machine secrets. This is an ephemeral resource that does not persist secrets in Terraform state. The admin client certificate is generated with pinned timestamps so kubeconfig_raw is byte-identical on every open as long as machine_secrets and not_before are unchanged.
---
# talos_cluster_kubeconfig (Ephemeral Resource)
Generate a kubeconfig for a Talos cluster from machine secrets. This is an ephemeral resource that does not persist secrets in Terraform state. The admin client certificate is generated with pinned timestamps so kubeconfig_raw is byte-identical on every open as long as machine_secrets and not_before are unchanged.
## Example Usage
```terraform
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "example-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.5.0.2:6443"
}
# Recommended pattern for stable kubeconfig when storing in a secret manager:
# Persist not_before in terraform_data so the admin cert timestamps are fixed
# across plan invocations and kubeconfig_raw is byte-identical on every open.
resource "terraform_data" "kubeconfig_nbf" {
input = plantimestamp()
lifecycle {
ignore_changes = [input]
}
}
ephemeral "talos_cluster_kubeconfig" "stable" {
cluster_name = "example-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.5.0.2:6443"
not_before = terraform_data.kubeconfig_nbf.output
crt_ttl = "87600h"
}
```
## Schema
### Required
- `cluster_name` (String) The name of the cluster; embedded in the kubeconfig context and cluster names
- `endpoint` (String) The Kubernetes API server URL to embed in the kubeconfig (e.g. https://1.2.3.4:6443)
- `machine_secrets` (Attributes) The secrets for the talos cluster (see [below for nested schema](#nestedatt--machine_secrets))
### Optional
- `crt_ttl` (String) The lifetime of the generated admin client certificate as a Go duration string (e.g. "8760h" for 1 year, "87600h" for 10 years). Defaults to "87600h" (10 years). Only used when not_before is set; when not_before is omitted the cert uses the K8s CA's NotAfter directly.
- `not_before` (String) RFC3339 timestamp to use as the NotBefore field of the generated admin client certificate. When set, the certificate validity starts at this time and ends at not_before + crt_ttl. Persist this value in a terraform_data resource so it is stable across plans and the generated kubeconfig_raw is byte-identical on every open. When omitted, the certificate uses the K8s CA's own NotBefore/NotAfter timestamps.
### Read-Only
- `kubeconfig_raw` (String, Sensitive) The raw kubeconfig
- `kubernetes_client_configuration` (Attributes) The kubernetes client configuration (see [below for nested schema](#nestedatt--kubernetes_client_configuration))
### Nested Schema for `machine_secrets`
Required:
- `certs` (Attributes) The certs for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--certs))
- `cluster` (Attributes) The cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--cluster))
- `secrets` (Attributes) The secrets for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--secrets))
- `trustdinfo` (Attributes) The trustd info for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--trustdinfo))
### Nested Schema for `machine_secrets.certs`
Required:
- `etcd` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--etcd))
- `k8s` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s))
- `k8s_aggregator` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_aggregator))
- `k8s_serviceaccount` (Attributes) (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_serviceaccount))
- `os` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--os))
### Nested Schema for `machine_secrets.certs.etcd`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_aggregator`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_serviceaccount`
Required:
- `key` (String, Sensitive) The key for the k8s service account
### Nested Schema for `machine_secrets.certs.os`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.cluster`
Required:
- `id` (String) The cluster id
- `secret` (String, Sensitive) The cluster secret
### Nested Schema for `machine_secrets.secrets`
Required:
- `bootstrap_token` (String, Sensitive) The bootstrap token for the talos kubernetes cluster
- `secretbox_encryption_secret` (String, Sensitive) The secretbox encryption secret for the talos kubernetes cluster
Optional:
- `aescbc_encryption_secret` (String, Sensitive) The aescbc encryption secret for the talos kubernetes cluster
### Nested Schema for `machine_secrets.trustdinfo`
Required:
- `token` (String, Sensitive) The trustd token for the talos kubernetes cluster
### Nested Schema for `kubernetes_client_configuration`
Read-Only:
- `ca_certificate` (String) The kubernetes CA certificate
- `client_certificate` (String) The kubernetes client certificate
- `client_key` (String, Sensitive) The kubernetes client key
- `host` (String) The kubernetes host
================================================
FILE: docs/ephemeral-resources/machine_configuration.md
================================================
---
page_title: "talos_machine_configuration Ephemeral Resource - talos"
subcategory: ""
description: |-
Generate a machine configuration for a node type. This is an ephemeral resource that does not persist secrets in Terraform state.
---
# talos_machine_configuration (Ephemeral Resource)
Generate a machine configuration for a node type. This is an ephemeral resource that does not persist secrets in Terraform state.
-> **Note:** It is recommended to set the optional `talos_version` attribute. Otherwise when using a new version of the provider with a new major version of the Talos SDK, new machineconfig features will be enabled by default which could cause unexpected behavior.
## Schema
### Required
- `cluster_endpoint` (String) The endpoint of the talos kubernetes cluster
- `cluster_name` (String) The name of the talos kubernetes cluster
- `machine_secrets` (Attributes) The secrets for the talos cluster (see [below for nested schema](#nestedatt--machine_secrets))
- `machine_type` (String) The type of machine to generate the configuration for
### Optional
- `config_patches` (List of String) The list of config patches to apply to the generated configuration
- `docs` (Boolean) Whether to generate documentation for the generated configuration. Defaults to false
- `examples` (Boolean) Whether to generate examples for the generated configuration. Defaults to false
- `kubernetes_version` (String) The version of kubernetes to use
- `talos_version` (String) The Talos version contract used to generate the machine configuration. This does not control the installed Talos version. Use `config_patches` to set `machine.install.image` to the desired value. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`
### Read-Only
- `machine_configuration` (String, Sensitive) The generated machine configuration
### Nested Schema for `machine_secrets`
Required:
- `certs` (Attributes) The certs for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--certs))
- `cluster` (Attributes) The cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--cluster))
- `secrets` (Attributes) The secrets for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--secrets))
- `trustdinfo` (Attributes) The trustd info for the talos kubernetes cluster (see [below for nested schema](#nestedatt--machine_secrets--trustdinfo))
### Nested Schema for `machine_secrets.certs`
Required:
- `etcd` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--etcd))
- `k8s` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s))
- `k8s_aggregator` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_aggregator))
- `k8s_serviceaccount` (Attributes) (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_serviceaccount))
- `os` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--os))
### Nested Schema for `machine_secrets.certs.etcd`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_aggregator`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_serviceaccount`
Required:
- `key` (String, Sensitive) The key for the k8s service account
### Nested Schema for `machine_secrets.certs.os`
Required:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.cluster`
Required:
- `id` (String) The cluster id
- `secret` (String, Sensitive) The cluster secret
### Nested Schema for `machine_secrets.secrets`
Required:
- `bootstrap_token` (String, Sensitive) The bootstrap token for the talos kubernetes cluster
- `secretbox_encryption_secret` (String, Sensitive) The secretbox encryption secret for the talos kubernetes cluster
Optional:
- `aescbc_encryption_secret` (String, Sensitive) The aescbc encryption secret for the talos kubernetes cluster
### Nested Schema for `machine_secrets.trustdinfo`
Required:
- `token` (String, Sensitive) The trustd token for the talos kubernetes cluster
================================================
FILE: docs/ephemeral-resources/machine_secrets.md
================================================
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "talos_machine_secrets Ephemeral Resource - talos"
subcategory: ""
description: |-
Generate machine secrets for Talos cluster. This is an ephemeral resource that does not persist secrets in Terraform state.
---
# talos_machine_secrets (Ephemeral Resource)
Generate machine secrets for Talos cluster. This is an ephemeral resource that does not persist secrets in Terraform state.
## Schema
### Optional
- `talos_version` (String) The Talos version contract used to generate the secrets. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`
### Read-Only
- `client_configuration` (Attributes) The generated client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `machine_secrets` (Attributes) The secrets for the talos cluster (see [below for nested schema](#nestedatt--machine_secrets))
### Nested Schema for `client_configuration`
Read-Only:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `machine_secrets`
Read-Only:
- `certs` (Attributes) (see [below for nested schema](#nestedatt--machine_secrets--certs))
- `cluster` (Attributes) The cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--cluster))
- `secrets` (Attributes) kubernetes cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--secrets))
- `trustdinfo` (Attributes) trustd secrets (see [below for nested schema](#nestedatt--machine_secrets--trustdinfo))
### Nested Schema for `machine_secrets.certs`
Read-Only:
- `etcd` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--etcd))
- `k8s` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s))
- `k8s_aggregator` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_aggregator))
- `k8s_serviceaccount` (Attributes) The service account secrets (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_serviceaccount))
- `os` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--os))
### Nested Schema for `machine_secrets.certs.etcd`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_aggregator`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_serviceaccount`
Read-Only:
- `key` (String, Sensitive) The service account key
### Nested Schema for `machine_secrets.certs.os`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.cluster`
Read-Only:
- `id` (String) The cluster ID
- `secret` (String, Sensitive) The cluster secret
### Nested Schema for `machine_secrets.secrets`
Read-Only:
- `aescbc_encryption_secret` (String, Sensitive) The AES-CBC encryption secret
- `bootstrap_token` (String, Sensitive) The bootstrap token
- `secretbox_encryption_secret` (String, Sensitive) The secretbox encryption secret
### Nested Schema for `machine_secrets.trustdinfo`
Read-Only:
- `token` (String, Sensitive) The trustd token
================================================
FILE: docs/guides/using_ephemeral_resources.md
================================================
---
page_title: "Using Ephemeral Resources - talos Provider"
subcategory: ""
description: |-
Learn how to use ephemeral resources in the Talos provider to prevent secrets from being stored in Terraform state
---
# Using Ephemeral Resources in the Talos Provider
Ephemeral resources are Terraform resources that are essentially temporary. They allow you to access and use data in your configurations without that data being stored in Terraform state. This is particularly important for sensitive data like machine secrets, certificates, and kubeconfig files.
Ephemeral resources are available in Terraform v1.10 and later. For more information, see the [official HashiCorp documentation for Ephemeral Resources](https://developer.hashicorp.com/terraform/language/resources/ephemeral).
## Available Ephemeral Resources
The Talos provider includes five ephemeral resources:
- [`talos_machine_secrets`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/machine_secrets) - Generate machine secrets without storing them in state
- [`talos_machine_configuration`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/machine_configuration) - Generate machine configuration without storing secrets in state
- [`talos_client_configuration`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/client_configuration) - Generate client configuration (talosconfig) without storing credentials in state
- [`talos_cluster_kubeconfig`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/cluster_kubeconfig) - Retrieve kubeconfig without storing credentials in state
- [`talos_cluster_health`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/cluster_health) - Check cluster health without storing credentials in state
These complement the existing data sources and resources, allowing you to avoid storing credentials and secret values in your Terraform state.
## Why Use Ephemeral Resources?
**Security Benefits:**
- Secrets never written to Terraform state files
- Reduces risk of credential exposure through state files
- Complies with security policies requiring secret-free state
**When to Use:**
- Generating Talos machine secrets
- Creating machine configurations with sensitive data
- Retrieving kubeconfig files
- Any workflow where secrets shouldn't persist in state
## Critical: Machine Secrets Persistence
**IMPORTANT**: Do not use `ephemeral "talos_machine_secrets"` without also storing them in a secret manager. Generating ephemeral machine secrets without persistence would create **new secrets on every Terraform run**, causing:
- Unpredictable changes to dependent resources
- Need to reconfigure all cluster nodes
- Loss of access to the cluster with previous credentials
- Non-deterministic infrastructure state
### Correct Pattern: Secret Manager Integration
Machine secrets should be:
1. Generated once and stored in a secret manager (Vault, AWS Secrets Manager, etc.)
2. Retrieved ephemerally from the secret manager when needed
3. Used to generate machine configurations deterministically
This ensures:
- Secrets remain stable across Terraform runs
- No secrets stored in Terraform state
- Deterministic, reproducible infrastructure
- Compliance with security policies
## Using Ephemeral Resources with Write-Only Attributes
Ephemeral resources are a source of ephemeral data, and they can be referenced in your configuration just like the attributes of resources and data sources. However, a field that references an ephemeral resource must be capable of handling ephemeral data.
The Talos provider includes write-only attributes that accept ephemeral values:
- `machine_configuration_input_wo` - Write-only alternative to `machine_configuration_input` on `talos_machine_configuration_apply` resource (requires Terraform 1.11+)
## Example: Using Vault for Secret Persistence
This example demonstrates the correct pattern for managing Talos machine secrets with Vault. Both secret generation and retrieval can coexist in the same configuration:
```terraform
terraform {
required_version = ">= 1.11"
required_providers {
talos = {
source = "siderolabs/talos"
version = "~> 0.11"
}
vault = {
source = "hashicorp/vault"
version = "~> 5.0"
}
}
}
# Step 1: Generate and store secrets in Vault
# The ephemeral resource generates secrets only when needed (first run)
# After initial creation, this won't be evaluated because data_json_wo_version is hardcoded
ephemeral "talos_machine_secrets" "this" {}
resource "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-${var.cluster_name}"
# Write-only attributes prevent secrets from being stored in Terraform state
data_json_wo = jsonencode({
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
client_configuration = ephemeral.talos_machine_secrets.this.client_configuration
})
# Hardcoded version prevents unnecessary refreshes after initial creation
data_json_wo_version = 1
}
# Step 2: Retrieve secrets ephemerally from Vault
# This runs on every terraform operation but values are never stored in state
# Referencing the resource attributes creates implicit dependency on the secret
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = vault_kv_secret_v2.talos_secrets.mount
name = vault_kv_secret_v2.talos_secrets.name
}
locals {
# Decode the secret data
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
# Step 3: Generate machine configuration using retrieved secrets
ephemeral "talos_machine_configuration" "controlplane" {
cluster_name = var.cluster_name
cluster_endpoint = var.cluster_endpoint
machine_type = "controlplane"
machine_secrets = local.talos_data.machine_secrets
}
# Step 4: Apply configuration using write-only input
resource "talos_machine_configuration_apply" "controlplane" {
client_configuration_wo = local.talos_data.client_configuration
machine_configuration_input_wo = ephemeral.talos_machine_configuration.controlplane.machine_configuration
node = var.controlplane_node
# Note: machine_configuration computed attribute will be null in state
# This is expected behavior for secret-free operation
}
```
**Secret-Free Operation:**
When using write-only attributes (`_wo` variants), the provider ensures zero secrets leak into state:
- **Write-only inputs** (`client_configuration_wo`, `machine_configuration_input_wo`): Never stored in state
- **Computed outputs** (`machine_configuration`): Automatically set to null in state when using write-only inputs
The provider computes the machine configuration internally during apply operations without persisting it. This provides complete secret-free operation while maintaining full functionality.
**How This Works:**
1. **First Run**:
- Secrets are generated ephemerally
- Stored in Vault with version 1
- Retrieved from Vault for immediate use
- All in a single `terraform apply` (Terraform handles the ordering automatically)
2. **Subsequent Runs**:
- The `vault_kv_secret_v2` resource doesn't need updates (version is hardcoded)
- The `ephemeral "talos_machine_secrets"` isn't evaluated (no dependent resources need it)
- Secrets are retrieved ephemerally from Vault for use in configuration
**Key Benefits:**
- Works in a single run on first apply
- Both blocks coexist permanently in your configuration
- Terraform handles all dependencies automatically
- No secrets stored in Terraform state
### Alternative Pattern: External Secret Generation
If you prefer to manage secret generation outside Terraform:
```bash
# Generate secrets manually using talosctl
talosctl gen secrets -o secrets.yaml
# Store in Vault using vault CLI
vault kv put secret/talos-cluster-prod \
machine_secrets="$(yq -o=json '.machine_secrets' secrets.yaml)" \
client_configuration="$(yq -o=json '.client_configuration' secrets.yaml)"
```
Then your Terraform configuration only needs the retrieval part:
```terraform
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-prod"
}
locals {
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
ephemeral "talos_machine_configuration" "controlplane" {
cluster_name = "prod-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
machine_secrets = local.talos_data.machine_secrets
}
```
## Example: Generating Kubeconfig Ephemerally from Machine Secrets
This example shows how to generate a kubeconfig ephemerally from machine secrets stored in Vault.
The kubeconfig is generated locally from the Kubernetes CA key — no live cluster required.
### Simple usage (CA-pinned timestamps)
When `not_before` is omitted, the admin certificate validity window is taken from the K8s CA's
own timestamps (set once when the cluster was created). The output is byte-identical on every
plan as long as `machine_secrets` does not change.
```terraform
# Retrieve stored secrets from Vault
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-prod"
}
locals {
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
# Generate kubeconfig without storing in state
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "prod-cluster"
machine_secrets = local.talos_data.machine_secrets
endpoint = "https://10.5.0.2:6443"
}
# Output the kubeconfig (marked as ephemeral)
output "kubeconfig" {
value = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
sensitive = true
ephemeral = true
}
```
### Recommended pattern for Vault-backed workflows (explicit `not_before`)
When storing `kubeconfig_raw` in a Vault KV secret (or any resource that detects byte changes),
use a `terraform_data` resource to persist a stable `not_before` timestamp in Terraform state.
This pins the admin certificate validity window so `kubeconfig_raw` is byte-identical across
all plan invocations — no `ignore_changes` or `data_json_wo_version` bumps required until you
explicitly rotate the certificate.
```terraform
# Persist the admin cert NotBefore timestamp in regular Terraform state.
# Use ignore_changes so it is set once and never updated automatically.
# To rotate the cert: taint this resource and re-apply.
resource "terraform_data" "kubeconfig_nbf" {
input = plantimestamp()
lifecycle {
ignore_changes = [input]
}
}
# Generate kubeconfig with pinned timestamps
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "prod-cluster"
machine_secrets = local.talos_data.machine_secrets
endpoint = "https://10.5.0.2:6443"
not_before = terraform_data.kubeconfig_nbf.output
crt_ttl = "87600h"
}
# Store kubeconfig in Vault — kubeconfig_raw is stable so this resource
# only updates when machine_secrets or not_before change.
resource "vault_kv_secret_v2" "kubeconfig" {
mount = "secret"
name = "kubeconfig-prod-cluster"
data_json_wo = jsonencode({ kubeconfig = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw })
data_json_wo_version = 1
}
```
**Certificate rotation**: taint `terraform_data.kubeconfig_nbf` to force a new `not_before`,
which produces a new cert and triggers a `data_json_wo_version` bump on the Vault secret.
**Note**: The kubeconfig is generated locally from the machine secrets and does not require
a running cluster.
## Alternative Secret Managers
While the examples above use HashiCorp Vault, you can use any secret manager that supports:
- Storing secrets via Terraform resources
- Retrieving secrets via ephemeral resources
### AWS Secrets Manager Example
```terraform
# Store secrets in AWS Secrets Manager
resource "aws_secretsmanager_secret" "talos_secrets" {
name = "talos-cluster-${var.cluster_name}"
}
resource "aws_secretsmanager_secret_version" "talos_secrets" {
secret_id = aws_secretsmanager_secret.talos_secrets.id
secret_string = jsonencode({
machine_secrets = talos_machine_secrets.this.machine_secrets
client_configuration = talos_machine_secrets.this.client_configuration
})
}
# Note: AWS provider doesn't yet have ephemeral resources for Secrets Manager
# You would use a data source, which still stores in state
# Watch for AWS provider updates adding ephemeral support
```
### Azure Key Vault Example
```terraform
# Store secrets in Azure Key Vault
resource "azurerm_key_vault_secret" "talos_secrets" {
name = "talos-cluster-${var.cluster_name}"
value = jsonencode({
machine_secrets = talos_machine_secrets.this.machine_secrets
client_configuration = talos_machine_secrets.this.client_configuration
})
key_vault_id = azurerm_key_vault.main.id
}
# Note: Azure provider doesn't yet have ephemeral resources for Key Vault
# You would use a data source for now
```
## Important Considerations
### Terraform Version Requirements
- **Terraform 1.10+**: Supports ephemeral resources only (no write-only attributes)
- **Terraform 1.11+**: Supports both ephemeral resources and write-only attributes
- **OpenTofu 1.11+**: Supports both ephemeral resources and write-only attributes
- Note: OpenTofu 1.10 does NOT support ephemeral resources (they were introduced in 1.11)
**For the examples in this guide**: Terraform 1.11+ or OpenTofu 1.11+ required (uses write-only attributes)
### Compatibility with Existing Resources
Ephemeral resources complement existing data sources and resources:
- **Data sources** (e.g., `data.talos_machine_configuration`) - Still work, but store output in state
- **Ephemeral resources** (e.g., `ephemeral.talos_machine_configuration`) - Same functionality, no state storage
You can migrate existing configurations to ephemeral resources by:
1. Changing `data "talos_machine_configuration"` to `ephemeral "talos_machine_configuration"`
2. Updating references from `data.talos_machine_configuration.this` to `ephemeral.talos_machine_configuration.this`
3. Using write-only attributes (e.g., `machine_configuration_input_wo`) where applicable
## Migration Guide
### From Data Source to Ephemeral Resource with Vault
**Before (using data source):**
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "my-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
}
```
**After (using ephemeral resources with Vault):**
Step 1 - Store existing secrets in Vault (one-time migration):
```terraform
# Assuming you have existing talos_machine_secrets resource
resource "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-my-cluster"
data_json_wo = jsonencode({
machine_secrets = talos_machine_secrets.this.machine_secrets
client_configuration = talos_machine_secrets.this.client_configuration
})
}
```
Step 2 - Use ephemeral resources to retrieve from Vault:
```terraform
# Retrieve secrets from Vault ephemerally
# Reference the resource to create implicit dependency
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = vault_kv_secret_v2.talos_secrets.mount
name = vault_kv_secret_v2.talos_secrets.name
}
locals {
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
# Generate configuration ephemerally
ephemeral "talos_machine_configuration" "this" {
cluster_name = "my-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
machine_secrets = local.talos_data.machine_secrets
}
# Apply configuration using write-only attribute
resource "talos_machine_configuration_apply" "this" {
client_configuration_wo = local.talos_data.client_configuration
machine_configuration_input_wo = ephemeral.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
}
```
Step 3 - After verifying the migration works, remove the `talos_machine_secrets` resource from your state:
```bash
terraform state rm talos_machine_secrets.this
```
**Benefits:**
- Machine secrets stored securely in Vault, not in Terraform state
- Secrets remain stable across Terraform runs
- Machine configuration never stored in state
- Deterministic infrastructure state
- Improved security and compliance
================================================
FILE: docs/guides/version-0.2-upgrade.html.md
================================================
---
page_title: "Terraform Talos Provider Version 0.2 Upgrade Guide"
description: |-
Terraform Talos Provider Version 0.2 Upgrade Guide
---
# Terraform Talos Provider Version 0.2 Upgrade Guide
Version 0.2 of the Talos Terraform provider is a major release and include some breaking chages. This guide will walk you through the changes and how to upgrade your Terraform configuration.
~> **NOTE:** Version 0.2 of the Talos Terraform provider drops support for the following resources:
> * `talos_client_configuration`
> * `talos_cluster_kubeconfig`
> * `talos_machine_configuration_controlplane`
> * `talos_machine_configuration_worker`
The following table lists the resources that have been removed and the new resources that replace them.
| Removed Resource | Type | New Resource | Type |
| ------------------------------------------ | -------- | ----------------------------- | ------------- |
| `talos_client_configuration` | Resource | `talos_client_configuration` | Data Source |
| `talos_cluster_kubeconfig` | Resource | `talos_cluster_kubeconfig` | Data Source |
| `talos_machine_configuration_controlplane` | Resource | `talos_machine_configuration` | Data Resource |
| `talos_machine_configuration_worker` | Resource | `talos_machine_configuration` | Data Resource |
## Upgrade topics:
- [Upgrading `talos_client_configuration` resource](#upgrading-talos_client_configuration-resource)
- [Upgrading `talos_cluster_kubeconfig` resource](#upgrading-talos_cluster_kubeconfig-resource)
- [Upgrading `talos_machine_configuration_controlplane` resource](#upgrading-talos_machine_configuration_controlplane-resource)
- [Upgrading `talos_machine_configuration_worker` resource](#upgrading-talos_machine_configuration_worker-resource)
### Upgrading `talos_client_configuration` resource
The `talos_client_configuration` resource has been removed. The `talos_client_configuration` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "talosconfig" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
```
`talos_client_configuration` resource should be first removed from the state:
```bash
terraform state rm talos_client_configuration.talosconfig
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
}
```
### Upgrading `talos_cluster_kubeconfig` resource
The `talos_cluster_kubeconfig` resource has been removed. The `talos_cluster_kubeconfig` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_cluster_kubeconfig" "kubeconfig" {
talos_config = talos_client_configuration.this.talos_config
endpoint = "10.5.0.2"
node = "10.5.0.2"
}
```
`talos_cluster_kubeconfig` resource should be first removed from the state:
```bash
terraform state rm talos_cluster_kubeconfig.kubeconfig
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_cluster_kubeconfig" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
}
```
### Upgrading `talos_machine_configuration_controlplane` resource
The `talos_machine_configuration_controlplane` resource has been removed. The `talos_machine_configuration` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_controlplane" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
talos_config = talos_client_configuration.this.talos_config
machine_configuration = talos_machine_configuration_controlplane.this.machine_config
node = "10.5.0.2"
endpoint = "10.5.0.2"
}
```
`talos_machine_configuration_controlplane` resource should be first removed from the state:
```bash
terraform state rm talos_machine_configuration_controlplane.cp
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
talos_version = talos_machine_secrets.this.talos_version
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
}
```
### Upgrading `talos_machine_configuration_worker` resource
The `talos_machine_configuration_worker` resource has been removed. The `talos_machine_configuration` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_worker" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
talos_config = talos_client_configuration.this.talos_config
machine_configuration = talos_machine_configuration_worker.this.machine_config
node = "10.5.0.3"
endpoint = "10.5.0.3"
}
```
`talos_machine_configuration_worker` resource should be first removed from the state:
```bash
terraform state rm talos_machine_configuration_worker.worker
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "worker"
talos_version = talos_machine_secrets.this.talos_version
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.3"
}
```
================================================
FILE: docs/index.md
================================================
---
page_title: "Provider: Talos"
description: |-
The Talos provider is used to manage a Talos cluster config generation and initial setup.
---
# Talos Provider
Talos provider allows to generate configs for a Talos cluster and apply them to the nodes, bootstrap nodes, check cluster health, and retrieve `kubeconfig` and `talosconfig`.
Complete usages for this provider across a variety of environments can be found [here](https://github.com/siderolabs/contrib/tree/main/examples/terraform).
================================================
FILE: docs/resources/cluster_kubeconfig.md
================================================
---
page_title: "talos_cluster_kubeconfig Resource - talos"
subcategory: ""
description: |-
Retrieves the kubeconfig for a Talos cluster
---
# talos_cluster_kubeconfig (Resource)
Retrieves the kubeconfig for a Talos cluster
## Example Usage
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
}
}
})
]
}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = "10.5.0.2"
client_configuration = talos_machine_secrets.this.client_configuration
}
resource "talos_cluster_kubeconfig" "this" {
depends_on = [
talos_machine_bootstrap.this
]
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
}
```
## Schema
### Required
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `node` (String) controlplane node to retrieve the kubeconfig from
### Optional
- `certificate_renewal_duration` (String) The duration in hours before the certificate is renewed, defaults to 720h. Must be a valid duration string
- `endpoint` (String) endpoint to use for the talosclient. If not set, the node value will be used
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
### Read-Only
- `id` (String) The ID of this resource.
- `kubeconfig_raw` (String, Sensitive) The raw kubeconfig
- `kubernetes_client_configuration` (Attributes) The kubernetes client configuration (see [below for nested schema](#nestedatt--kubernetes_client_configuration))
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `timeouts`
Optional:
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
### Nested Schema for `kubernetes_client_configuration`
Read-Only:
- `ca_certificate` (String) The kubernetes CA certificate
- `client_certificate` (String) The kubernetes client certificate
- `client_key` (String, Sensitive) The kubernetes client key
- `host` (String) The kubernetes host
================================================
FILE: docs/resources/image_factory_schematic.md
================================================
---
page_title: "talos_image_factory_schematic Resource - talos"
subcategory: ""
description: |-
The image factory schematic resource allows you to create a schematic for a Talos image.
---
# talos_image_factory_schematic (Resource)
The image factory schematic resource allows you to create a schematic for a Talos image.
## Example Usage
```terraform
provider "talos" {}
data "talos_image_factory_extensions_versions" "this" {
# get the latest talos version
talos_version = "v1.7.5"
filters = {
names = [
"amdgpu",
"tailscale",
]
}
}
resource "talos_image_factory_schematic" "this" {
schematic = yamlencode(
{
customization = {
systemExtensions = {
officialExtensions = data.talos_image_factory_extensions_versions.this.extensions_info.*.name
}
}
}
)
}
output "schematic_id" {
value = talos_image_factory_schematic.this.id
}
```
## Schema
### Optional
- `schematic` (String) The schematic yaml respresentation to generate the image.
If not set, a vanilla Talos image schematic will be generated.
> Refer to [image-factory](https://github.com/siderolabs/image-factory?tab=readme-ov-file#post-schematics) for the schema.
### Read-Only
- `id` (String) The unique ID of the schematic, returned from Image Factory.
================================================
FILE: docs/resources/machine_bootstrap.md
================================================
---
page_title: "talos_machine_bootstrap Resource - talos"
subcategory: ""
description: |-
The machine bootstrap resource allows you to bootstrap a Talos node.
---
# talos_machine_bootstrap (Resource)
The machine bootstrap resource allows you to bootstrap a Talos node.
## Example Usage
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
}
}
})
]
}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = "10.5.0.2"
client_configuration = talos_machine_secrets.this.client_configuration
}
```
## Schema
### Required
- `node` (String) The name of the node to bootstrap
### Optional
> **NOTE**: [Write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments) are supported in Terraform 1.11 and later.
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `client_configuration_wo` (Attributes, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client configuration data (write-only). Use this instead of client_configuration when using ephemeral resources. Requires Terraform 1.11+ (see [below for nested schema](#nestedatt--client_configuration_wo))
- `endpoint` (String) The endpoint of the machine to bootstrap
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
### Read-Only
- `id` (String) This is a unique identifier for the machine
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `client_configuration_wo`
Required:
- `ca_certificate` (String, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client CA certificate
- `client_certificate` (String, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client certificate
- `client_key` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client key
### Nested Schema for `timeouts`
Optional:
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
## Import
Import is supported using the following syntax:
```terraform
# machine bootstrap can be imported to let terraform know that the machine is already bootstrapped
terraform import talos_machine_bootstrap.this
```
================================================
FILE: docs/resources/machine_configuration_apply.md
================================================
---
page_title: "talos_machine_configuration_apply Resource - talos"
subcategory: ""
description: |-
The machine configuration apply resource allows to apply machine configuration to a node
---
# talos_machine_configuration_apply (Resource)
The machine configuration apply resource allows to apply machine configuration to a node
## Example Usage
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
image = "ghcr.io/siderolabs/installer:v1.12.6"
}
}
})
]
}
```
## Schema
### Required
- `node` (String) The name of the node to bootstrap
### Optional
> **NOTE**: [Write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments) are supported in Terraform 1.11 and later.
- `apply_mode` (String) The mode of the apply operation. Use 'staged_if_needing_reboot' for automatic reboot prevention: performs a dry-run and uses 'staged' mode if reboot is needed, 'auto' otherwise
- `client_configuration` (Attributes) The client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `client_configuration_wo` (Attributes, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client configuration data (write-only). Use this instead of client_configuration when using ephemeral resources. Requires Terraform 1.11+ (see [below for nested schema](#nestedatt--client_configuration_wo))
- `config_patches` (List of String) The list of config patches to apply
- `endpoint` (String) The endpoint of the machine to bootstrap
- `machine_configuration_input` (String, Sensitive) The machine configuration to apply
- `machine_configuration_input_wo` (String, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The machine configuration to apply (write-only). Use this instead of machine_configuration_input when using ephemeral resources. Requires Terraform 1.11+
- `on_destroy` (Attributes) Actions to be taken on destroy, if *reset* is not set this is a no-op.
> Note: Any changes to *on_destroy* block has to be applied first by running *terraform apply* first,
then a subsequent *terraform destroy* for the changes to take effect due to limitations in Terraform provider framework. (see [below for nested schema](#nestedatt--on_destroy))
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
### Read-Only
- `id` (String) This is a unique identifier for the machine
- `machine_configuration` (String, Sensitive) The generated machine configuration after applying patches
- `machine_configuration_hash` (String) SHA256 hex digest of the rendered machine configuration (input plus patches). Persisted in state so that changes to machine_configuration_input_wo — which is write-only and itself invisible to state — still surface as plan diffs.
- `resolved_apply_mode` (String) The actual apply mode used. When apply_mode is 'staged_if_needing_reboot', shows the resolved mode ('auto' or 'staged') based on dry-run analysis. Equals apply_mode for other modes.
### Nested Schema for `client_configuration`
Required:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `client_configuration_wo`
Required:
- `ca_certificate` (String, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client CA certificate
- `client_certificate` (String, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client certificate
- `client_key` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) The client key
### Nested Schema for `on_destroy`
Optional:
- `graceful` (Boolean) Graceful indicates whether node should leave etcd before the upgrade, it also enforces etcd checks before leaving. Default true
- `reboot` (Boolean) Reboot indicates whether node should reboot or halt after resetting. Default false
- `reset` (Boolean) Reset the machine to the initial state (STATE and EPHEMERAL will be wiped). Default false
### Nested Schema for `timeouts`
Optional:
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
- `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs.
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
================================================
FILE: docs/resources/machine_secrets.md
================================================
---
page_title: "talos_machine_secrets Resource - talos"
subcategory: ""
description: |-
Generate machine secrets for Talos cluster.
---
# talos_machine_secrets (Resource)
Generate machine secrets for Talos cluster.
## Example Usage
```terraform
resource "talos_machine_secrets" "machine_secrets" {}
```
## Schema
### Optional
- `talos_version` (String) The Talos version contract used to generate the secrets. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`
### Read-Only
- `client_configuration` (Attributes) The generated client configuration data (see [below for nested schema](#nestedatt--client_configuration))
- `id` (String) The computed ID of the Talos cluster
- `machine_secrets` (Attributes) The secrets for the talos cluster (see [below for nested schema](#nestedatt--machine_secrets))
### Nested Schema for `client_configuration`
Read-Only:
- `ca_certificate` (String) The client CA certificate
- `client_certificate` (String) The client certificate
- `client_key` (String, Sensitive) The client key
### Nested Schema for `machine_secrets`
Read-Only:
- `certs` (Attributes) (see [below for nested schema](#nestedatt--machine_secrets--certs))
- `cluster` (Attributes) The cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--cluster))
- `secrets` (Attributes) kubernetes cluster secrets (see [below for nested schema](#nestedatt--machine_secrets--secrets))
- `trustdinfo` (Attributes) trustd secrets (see [below for nested schema](#nestedatt--machine_secrets--trustdinfo))
### Nested Schema for `machine_secrets.certs`
Read-Only:
- `etcd` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--etcd))
- `k8s` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s))
- `k8s_aggregator` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_aggregator))
- `k8s_serviceaccount` (Attributes) The service account secrets (see [below for nested schema](#nestedatt--machine_secrets--certs--k8s_serviceaccount))
- `os` (Attributes) The certificate and key pair (see [below for nested schema](#nestedatt--machine_secrets--certs--os))
### Nested Schema for `machine_secrets.certs.etcd`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_aggregator`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.certs.k8s_serviceaccount`
Read-Only:
- `key` (String, Sensitive) The service account key
### Nested Schema for `machine_secrets.certs.os`
Read-Only:
- `cert` (String) certificate data
- `key` (String, Sensitive) key data
### Nested Schema for `machine_secrets.cluster`
Read-Only:
- `id` (String) The cluster ID
- `secret` (String, Sensitive) The cluster secret
### Nested Schema for `machine_secrets.secrets`
Read-Only:
- `aescbc_encryption_secret` (String, Sensitive) The AES-CBC encryption secret
- `bootstrap_token` (String, Sensitive) The bootstrap token
- `secretbox_encryption_secret` (String, Sensitive) The secretbox encryption secret
### Nested Schema for `machine_secrets.trustdinfo`
Read-Only:
- `token` (String, Sensitive) The trustd token
## Import
Import is supported using the following syntax:
```terraform
# machine secrets can be imported from an existing secrets file
terraform import talos_machine_secrets.this
```
================================================
FILE: examples/README.md
================================================
# Talos provider examples
This directory contains a set of examples on using the Talos provider.
================================================
FILE: examples/data-sources/talos_client_configuration/data-source.tf
================================================
resource "talos_machine_secrets" "this" {}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
================================================
FILE: examples/data-sources/talos_cluster_kubeconfig/data-source.tf
================================================
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
}
}
})
]
}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = "10.5.0.2"
client_configuration = talos_machine_secrets.this.client_configuration
}
data "talos_cluster_kubeconfig" "this" {
depends_on = [
talos_machine_bootstrap.this
]
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
}
================================================
FILE: examples/data-sources/talos_image_factory_extensions_versions/data-source.tf
================================================
provider "talos" {}
data "talos_image_factory_extensions_versions" "this" {
# get the latest talos version
talos_version = "v1.7.5"
filters = {
names = [
"amdgpu",
"tailscale",
]
}
}
================================================
FILE: examples/data-sources/talos_image_factory_overlays_versions/data-source.tf
================================================
provider "talos" {}
data "talos_image_factory_overlays_versions" "this" {
# get the latest talos version
talos_version = "v1.7.5"
filters = {
name = "rock4cplus"
}
}
================================================
FILE: examples/data-sources/talos_image_factory_urls/data-source.tf
================================================
data "talos_image_factory_urls" "this" {
talos_version = "v1.7.5"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "metal"
}
output "installer_image" {
value = data.talos_image_factory_urls.this.urls.installer
}
================================================
FILE: examples/data-sources/talos_image_factory_versions/data-source.tf
================================================
provider "talos" {}
data "talos_image_factory_versions" "this" {}
output "latest" {
value = element(data.talos_image_factory_versions.this.talos_versions, length(data.talos_image_factory_versions.this.talos_versions) - 1)
}
================================================
FILE: examples/data-sources/talos_machine_configuration/data-source.tf
================================================
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
================================================
FILE: examples/data-sources/talos_machine_disks/data-source.tf
================================================
resource "talos_machine_secrets" "this" {}
data "talos_machine_disks" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
selector = "disk.size > 6u * GB"
}
# for example, this could be used to pass in a list of disks to rook-ceph
output "nvme_disks" {
value = data.talos_machine_disks.this.disks.*.name
}
================================================
FILE: examples/ephemeral-resources/README.md
================================================
# Ephemeral Resources Examples
This directory contains examples demonstrating how to use ephemeral resources in the Talos provider to prevent secrets from being stored in Terraform state.
## Prerequisites
- **Terraform 1.11+** or **OpenTofu 1.11+** (required for ephemeral resources and write-only attributes)
- Note: Terraform 1.10 supports ephemeral resources but not write-only attributes
- Note: OpenTofu 1.11 introduced both features together (1.10 does not support ephemeral resources)
- Talos provider 0.11.0 or later
- HashiCorp Vault (or another secret manager)
- Access to Talos nodes
## Important: Secret Manager Requirement
**These examples require a secret manager** (like HashiCorp Vault) to store machine secrets. Do not generate `ephemeral "talos_machine_secrets"` directly without persisting them, as this will create new secrets on every Terraform run, causing unpredictable infrastructure changes.
### Why Secret Manager Integration is Required
Machine secrets must remain stable throughout the cluster lifecycle. Without a secret manager:
- New secrets generated on every `terraform plan` or `apply`
- Dependent resources constantly show changes
- Loss of cluster access when secrets regenerate
- Non-deterministic infrastructure state
### Setup Process
1. **First time setup**: Generate and store machine secrets in Vault
2. **Regular usage**: Retrieve secrets ephemerally from Vault
3. **Always**: Use ephemeral machine configurations to avoid storing configs in state
See the [Using Ephemeral Resources Guide](../../docs/guides/using_ephemeral_resources.md) for detailed setup instructions.
## Examples
### Basic Example
The [basic](./basic/) example demonstrates:
- Storing machine secrets in Vault (initial setup)
- Retrieving machine secrets ephemerally from Vault
- Creating machine configuration without storing secrets
- Applying configuration using write-only attributes
- Retrieving kubeconfig ephemerally
## Key Benefits
1. **Security**: Secrets never written to Terraform state
2. **Stability**: Machine secrets remain constant across runs
3. **Compliance**: Meets security policies requiring secret-free state
4. **Deterministic**: Infrastructure state is reproducible and predictable
## Learn More
- [Using Ephemeral Resources Guide](../../docs/guides/using_ephemeral_resources.md)
- [Ephemeral Resources Documentation](../../docs/ephemeral-resources/)
- [Terraform Ephemeral Resources](https://developer.hashicorp.com/terraform/language/resources/ephemeral)
- [Vault Ephemeral Resources](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/ephemeral-resources/kv_secret_v2)
================================================
FILE: examples/ephemeral-resources/basic/README.md
================================================
# Basic Ephemeral Resources Example
This example demonstrates the fundamental pattern for using ephemeral resources with the Talos provider and HashiCorp Vault.
## What This Example Shows
- How to store machine secrets in Vault (one-time setup)
- How to retrieve machine secrets ephemerally from Vault
- How to generate machine configurations without storing them in state
- How to use write-only attributes to prevent secrets in state
- How to retrieve kubeconfig ephemerally
## Prerequisites
- **Terraform 1.11+** or **OpenTofu 1.11+** (required for ephemeral resources and write-only attributes)
- Talos provider 0.11.0 or later
- HashiCorp Vault provider 5.0+
- Vault instance accessible from Terraform
- Talos node at 10.5.0.2 (or modify variables)
## Setup Instructions
### Step 1: Configure Vault Provider
Ensure your Vault provider is configured with appropriate credentials:
```bash
export VAULT_ADDR="https://vault.example.com:8200"
export VAULT_TOKEN="your-vault-token"
```
Or configure in your Terraform:
```terraform
provider "vault" {
address = "https://vault.example.com:8200"
}
```
### Step 2: Apply the Configuration
Simply run:
```bash
terraform init
terraform apply
```
**That's it!** On the first run, Terraform will:
1. Generate machine secrets ephemerally
2. Store them in Vault at `secret/data/talos-example-cluster`
3. Retrieve them from Vault for immediate use
4. Configure and bootstrap your Talos cluster
All in a single apply, thanks to Terraform's automatic dependency resolution.
### Step 3: Verify Deterministic Behavior
Run `terraform plan` again:
```bash
terraform plan
```
You should see no changes. The secrets are now stable in Vault and retrieved ephemerally on each run.
## Understanding the Pattern
### Why Vault?
Without Vault:
- `ephemeral "talos_machine_secrets"` would generate NEW secrets every run
- Cluster access would be lost
- All dependent resources would show changes
With Vault:
- Secrets stored once on first run, retrieved ephemerally thereafter
- Stable, deterministic infrastructure
- No secrets in Terraform state
- Works seamlessly in a single apply
### What's Ephemeral?
- **Machine secrets generation**: Only evaluated on first run (stored in Vault with hardcoded version)
- **Machine secrets retrieval**: Retrieved from Vault on every run (never stored in state)
- **Client configuration**: Retrieved from Vault on every run (never stored in state)
- **Machine configuration**: Generated fresh each time (but deterministically from stable secrets)
- **Kubeconfig**: Retrieved fresh each time from the cluster
### The Magic of Hardcoded Version
The `data_json_wo_version = 1` in the Vault resource is key:
- On first run: Secret doesn't exist, so Terraform generates it and stores version 1
- On subsequent runs: Secret version 1 exists and matches, so no update needed
- Since no update is needed, the `ephemeral "talos_machine_secrets"` isn't evaluated
- This prevents regenerating secrets while keeping all code in place
## Outputs
The example includes an ephemeral output for the kubeconfig:
```terraform
output "kubeconfig" {
value = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
sensitive = true
ephemeral = true
}
```
Access it with:
```bash
terraform output -raw kubeconfig > kubeconfig.yaml
export KUBECONFIG=kubeconfig.yaml
kubectl get nodes
```
## Troubleshooting
### "No secret found at path"
Ensure you've completed Step 2 to store secrets in Vault first.
### "Resources keep changing on every plan"
Check if the `data_json_wo_version` in the `vault_kv_secret_v2` resource is properly hardcoded. It should be set to a fixed value (e.g., `1`) to prevent regeneration.
### "Cannot connect to cluster"
Verify the secrets in Vault match what was originally used to configure the cluster. If you regenerated secrets, you'll need to reconfigure all cluster nodes.
================================================
FILE: examples/ephemeral-resources/basic/main.tf
================================================
terraform {
required_version = ">= 1.11"
required_providers {
talos = {
source = "siderolabs/talos"
version = "~> 0.11"
}
vault = {
source = "hashicorp/vault"
version = "~> 5.0"
}
}
}
# This example demonstrates the correct pattern for using ephemeral resources
# with Talos. Only machine_secrets is persisted to Vault — client_configuration
# is a derived value regenerated on every apply from the CA key.
# STEP 1: Generate and store machine secrets in Vault.
# The ephemeral resource generates secrets, which are immediately stored in Vault
# using write-only attributes. After the initial run, this ephemeral resource won't
# be evaluated again because data_json_wo_version is hardcoded.
ephemeral "talos_machine_secrets" "this" {}
resource "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-example-cluster"
# Only machine_secrets is persisted — client_configuration is derived and never stored.
data_json_wo = jsonencode({
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
})
# Hardcoded version prevents unnecessary refreshes after initial creation.
data_json_wo_version = 1
}
# STEP 2: Retrieve secrets ephemerally from Vault for cluster operations.
# This ephemeral resource reads from Vault on every run, but values are never
# stored in Terraform state.
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = vault_kv_secret_v2.talos_secrets.mount
name = vault_kv_secret_v2.talos_secrets.name
}
locals {
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
# STEP 3: Generate client credentials from machine_secrets on every apply.
# The cert is generated locally from the Talos OS CA key — no live API call.
# A 10-year lifetime matches the CA so no rotation is needed within the cluster lifetime.
ephemeral "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = local.talos_data.machine_secrets
crt_ttl = "87600h"
endpoints = ["10.5.0.2"]
nodes = ["10.5.0.2"]
}
# STEP 4: Persist talos_config to Vault for operators to use with talosctl.
# data_json_wo_version = 1 suppresses plan diffs after initial creation.
# Bump the version to force a refresh (e.g. after a CA rotation).
resource "vault_kv_secret_v2" "talos_config" {
mount = "secret"
name = "talos-example-cluster-config"
data_json_wo = jsonencode({
talos_config = ephemeral.talos_client_configuration.this.talos_config
})
data_json_wo_version = 1
}
# Generate controlplane machine configuration using ephemeral secrets from Vault.
ephemeral "talos_machine_configuration" "controlplane" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
machine_secrets = local.talos_data.machine_secrets
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sda"
}
}
})
]
}
# Apply configuration to a machine.
resource "talos_machine_configuration_apply" "controlplane" {
client_configuration_wo = ephemeral.talos_client_configuration.this.client_configuration
machine_configuration_input_wo = ephemeral.talos_machine_configuration.controlplane.machine_configuration
node = "10.5.0.2"
}
# Bootstrap the cluster.
resource "talos_machine_bootstrap" "this" {
client_configuration_wo = ephemeral.talos_client_configuration.this.client_configuration
node = "10.5.0.2"
depends_on = [
talos_machine_configuration_apply.controlplane
]
}
# Wait for cluster to be healthy (ephemeral — doesn't leak secrets to state).
ephemeral "talos_cluster_health" "this" {
client_configuration = ephemeral.talos_client_configuration.this.client_configuration
endpoints = ["10.5.0.2"]
control_plane_nodes = ["10.5.0.2"]
worker_nodes = []
skip_kubernetes_checks = false
depends_on = [
talos_machine_bootstrap.this
]
}
# Retrieve cluster kubeconfig ephemerally.
ephemeral "talos_cluster_kubeconfig" "this" {
client_configuration = ephemeral.talos_client_configuration.this.client_configuration
node = "10.5.0.2"
depends_on = [
ephemeral.talos_cluster_health.this
]
}
# Output the kubeconfig (marked as ephemeral).
output "kubeconfig" {
value = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
sensitive = true
ephemeral = true
}
================================================
FILE: examples/ephemeral-resources/talos_cluster_kubeconfig/ephemeral-resource.tf
================================================
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "example-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.5.0.2:6443"
}
# Recommended pattern for stable kubeconfig when storing in a secret manager:
# Persist not_before in terraform_data so the admin cert timestamps are fixed
# across plan invocations and kubeconfig_raw is byte-identical on every open.
resource "terraform_data" "kubeconfig_nbf" {
input = plantimestamp()
lifecycle {
ignore_changes = [input]
}
}
ephemeral "talos_cluster_kubeconfig" "stable" {
cluster_name = "example-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.5.0.2:6443"
not_before = terraform_data.kubeconfig_nbf.output
crt_ttl = "87600h"
}
================================================
FILE: examples/resources/talos_cluster_kubeconfig/resource.tf
================================================
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
}
}
})
]
}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = "10.5.0.2"
client_configuration = talos_machine_secrets.this.client_configuration
}
resource "talos_cluster_kubeconfig" "this" {
depends_on = [
talos_machine_bootstrap.this
]
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
}
================================================
FILE: examples/resources/talos_image_factory_schematic/resource.tf
================================================
provider "talos" {}
data "talos_image_factory_extensions_versions" "this" {
# get the latest talos version
talos_version = "v1.7.5"
filters = {
names = [
"amdgpu",
"tailscale",
]
}
}
resource "talos_image_factory_schematic" "this" {
schematic = yamlencode(
{
customization = {
systemExtensions = {
officialExtensions = data.talos_image_factory_extensions_versions.this.extensions_info.*.name
}
}
}
)
}
output "schematic_id" {
value = talos_image_factory_schematic.this.id
}
================================================
FILE: examples/resources/talos_machine_bootstrap/import.sh
================================================
# machine bootstrap can be imported to let terraform know that the machine is already bootstrapped
terraform import talos_machine_bootstrap.this
================================================
FILE: examples/resources/talos_machine_bootstrap/resource.tf
================================================
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
}
}
})
]
}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = "10.5.0.2"
client_configuration = talos_machine_secrets.this.client_configuration
}
================================================
FILE: examples/resources/talos_machine_configuration_apply/resource.tf
================================================
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
machine_type = "controlplane"
cluster_endpoint = "https://cluster.local:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
nodes = ["10.5.0.2"]
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
image = "ghcr.io/siderolabs/installer:v1.12.6"
}
}
})
]
}
================================================
FILE: examples/resources/talos_machine_secrets/import.sh
================================================
# machine secrets can be imported from an existing secrets file
terraform import talos_machine_secrets.this
================================================
FILE: examples/resources/talos_machine_secrets/resource.tf
================================================
resource "talos_machine_secrets" "machine_secrets" {}
================================================
FILE: go.mod
================================================
module github.com/siderolabs/terraform-provider-talos
go 1.26.2
require (
github.com/blang/semver/v4 v4.0.0
github.com/hashicorp/terraform-plugin-docs v0.24.0
github.com/hashicorp/terraform-plugin-framework v1.19.0
github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0
github.com/hashicorp/terraform-plugin-framework-validators v0.19.0
github.com/hashicorp/terraform-plugin-go v0.31.0
github.com/hashicorp/terraform-plugin-log v0.10.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0
github.com/hashicorp/terraform-plugin-testing v1.15.0
github.com/siderolabs/crypto v0.6.5
github.com/siderolabs/gen v0.8.6
github.com/siderolabs/image-factory v1.1.0
github.com/siderolabs/net v0.4.0
github.com/siderolabs/talos v1.13.0
github.com/siderolabs/talos/pkg/machinery v1.13.0
github.com/stretchr/testify v1.11.1
go.yaml.in/yaml/v4 v4.0.0-rc.4
golang.org/x/crypto v0.50.0
golang.org/x/mod v0.35.0
google.golang.org/grpc v1.80.0
k8s.io/client-go v0.35.4
)
require (
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
github.com/ProtonMail/gopenpgp/v2 v2.10.0 // indirect
github.com/adrg/xdg v0.5.3 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/gertd/go-pluralize v0.2.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/siderolabs/go-api-signature v0.3.12 // indirect
github.com/siderolabs/protoenc v0.2.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
)
require (
cel.dev/expr v0.25.1 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/agext/levenshtein v1.2.2 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/containerd/go-cni v1.1.13 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/containernetworking/cni v1.3.0 // indirect
github.com/cosi-project/runtime v1.14.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/dot v1.11.0 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.22.5 // indirect
github.com/go-openapi/jsonreference v0.21.5 // indirect
github.com/go-openapi/swag v0.25.5 // indirect
github.com/go-openapi/swag/cmdutils v0.25.5 // indirect
github.com/go-openapi/swag/conv v0.25.5 // indirect
github.com/go-openapi/swag/fileutils v0.25.5 // indirect
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
github.com/go-openapi/swag/loading v0.25.5 // indirect
github.com/go-openapi/swag/mangling v0.25.5 // indirect
github.com/go-openapi/swag/netutils v0.25.5 // indirect
github.com/go-openapi/swag/stringutils v0.25.5 // indirect
github.com/go-openapi/swag/typeutils v0.25.5 // indirect
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
github.com/go-test/deep v1.1.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.28.0 // indirect
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.21.5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/cli v1.1.7 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-cty v1.5.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.7.0 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.8.0 // indirect
github.com/hashicorp/hc-install v0.9.3 // indirect
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.25.0 // indirect
github.com/hashicorp/terraform-json v0.27.2 // indirect
github.com/hashicorp/terraform-registry-address v0.4.0 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jsimonetti/rtnetlink/v2 v2.2.1-0.20260317095713-310581b9c6ac // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mdlayher/ethtool v0.5.1 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.9.0 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/moby/api v1.54.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.3.0 // indirect
github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sasha-s/go-deadlock v0.3.6 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/siderolabs/go-circular v0.2.3 // indirect
github.com/siderolabs/go-kubernetes v0.2.36 // indirect
github.com/siderolabs/go-pointer v1.0.1 // indirect
github.com/siderolabs/go-procfs v0.1.2 // indirect
github.com/siderolabs/go-retry v0.3.3 // indirect
github.com/siderolabs/go-talos-support v0.2.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/vbatts/tar-split v0.12.2 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/yuin/goldmark v1.7.7 // indirect
github.com/yuin/goldmark-meta v1.1.0 // indirect
github.com/zclconf/go-cty v1.17.0 // indirect
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.35.4 // indirect
k8s.io/apimachinery v0.35.4 // indirect
k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf // indirect
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
================================================
FILE: go.sum
================================================
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0=
github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/gopenpgp/v2 v2.10.0 h1:llCzLvntC9+iH+if/na4AgKTef/Zm4vpaRrR3+JdKvo=
github.com/ProtonMail/gopenpgp/v2 v2.10.0/go.mod h1:dc0h9Pg3ftfN0U4pfRzujilfh61A2R52wgMkZWcWm2I=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/brianvoe/gofakeit/v7 v7.7.3 h1:RWOATEGpJ5EVg2nN8nlaEyaV/aB4d6c3GqYrbqQekss=
github.com/brianvoe/gofakeit/v7 v7.7.3/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.21.0 h1:4dpx1J/B/1apeTmWBH5BkVLayHTkFrMovVPnHEk+l3k=
github.com/cilium/ebpf v0.21.0/go.mod h1:1kHKv6Kvh5a6TePP5vvvoMa1bclRyzUXELSs272fmIQ=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/containerd/go-cni v1.1.13 h1:eFSGOKlhoYNxpJ51KRIMHZNlg5UgocXEIEBGkY7Hnis=
github.com/containerd/go-cni v1.1.13/go.mod h1:nTieub0XDRmvCZ9VI/SBG6PyqT95N4FIhxsauF1vSBI=
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo=
github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4=
github.com/cosi-project/runtime v1.14.1 h1:1mxuH0zGXdJIy6762kaQsd+7C9MmzzuvIVIfWd867Os=
github.com/cosi-project/runtime v1.14.1/go.mod h1:SfzpfNx7YwK8byi1X6ytikDXVMmbC7UpiCWdzntRf8M=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=
github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/dot v1.11.0 h1:zsrhCuFHAJge/aZIC4N4LdHy5tqYu4tWEaUzIwdYj4Y=
github.com/emicklei/dot v1.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=
github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=
github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=
github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=
github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=
github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=
github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=
github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc=
github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.21.5 h1:KTJG9Pn/jC0VdZR6ctV3/jcN+q6/Iqlx0sTVz3ywZlM=
github.com/google/go-containerregistry v0.21.5/go.mod h1:ySvMuiWg+dOsRW0Hw8GYwfMwBlNRTmpYBFJPlkco5zU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/hashicorp/cli v1.1.7 h1:/fZJ+hNdwfTSfsxMBa9WWMlfjUZbX8/LnUxgAd7lCVU=
github.com/hashicorp/cli v1.1.7/go.mod h1:e6Mfpga9OCT1vqzFuoGZiiF/KaG9CbUfO5s3ghU3YgU=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0=
github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA=
github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hc-install v0.9.3 h1:1H4dgmgzxEVwT6E/d/vIL5ORGVKz9twRwDw+qA5Hyho=
github.com/hashicorp/hc-install v0.9.3/go.mod h1:FQlQ5I3I/X409N/J1U4pPeQQz1R3BoV0IysB7aiaQE0=
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/terraform-exec v0.25.0 h1:Bkt6m3VkJqYh+laFMrWIpy9KHYFITpOyzRMNI35rNaY=
github.com/hashicorp/terraform-exec v0.25.0/go.mod h1:dl9IwsCfklDU6I4wq9/StFDp7dNbH/h5AnfS1RmiUl8=
github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU=
github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE=
github.com/hashicorp/terraform-plugin-docs v0.24.0 h1:YNZYd+8cpYclQyXbl1EEngbld8w7/LPOm99GD5nikIU=
github.com/hashicorp/terraform-plugin-docs v0.24.0/go.mod h1:YLg+7LEwVmRuJc0EuCw0SPLxuQXw5mW8iJ5ml/kvi+o=
github.com/hashicorp/terraform-plugin-framework v1.19.0 h1:q0bwyhxAOR3vfdgbk9iplv3MlTv/dhBHTXjQOtQDoBA=
github.com/hashicorp/terraform-plugin-framework v1.19.0/go.mod h1:YRXOBu0jvs7xp4AThBbX4mAzYaMJ1JgtFH//oGKxwLc=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0 h1:jblRy1PkLfPm5hb5XeMa3tezusnMRziUGqtT5epSYoI=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0/go.mod h1:5jm2XK8uqrdiSRfD5O47OoxyGMCnwTcl8eoiDgSa+tc=
github.com/hashicorp/terraform-plugin-framework-validators v0.19.0 h1:Zz3iGgzxe/1XBkooZCewS0nJAaCFPFPHdNJd8FgE4Ow=
github.com/hashicorp/terraform-plugin-framework-validators v0.19.0/go.mod h1:GBKTNGbGVJohU03dZ7U8wHqc2zYnMUawgCN+gC0itLc=
github.com/hashicorp/terraform-plugin-go v0.31.0 h1:0Fz2r9DQ+kNNl6bx8HRxFd1TfMKUvnrOtvJPmp3Z0q8=
github.com/hashicorp/terraform-plugin-go v0.31.0/go.mod h1:A88bDhd/cW7FnwqxQRz3slT+QY6yzbHKc6AOTtmdeS8=
github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g=
github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0 h1:MKS/2URqeJRwJdbOfcbdsZCq/IRrNkqJNN0GtVIsuGs=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0/go.mod h1:PuG4P97Ju3QXW6c6vRkRadWJbvnEu2Xh+oOuqcYOqX4=
github.com/hashicorp/terraform-plugin-testing v1.15.0 h1:/fimKyl0YgD7aAtJkuuAZjwBASXhCIwWqMbDLnKLMe4=
github.com/hashicorp/terraform-plugin-testing v1.15.0/go.mod h1:bGXMw7bE95EiZhSBV3rM2W8TiffaPTDuLS+HFI/lIYs=
github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk=
github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE=
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
github.com/jsimonetti/rtnetlink/v2 v2.2.1-0.20260317095713-310581b9c6ac h1:UfziP9RaDM6D+f+yNdL3T/N1DztwltLDGBkpybF/fYs=
github.com/jsimonetti/rtnetlink/v2 v2.2.1-0.20260317095713-310581b9c6ac/go.mod h1:A/gqt1BEMJcvzGQJXQ3SnsDOQL7QRNhxTiC3eb++608=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mdlayher/ethtool v0.5.1 h1:U4GThY6WgNJUJsMrUzBmoOTdQHFWxFPTHTeNnn3GCvU=
github.com/mdlayher/ethtool v0.5.1/go.mod h1:Pz39PaAy96Ea1SrCvEO/pPEAeULvRJjO6zspuEMhJy4=
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco=
github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 h1:rh2lKw/P/EqHa724vYH2+VVQ1YnW4u6EOXl0PMAovZE=
github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 h1:S1hI5JiKP7883xBzZAr1ydcxrKNSVNm7+3+JwjxZEsg=
github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25/go.mod h1:ZQntvDG8TkPgljxtA0R9frDoND4QORU1VXz015N5Ks4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sasha-s/go-deadlock v0.3.6 h1:TR7sfOnZ7x00tWPfD397Peodt57KzMDo+9Ae9rMiUmw=
github.com/sasha-s/go-deadlock v0.3.6/go.mod h1:CUqNyyvMxTyjFqDT7MRg9mb4Dv/btmGTqSR+rky/UXo=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/siderolabs/crypto v0.6.5 h1:Elq5tpWP2ApZ4Y+Kg+eIDiiWbmriCPI1mjYIMwvsYkw=
github.com/siderolabs/crypto v0.6.5/go.mod h1:QjVcrdJQE1sxhjHqieCgwGdlIYq/xCP2DL53Up8nbU4=
github.com/siderolabs/gen v0.8.6 h1:pE6shuqov3L+5rEcAUJ/kY6iJofimljQw5G95P8a5c4=
github.com/siderolabs/gen v0.8.6/go.mod h1:J9IbusbES2W6QWjtSHpDV9iPGZHc978h1+KJ4oQRspQ=
github.com/siderolabs/go-api-signature v0.3.12 h1:i1X+kPh9fzo+lEjtEplZSbtq1p21vKv4FCWJcB/ozvk=
github.com/siderolabs/go-api-signature v0.3.12/go.mod h1:dPLiXohup4qHX7KUgF/wwOE3lRU5uAr3ssEomNxiyxY=
github.com/siderolabs/go-circular v0.2.3 h1:GKkA1Tw79kEFGtWdl7WTxEUTbwtklITeiRT0V1McHrA=
github.com/siderolabs/go-circular v0.2.3/go.mod h1:YBN/q9YpQphUYnBtBgPsngauSHj1TEZfgQZWZVjk1WE=
github.com/siderolabs/go-kubernetes v0.2.36 h1:n8p6c7M0kDnrm9WoQ8njHUs/k3o//kEh9t0eFsnHp9Q=
github.com/siderolabs/go-kubernetes v0.2.36/go.mod h1:jQAuwnqjGD0wakL5OYJz8LrX2eYoDqgKrS2TnqesUoU=
github.com/siderolabs/go-pointer v1.0.1 h1:f7Yi4IK1jptS8yrT9GEbwhmGcVxvPQgBUG/weH3V3DM=
github.com/siderolabs/go-pointer v1.0.1/go.mod h1:C8Q/3pNHT4RE9e4rYR9PHeS6KPMlStRBgYrJQJNy/vA=
github.com/siderolabs/go-procfs v0.1.2 h1:bDs9hHyYGE2HO1frpmUsD60yg80VIEDrx31fkbi4C8M=
github.com/siderolabs/go-procfs v0.1.2/go.mod h1:dBzQXobsM7+TWRRI3DS9X7vAuj8Nkfgu3Z/U9iY3ZTY=
github.com/siderolabs/go-retry v0.3.3 h1:zKV+S1vumtO72E6sYsLlmIdV/G/GcYSBLiEx/c9oCEg=
github.com/siderolabs/go-retry v0.3.3/go.mod h1:Ff/VGc7v7un4uQg3DybgrmOWHEmJ8BzZds/XNn/BqMI=
github.com/siderolabs/go-talos-support v0.2.1 h1:QVFhcmGw1ZTTXEtukBgrdjNxYbsPAOTMpopisV4EbgU=
github.com/siderolabs/go-talos-support v0.2.1/go.mod h1:tfwP9mpPdmqLU8DuCDgcAd0ficLlJuB/XMtrIV8g+20=
github.com/siderolabs/image-factory v1.1.0 h1:M5OZXPKQtBlUbzzH9PSP+ydp75S3mdYyiLXo7BoX9mc=
github.com/siderolabs/image-factory v1.1.0/go.mod h1:cequkUpOoM+D1fA5kQrs573aLFwoqTPMAAeA1N1poog=
github.com/siderolabs/net v0.4.0 h1:1bOgVay/ijPkJz4qct98nHsiB/ysLQU0KLoBC4qLm7I=
github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM=
github.com/siderolabs/protoenc v0.2.4 h1:D3Fpn2nQSQOhl8ZlAxijZAf7K6F8CM1uZq0afIGsr8Q=
github.com/siderolabs/protoenc v0.2.4/go.mod h1:i5XLHjfv5vyi7LhQrSEo19HCA+lYtDd7CWxsoWp9XE8=
github.com/siderolabs/talos v1.13.0 h1:4KXRbxCZ/8FtXRV/cEaouW8K/LjRkBLG7a42dsNLe8M=
github.com/siderolabs/talos v1.13.0/go.mod h1:Lvg9a5c4rxCjl8fos3akomDn2mqWFXwzNPbgZMNnE94=
github.com/siderolabs/talos/pkg/machinery v1.13.0 h1:nNfAUqgD/yOb4RZAc3xrQXKYllIK36RPaJacNQQC3TI=
github.com/siderolabs/talos/pkg/machinery v1.13.0/go.mod h1:70Up2PI+g6wxW4rJ8AIlZO0MfQ8gglyfqsyBv0PdnSo=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
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/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
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=
go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw=
go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.35.4 h1:P7nFYKl5vo9AGUp1Z+Pmd3p2tA7bX2wbFWCvDeRv988=
k8s.io/api v0.35.4/go.mod h1:yl4lqySWOgYJJf9RERXKUwE9g2y+CkuwG+xmcOK8wXU=
k8s.io/apimachinery v0.35.4 h1:xtdom9RG7e+yDp71uoXoJDWEE2eOiHgeO4GdBzwWpds=
k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc=
k8s.io/client-go v0.35.4 h1:DN6fyaGuzK64UvnKO5fOA6ymSjvfGAnCAHAR0C66kD8=
k8s.io/client-go v0.35.4/go.mod h1:2Pg9WpsS4NeOpoYTfHHfMxBG8zFMSAUi4O/qoiJC3nY=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf h1:btPscg4cMql0XdYK2jLsJcNEKmACJz8l+U7geC06FiM=
k8s.io/kube-openapi v0.0.0-20260304202019-5b3e3fdb0acf/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
================================================
FILE: hack/release.sh
================================================
#!/usr/bin/env bash
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2024-08-29T12:20:48Z by kres b5ca957.
set -e
RELEASE_TOOL_IMAGE="ghcr.io/siderolabs/release-tool:latest"
function release-tool {
docker pull "${RELEASE_TOOL_IMAGE}" >/dev/null
docker run --rm -w /src -v "${PWD}":/src:ro "${RELEASE_TOOL_IMAGE}" -l -d -n -t "${1}" ./hack/release.toml
}
function changelog {
if [ "$#" -eq 1 ]; then
(release-tool ${1}; echo; cat CHANGELOG.md) > CHANGELOG.md- && mv CHANGELOG.md- CHANGELOG.md
else
echo 1>&2 "Usage: $0 changelog [tag]"
exit 1
fi
}
function release-notes {
release-tool "${2}" > "${1}"
}
function cherry-pick {
if [ $# -ne 2 ]; then
echo 1>&2 "Usage: $0 cherry-pick "
exit 1
fi
git checkout $2
git fetch
git rebase upstream/$2
git cherry-pick -x $1
}
function commit {
if [ $# -ne 1 ]; then
echo 1>&2 "Usage: $0 commit "
exit 1
fi
if is_on_main_branch; then
update_license_files
fi
git commit -s -m "release($1): prepare release" -m "This is the official $1 release."
}
function is_on_main_branch {
main_remotes=("upstream" "origin")
branch_names=("main" "master")
current_branch=$(git rev-parse --abbrev-ref HEAD)
echo "Check current branch: $current_branch"
for remote in "${main_remotes[@]}"; do
echo "Fetch remote $remote..."
if ! git fetch --quiet "$remote" &>/dev/null; then
echo "Failed to fetch $remote, skip..."
continue
fi
for branch_name in "${branch_names[@]}"; do
if ! git rev-parse --verify "$branch_name" &>/dev/null; then
echo "Branch $branch_name does not exist, skip..."
continue
fi
echo "Branch $remote/$branch_name exists, comparing..."
merge_base=$(git merge-base "$current_branch" "$remote/$branch_name")
latest_main=$(git rev-parse "$remote/$branch_name")
if [ "$merge_base" = "$latest_main" ]; then
echo "Current branch is up-to-date with $remote/$branch_name"
return 0
else
echo "Current branch is not on $remote/$branch_name"
return 1
fi
done
done
echo "No main or master branch found on any remote"
return 1
}
function update_license_files {
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
parent_dir="$(dirname "$script_dir")"
current_year=$(date +"%Y")
change_date=$(date -v+4y +"%Y-%m-%d" 2>/dev/null || date -d "+4 years" +"%Y-%m-%d" 2>/dev/null || date --date="+4 years" +"%Y-%m-%d")
# Find LICENSE and .kres.yaml files recursively in the parent directory (project root)
find "$parent_dir" \( -name "LICENSE" -o -name ".kres.yaml" \) -type f | while read -r file; do
temp_file="${file}.tmp"
if [[ $file == *"LICENSE" ]]; then
if grep -q "^Business Source License" "$file"; then
sed -e "s/The Licensed Work is (c) [0-9]\{4\}/The Licensed Work is (c) $current_year/" \
-e "s/Change Date: [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}/Change Date: $change_date/" \
"$file" >"$temp_file"
else
continue # Not a Business Source License file
fi
elif [[ $file == *".kres.yaml" ]]; then
sed -E 's/^([[:space:]]*)ChangeDate:.*$/\1ChangeDate: "'"$change_date"'"/' "$file" >"$temp_file"
fi
# Check if the file has changed
if ! cmp -s "$file" "$temp_file"; then
mv "$temp_file" "$file"
echo "Updated: $file"
git add "$file"
else
echo "No changes: $file"
rm "$temp_file"
fi
done
}
if declare -f "$1" > /dev/null
then
cmd="$1"
shift
$cmd "$@"
else
cat < ", os.Args[0])
}
resourceType := os.Args[1]
resourceName := strings.ToLower(strings.Split(resourceType, ".")[1])
resource, ok := resourceMaps()[resourceType]
if !ok {
log.Fatalf("unknown type: %s", resourceType)
}
blockDiskSpecStruct := reflect.TypeOf(resource)
g.Printf("type %s struct {\n", resourceName)
for i := range blockDiskSpecStruct.NumField() {
tfType, err := mapGoTypesToTFTypes(blockDiskSpecStruct.Field(i).Type)
if err != nil {
log.Fatalf("failed to map type %s: %v", blockDiskSpecStruct.Field(i).Type.Name(), err)
}
g.Printf("\t%s %s `tfsdk:\"%s\"`\n",
blockDiskSpecStruct.Field(i).Name,
tfType,
strings.ReplaceAll(blockDiskSpecStruct.Field(i).Tag.Get("yaml"), ",omitempty", ""),
)
}
g.Printf("}\n")
g.Printf("\n")
g.Printf("var %sAttributes = map[string]schema.Attribute{\n", resourceName)
for i := 0; i < blockDiskSpecStruct.NumField(); i++ {
attributeType, err := mapGoTypesToTFAttributeSpec(blockDiskSpecStruct.Field(i).Type)
if err != nil {
log.Fatalf("failed to map type %s: %v", blockDiskSpecStruct.Field(i).Type.Name(), err)
}
g.Printf("\t\"%s\": %s,\n",
strings.ReplaceAll(blockDiskSpecStruct.Field(i).Tag.Get("yaml"), ",omitempty", ""),
attributeType,
)
}
g.Printf("}\n")
g.Printf("func %sToTFTypes(%sSpec %s) %s {\n", resourceName, resourceName, resourceType, resourceName)
g.Printf("\treturn %s{\n", resourceName)
for i := 0; i < blockDiskSpecStruct.NumField(); i++ {
tfType, err := mapStructTypeToTFType(blockDiskSpecStruct.Field(i), resourceName)
if err != nil {
log.Fatalf("failed to map type %s: %v", blockDiskSpecStruct.Field(i).Type.Name(), err)
}
g.Printf("\t\t%s: %s,\n",
blockDiskSpecStruct.Field(i).Name,
tfType,
)
}
g.Printf("\t}\n")
g.Printf("}\n")
src := g.format()
if err := os.WriteFile(fmt.Sprintf("%s_types.go", os.Args[2]), src, 0o644); err != nil {
log.Fatalf("failed to write file: %v", err)
}
}
type Generator struct {
buf bytes.Buffer
}
func (g *Generator) Printf(format string, args ...interface{}) {
fmt.Fprintf(&g.buf, format, args...)
}
// format returns the gofmt-ed contents of the Generator's buffer.
func (g *Generator) format() []byte {
src, err := format.Source(g.buf.Bytes())
if err != nil {
// Should never happen, but can arise when developing this code.
// The user can compile the output to see the error.
log.Printf("warning: internal error: invalid Go generated: %s", err)
log.Printf("warning: compile the package to analyze the error")
return g.buf.Bytes()
}
return src
}
func mapGoTypesToTFTypes(goType reflect.Type) (string, error) {
// handle the case where the field is a slice
if goType.Kind() == reflect.Slice {
switch goType.Elem().Kind() {
case reflect.String:
return "types.List", nil
default:
return "", fmt.Errorf("unsupported slice type: %s", goType.Elem().Name())
}
}
switch goType.Name() {
case "string":
return "types.String", nil
case "uint", "uint64", "int", "int64":
return "types.Int64", nil
case "bool":
return "types.Bool", nil
default:
return "", fmt.Errorf("unsupported type: %s", goType.Name())
}
}
func mapGoTypesToTFAttributeSpec(goType reflect.Type) (string, error) {
// handle the case where the field is a slice
if goType.Kind() == reflect.Slice {
switch goType.Elem().Kind() {
case reflect.String:
return `schema.ListAttribute{
ElementType: types.StringType,
Computed: true,
}`, nil
default:
return "", fmt.Errorf("unsupported slice type: %s", goType.Elem().Name())
}
}
switch goType.Name() {
case "string":
return `schema.StringAttribute{
Computed: true,
}`, nil
case "uint", "uint64", "int", "int64":
return `schema.Int64Attribute{
Computed: true,
}`, nil
case "bool":
return `schema.BoolAttribute{
Computed: true,
}`, nil
default:
return "", fmt.Errorf("unsupported type: %s", goType.Name())
}
}
func mapStructTypeToTFType(structField reflect.StructField, resourceName string) (string, error) {
// handle the case where the field is a slice
if structField.Type.Kind() == reflect.Slice {
switch structField.Type.Elem().Kind() {
case reflect.String:
return fmt.Sprintf(`types.ListValueMust(types.StringType, xslices.Map(%sSpec.%s, func(s string) attr.Value {
return types.StringValue(s)
}))`, resourceName, structField.Name), nil
default:
return "", fmt.Errorf("unsupported slice type: %s", structField.Type.Elem().Name())
}
}
switch structField.Type.Name() {
case "string":
return fmt.Sprintf("types.StringValue(%sSpec.%s)", resourceName, structField.Name), nil
case "uint", "uint64", "int", "int64":
return fmt.Sprintf("types.Int64Value(int64(%sSpec.%s))", resourceName, structField.Name), nil
case "bool":
return fmt.Sprintf("types.BoolValue(%sSpec.%s)", resourceName, structField.Name), nil
default:
return "", fmt.Errorf("unsupported type: %s", structField.Type.Name())
}
}
================================================
FILE: pkg/talos/provider.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package talos is a Terraform provider for Talos.
package talos
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/siderolabs/image-factory/pkg/client"
)
const (
// ImageFactoryURL is the default URL of Image Factory.
ImageFactoryURL = "https://factory.talos.dev"
)
// talosProvider is the provider implementation.
type talosProvider struct{}
type talosProviderModelV0 struct {
ImageFactoryURL types.String `tfsdk:"image_factory_url"`
}
// New is a helper function to simplify provider server and testing implementation.
func New() provider.Provider {
return &talosProvider{}
}
// Metadata returns the provider type name.
func (p *talosProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "talos"
}
// Schema defines the provider-level schema for configuration data.
func (p *talosProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"image_factory_url": schema.StringAttribute{
Optional: true,
Description: "The URL of Image Factory to generate schematics. If not set defaults to https://factory.talos.dev.",
},
},
}
}
// Configure prepares a Talos client for data sources and resources.
func (p *talosProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var config talosProviderModelV0
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
imageFactoryURL := config.ImageFactoryURL.ValueString()
if imageFactoryURL == "" && !config.ImageFactoryURL.IsUnknown() {
imageFactoryURL = ImageFactoryURL
}
imageFactoryClient, err := client.New(imageFactoryURL)
if err != nil {
resp.Diagnostics.AddError("failed to create Image Factory client", err.Error())
return
}
resp.DataSourceData = imageFactoryClient
resp.ResourceData = imageFactoryClient
}
// DataSources defines the data sources implemented in the provider.
func (p *talosProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewTalosMachineDisksDataSource,
NewTalosMachineConfigurationDataSource,
NewTalosClientConfigurationDataSource,
NewTalosClusterHealthDataSource,
NewTalosClusterKubeConfigDataSource,
NewTalosImageFactoryVersionsDataSource,
NewTalosImageFactoryExtensionsVersionsDataSource,
NewTalosImageFactoryOverlaysVersionsDataSource,
NewTalosImageFactoryURLSDataSource,
}
}
// Resources defines the resources implemented in the provider.
func (p *talosProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewTalosMachineSecretsResource,
NewTalosMachineConfigurationApplyResource,
NewTalosMachineBootstrapResource,
NewTalosClusterKubeConfigResource,
NewTalosImageFactorySchematicResource,
}
}
// EphemeralResources defines the ephemeral resources implemented in the provider.
func (p *talosProvider) EphemeralResources(_ context.Context) []func() ephemeral.EphemeralResource {
return []func() ephemeral.EphemeralResource{
NewTalosMachineSecretsEphemeralResource,
NewTalosMachineConfigurationEphemeralResource,
NewTalosClientConfigurationEphemeralResource,
NewTalosClusterKubeConfigEphemeralResource,
NewTalosClusterHealthEphemeralResource,
}
}
================================================
FILE: pkg/talos/provider_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"fmt"
"os"
"strings"
"text/template"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"github.com/siderolabs/terraform-provider-talos/pkg/talos"
)
// testAccProtoV6ProviderFactories are used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"talos": providerserver.NewProtocol6WithError(talos.New()),
}
type dynamicConfig struct {
Provider string
ResourceName string
IsoURL string
CPUMode string
WithApplyConfig bool
WithBootstrap bool
WithRetrieveKubeConfig bool
WithClusterHealth bool
}
const (
cpuModeHostPassthrough = "host-passthrough"
cpuModeHostModel = "host-model"
)
func (c *dynamicConfig) render() string {
cpuMode := cpuModeHostPassthrough
if os.Getenv("CI") != "" {
cpuMode = cpuModeHostModel
}
c.CPUMode = cpuMode
c.IsoURL = fmt.Sprintf("https://github.com/siderolabs/talos/releases/download/%s/metal-amd64.iso", gendata.VersionTag)
configTemplate := `
resource "talos_machine_secrets" "this" {}
resource "libvirt_volume" "cp" {
name = "{{ .ResourceName }}"
size = 6442450944
}
resource "libvirt_domain" "cp" {
name = "{{ .ResourceName }}"
firmware = "/usr/share/OVMF/OVMF_CODE_4M.fd"
lifecycle {
ignore_changes = [
cpu,
nvram,
disk["url"],
firmware,
]
}
cpu {
mode = "{{ .CPUMode }}"
}
console {
type = "pty"
target_port = "0"
}
graphics {
type = "vnc"
listen_type = "address"
}
disk {
url = "{{ .IsoURL }}"
}
disk {
volume_id = libvirt_volume.cp.id
}
boot_device {
dev = ["cdrom"]
}
network_interface {
network_name = "default"
wait_for_lease = true
}
vcpu = "2"
memory = "4096"
}
{{ if eq .Provider "talosv1"}}
resource "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_controlplane" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://${libvirt_domain.cp.network_interface[0].addresses[0]}:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
{{ if .WithApplyConfig }}
resource "talos_machine_configuration_apply" "this" {
talos_config = talos_client_configuration.this.talos_config
machine_configuration = talos_machine_configuration_controlplane.this.machine_config
node = libvirt_domain.cp.network_interface[0].addresses[0]
endpoint = libvirt_domain.cp.network_interface[0].addresses[0]
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/vda"
}
}
}),
]
}
{{ end }}
{{ if .WithBootstrap }}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = libvirt_domain.cp.network_interface[0].addresses[0]
endpoint = libvirt_domain.cp.network_interface[0].addresses[0]
talos_config = talos_client_configuration.this.talos_config
}
{{ end }}
{{ else }}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://${libvirt_domain.cp.network_interface[0].addresses[0]}:6443"
machine_type = "controlplane"
machine_secrets = talos_machine_secrets.this.machine_secrets
docs = false
examples = false
}
data "talos_machine_disks" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
node = libvirt_domain.cp.network_interface[0].addresses[0]
selector = "disk.size > 6u * GB"
}
{{ if .WithApplyConfig }}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = libvirt_domain.cp.network_interface[0].addresses[0]
config_patches = [
yamlencode({
machine = {
install = {
disk = data.talos_machine_disks.this.disks[0].dev_path
}
}
}),
]
}
{{ end }}
{{ if .WithBootstrap }}
resource "talos_machine_bootstrap" "this" {
depends_on = [
talos_machine_configuration_apply.this
]
node = libvirt_domain.cp.network_interface[0].addresses[0]
client_configuration = talos_machine_secrets.this.client_configuration
}
{{ end }}
{{ if .WithRetrieveKubeConfig }}
resource "talos_cluster_kubeconfig" "this" {
depends_on = [
talos_machine_bootstrap.this
]
client_configuration = talos_machine_secrets.this.client_configuration
node = libvirt_domain.cp.network_interface[0].addresses[0]
}
{{ end }}
{{ if .WithClusterHealth }}
data "talos_cluster_health" "this" {
depends_on = [
talos_cluster_kubeconfig.this
]
timeouts = {
read = "25m"
}
client_configuration = talos_machine_secrets.this.client_configuration
endpoints = libvirt_domain.cp.network_interface[0].addresses
control_plane_nodes = libvirt_domain.cp.network_interface[0].addresses
}
{{ end }}
{{ end }}
`
var config strings.Builder
template.Must(template.New("tf_config").Parse(configTemplate)).Execute(&config, c) //nolint:errcheck
return config.String()
}
================================================
FILE: pkg/talos/rfc6979.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
// RFC 6979 deterministic ECDSA nonce generation for P-256.
// This produces signatures that are fully deterministic (no randomness),
// which is required because Go 1.26+ ignores custom io.Reader in crypto/ecdsa.
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/hmac"
"crypto/sha256"
"encoding/asn1"
"io"
"math/big"
)
// deterministicECDSASigner wraps an ECDSA private key and produces RFC 6979
// deterministic signatures instead of using random nonces.
type deterministicECDSASigner struct {
key *ecdsa.PrivateKey
}
func (s *deterministicECDSASigner) Public() crypto.PublicKey {
return &s.key.PublicKey
}
func (s *deterministicECDSASigner) Sign(_ io.Reader, digest []byte, _ crypto.SignerOpts) ([]byte, error) {
r, ss := rfc6979Sign(s.key, digest)
return asn1.Marshal(struct {
R, S *big.Int
}{r, ss})
}
// rfc6979Sign computes an ECDSA signature using the deterministic nonce
// generation algorithm from RFC 6979, Section 3.2.
func rfc6979Sign(key *ecdsa.PrivateKey, hash []byte) (*big.Int, *big.Int) {
curve := key.Curve
n := curve.Params().N
qLen := (n.BitLen() + 7) / 8 // byte length of the curve order
// Use Bytes() instead of the deprecated D field.
// Bytes never returns an error for valid keys.
keyBytes, _ := key.Bytes() //nolint:errcheck
d := new(big.Int).SetBytes(keyBytes)
// int2octets: private key as a fixed-length big-endian integer.
x := keyBytes
// bits2octets: reduce hash modulo n, then encode as fixed-length.
h1 := bits2octets(hash, n, qLen)
// Section 3.2 steps a-f: initialize HMAC_DRBG.
hLen := sha256.Size // 32 for SHA-256
v := make([]byte, hLen)
k := make([]byte, hLen)
for i := range v {
v[i] = 0x01
}
// Step d: K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1))
k = hmacSHA256(k, v, []byte{0x00}, x, h1)
// Step e: V = HMAC_K(V)
v = hmacSHA256(k, v)
// Step f: K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1))
k = hmacSHA256(k, v, []byte{0x01}, x, h1)
// Step g: V = HMAC_K(V)
v = hmacSHA256(k, v)
// Step h: generate k candidates until valid.
for {
// For P-256 with SHA-256, one HMAC output (32 bytes) = qLen.
v = hmacSHA256(k, v)
kCandidate := bits2int(v, n)
if kCandidate.Sign() > 0 && kCandidate.Cmp(n) < 0 {
r, ss := rawECDSASign(curve, n, d, kCandidate, hash)
if r.Sign() > 0 && ss.Sign() > 0 {
return r, ss
}
}
// Not valid, update K and V per RFC 6979 step h.3.
k = hmacSHA256(k, v, []byte{0x00})
v = hmacSHA256(k, v)
}
}
// rawECDSASign computes (r, s) given a nonce k, following the standard ECDSA
// signature algorithm.
func rawECDSASign(curve elliptic.Curve, n, d, k *big.Int, hash []byte) (*big.Int, *big.Int) {
// R = k * G
// No non-deprecated API exposes the x-coordinate of a scalar multiplication.
rx, _ := curve.ScalarBaseMult(k.Bytes()) //nolint:staticcheck
r := new(big.Int).Mod(rx, n)
if r.Sign() == 0 {
return r, new(big.Int)
}
// s = k^-1 * (hash + r * d) mod n
e := bits2int(hash, n)
s := new(big.Int).Mul(r, d)
s.Add(s, e)
kInv := new(big.Int).ModInverse(k, n)
s.Mul(s, kInv)
s.Mod(s, n)
return r, s
}
// hmacSHA256 computes HMAC-SHA256(key, data...).
func hmacSHA256(key []byte, data ...[]byte) []byte {
h := hmac.New(sha256.New, key)
for _, d := range data {
h.Write(d) //nolint:errcheck
}
return h.Sum(nil)
}
// int2octets converts a big.Int to a fixed-length (qLen) big-endian byte slice.
func int2octets(v *big.Int, qLen int) []byte {
out := v.Bytes()
if len(out) >= qLen {
return out[:qLen]
}
padded := make([]byte, qLen)
copy(padded[qLen-len(out):], out)
return padded
}
// bits2int interprets a byte slice as a big-endian integer and reduces to
// the bit length of n.
func bits2int(b []byte, n *big.Int) *big.Int {
v := new(big.Int).SetBytes(b)
excess := len(b)*8 - n.BitLen()
if excess > 0 {
v.Rsh(v, uint(excess))
}
return v
}
// bits2octets converts a hash to a fixed-length byte slice reduced modulo n.
func bits2octets(hash []byte, n *big.Int, qLen int) []byte {
z := bits2int(hash, n)
if z.Cmp(n) >= 0 {
z.Sub(z, n)
}
return int2octets(z, qLen)
}
================================================
FILE: pkg/talos/rfc6979_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos //nolint:testpackage
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"encoding/hex"
"math/big"
"testing"
)
// TestRFC6979SignTestVectors validates the RFC 6979 implementation against
// the official test vectors from RFC 6979 Appendix A.2.5 (ECDSA P-256 / SHA-256).
func TestRFC6979SignTestVectors(t *testing.T) {
t.Parallel()
// Private key from RFC 6979 A.2.5.
xHex := "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721"
xBytes, err := hex.DecodeString(xHex)
if err != nil {
t.Fatalf("failed to decode test private key hex: %v", err)
}
key, err := ecdsa.ParseRawPrivateKey(elliptic.P256(), xBytes)
if err != nil {
t.Fatalf("failed to parse test private key: %v", err)
}
tests := []struct {
name string
message string
expectedR string
expectedS string
}{
{
name: "sample",
message: "sample",
expectedR: "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716",
expectedS: "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8",
},
{
name: "test",
message: "test",
expectedR: "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367",
expectedS: "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
hash := sha256.Sum256([]byte(tc.message))
r, s := rfc6979Sign(key, hash[:])
expectedR, _ := new(big.Int).SetString(tc.expectedR, 16)
expectedS, _ := new(big.Int).SetString(tc.expectedS, 16)
if r.Cmp(expectedR) != 0 {
t.Errorf("r mismatch for %q:\n got: %064X\n want: %064X", tc.message, r, expectedR)
}
if s.Cmp(expectedS) != 0 {
t.Errorf("s mismatch for %q:\n got: %064X\n want: %064X", tc.message, s, expectedS)
}
})
}
}
================================================
FILE: pkg/talos/talos_client_configuration_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
type talosClientConfigurationDataSource struct{}
type talosClientConfigurationDataSourceModelV0 struct {
ID types.String `tfsdk:"id"`
ClusterName types.String `tfsdk:"cluster_name"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
Endpoints types.List `tfsdk:"endpoints"`
Nodes types.List `tfsdk:"nodes"`
TalosConfig types.String `tfsdk:"talos_config"`
}
var _ datasource.DataSource = &talosClientConfigurationDataSource{}
// NewTalosClientConfigurationDataSource implements the datasource.DataSource interface.
func NewTalosClientConfigurationDataSource() datasource.DataSource {
return &talosClientConfigurationDataSource{}
}
func (d *talosClientConfigurationDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_client_configuration"
}
func (d *talosClientConfigurationDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generate client configuration for a Talos cluster",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The ID of this resource",
Computed: true,
},
"cluster_name": schema.StringAttribute{
Required: true,
Description: "The name of the cluster in the generated config",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Required: true,
Description: "The client configuration data",
},
"endpoints": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "endpoints to set in the generated config",
},
"nodes": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "nodes to set in the generated config",
},
"talos_config": schema.StringAttribute{
Computed: true,
Description: "The generated client configuration",
Sensitive: true,
},
},
}
}
func (d *talosClientConfigurationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state talosClientConfigurationDataSourceModelV0
diags := req.Config.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var endpoints []string
var nodes []string
resp.Diagnostics.Append(state.Endpoints.ElementsAs(ctx, &endpoints, true)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(state.Nodes.ElementsAs(ctx, &nodes, true)...)
if resp.Diagnostics.HasError() {
return
}
talosConfig, err := talosClientTFConfigToTalosClientConfig(
state.ClusterName.ValueString(),
state.ClientConfiguration.CA.ValueString(),
state.ClientConfiguration.Cert.ValueString(),
state.ClientConfiguration.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
if len(endpoints) > 0 {
talosConfig.Contexts[state.ClusterName.ValueString()].Endpoints = endpoints
}
if len(nodes) > 0 {
talosConfig.Contexts[state.ClusterName.ValueString()].Nodes = nodes
}
talosConfigStringBytes, err := talosConfig.Bytes()
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
state.TalosConfig = basetypes.NewStringValue(string(talosConfigStringBytes))
state.ID = state.ClusterName
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_client_configuration_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"strings"
"testing"
"text/template"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/siderolabs/talos/pkg/machinery/client/config"
"github.com/stretchr/testify/assert"
"go.yaml.in/yaml/v4"
)
func TestAccTalosClientConfigurationDataSource(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test data source with default values
{
Config: testAccTalosClientConfigurationDataSourceConfig("test-cluster", nil, nil),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "id", "test-cluster"),
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "cluster_name", "test-cluster"),
resource.TestCheckResourceAttrSet("data.talos_client_configuration.this", "client_configuration.%"),
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "endpoints.#", "0"),
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "nodes.#", "0"),
resource.TestCheckResourceAttrSet("data.talos_client_configuration.this", "talos_config"),
resource.TestCheckResourceAttrWith("data.talos_client_configuration.this", "talos_config", func(value string) error {
return validateTalosClientConfigContext(t, value, "test-cluster", nil, nil)
}),
),
},
// test data source with overrides
{
Config: testAccTalosClientConfigurationDataSourceConfig("test-cluster-1", []string{"10.5.0.2", "10.5.0.3"}, []string{"10.5.0.4"}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "id", "test-cluster-1"),
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "cluster_name", "test-cluster-1"),
resource.TestCheckResourceAttrSet("data.talos_client_configuration.this", "client_configuration.%"),
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "endpoints.0", "10.5.0.2"),
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "endpoints.1", "10.5.0.3"),
resource.TestCheckResourceAttr("data.talos_client_configuration.this", "nodes.0", "10.5.0.4"),
resource.TestCheckResourceAttrSet("data.talos_client_configuration.this", "talos_config"),
resource.TestCheckResourceAttrWith("data.talos_client_configuration.this", "talos_config", func(value string) error {
return validateTalosClientConfigContext(t, value, "test-cluster-1", []string{"10.5.0.2", "10.5.0.3"}, []string{"10.5.0.4"})
}),
),
},
},
})
}
func testAccTalosClientConfigurationDataSourceConfig(clusterName string, endpoints, nodes []string) string {
configTemplate := `
resource "talos_machine_secrets" "this" {}
data "talos_client_configuration" "this" {
cluster_name = "{{ .ClusterName }}"
client_configuration = talos_machine_secrets.this.client_configuration
{{if .Endpoints }}endpoints = [{{- range .Endpoints }}
"{{ . }}",
{{- end }}
]{{end }}
{{if .Nodes }}nodes = [{{- range .Nodes }}
"{{ . }}",
{{- end }}
]{{end }}
}
`
var config strings.Builder
template.Must(template.New("tf_config").Parse(configTemplate)).Execute(&config, struct { //nolint:errcheck
ClusterName string
Endpoints []string
Nodes []string
}{
ClusterName: clusterName,
Endpoints: endpoints,
Nodes: nodes,
})
return config.String()
}
func validateTalosClientConfigContext(t *testing.T, tc, contextName string, endpoints, nodes []string) error {
var talosConfig config.Config
if err := yaml.Unmarshal([]byte(tc), &talosConfig); err != nil {
return err
}
assert.Equal(t, contextName, talosConfig.Context)
if endpoints != nil {
assert.Equal(t, endpoints, talosConfig.Contexts[contextName].Endpoints)
}
if nodes != nil {
assert.Equal(t, nodes, talosConfig.Contexts[contextName].Nodes)
}
return nil
}
================================================
FILE: pkg/talos/talos_client_configuration_ephemeral_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
var _ ephemeral.EphemeralResource = &talosClientConfigurationEphemeralResource{}
type talosClientConfigurationEphemeralResource struct{}
type talosClientConfigurationEphemeralResourceModel struct {
ClusterName types.String `tfsdk:"cluster_name"`
MachineSecrets machineSecrets `tfsdk:"machine_secrets"`
NotBefore types.String `tfsdk:"not_before"`
CrtTTL types.String `tfsdk:"crt_ttl"`
Endpoints types.List `tfsdk:"endpoints"`
Nodes types.List `tfsdk:"nodes"`
TalosConfig types.String `tfsdk:"talos_config"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
}
// NewTalosClientConfigurationEphemeralResource implements the ephemeral.EphemeralResource interface.
func NewTalosClientConfigurationEphemeralResource() ephemeral.EphemeralResource {
return &talosClientConfigurationEphemeralResource{}
}
func (r *talosClientConfigurationEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_client_configuration"
}
func (r *talosClientConfigurationEphemeralResource) Schema(_ context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generate client configuration for a Talos cluster from machine secrets. " +
"This is an ephemeral resource that does not persist secrets in Terraform state. " +
"The admin client certificate is generated with pinned timestamps so talos_config " +
"is byte-identical on every open as long as machine_secrets and not_before are unchanged.",
Attributes: map[string]schema.Attribute{
"cluster_name": schema.StringAttribute{
Required: true,
Description: "The name of the cluster in the generated config",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"machine_secrets": machineSecretsSchemaAttribute(),
"not_before": schema.StringAttribute{
Optional: true,
Description: "RFC3339 timestamp to use as the NotBefore field of the generated admin client certificate. " +
"When set, the certificate validity starts at this time and ends at not_before + crt_ttl. " +
"Persist this value in a terraform_data resource so it is stable across plans and the " +
"generated talos_config is byte-identical on every open. " +
"When omitted, the certificate uses the OS CA's own NotBefore/NotAfter timestamps.",
Validators: []validator.String{
rfc3339Valid(),
},
},
"crt_ttl": schema.StringAttribute{
Optional: true,
Description: "The lifetime of the generated admin client certificate as a Go duration string " +
"(e.g. \"8760h\" for 1 year, \"87600h\" for 10 years). Defaults to \"87600h\" (10 years). " +
"Only used when not_before is set; when not_before is omitted the cert uses the OS CA's NotAfter directly.",
Validators: []validator.String{
goDurationValid(),
},
},
"endpoints": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "endpoints to set in the generated config",
},
"nodes": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "nodes to set in the generated config",
},
"talos_config": schema.StringAttribute{
Computed: true,
Description: "The generated client configuration",
Sensitive: true,
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Computed: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Computed: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Computed: true,
Sensitive: true,
Description: "The client key",
},
},
Computed: true,
Sensitive: true,
Description: "The generated client configuration data",
},
},
}
}
func (r *talosClientConfigurationEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosClientConfigurationEphemeralResourceModel
diags = obj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var endpoints []string
var nodes []string
resp.Diagnostics.Append(config.Endpoints.ElementsAs(ctx, &endpoints, true)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(config.Nodes.ElementsAs(ctx, &nodes, true)...)
if resp.Diagnostics.HasError() {
return
}
secretsBundle, err := machineSecretsToSecretsBundle(talosMachineSecretsResourceModelV1{
MachineSecrets: config.MachineSecrets,
})
if err != nil {
resp.Diagnostics.AddError("failed to convert machine secrets to secrets bundle", err.Error())
return
}
notBefore, notAfter, tsErr := resolveClientConfigTimestamps(config.NotBefore.ValueString(), config.CrtTTL.ValueString(), secretsBundle.Certs.OS.Crt)
if tsErr != nil {
resp.Diagnostics.AddError(tsErr.summary, tsErr.detail)
return
}
cc, err := generateClientConfiguration(secretsBundle, config.ClusterName.ValueString(), notBefore, notAfter)
if err != nil {
resp.Diagnostics.AddError("failed to generate client configuration", err.Error())
return
}
talosConfig, err := talosClientTFConfigToTalosClientConfig(
config.ClusterName.ValueString(),
cc.CA.ValueString(),
cc.Cert.ValueString(),
cc.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
if len(endpoints) > 0 {
talosConfig.Contexts[config.ClusterName.ValueString()].Endpoints = endpoints
}
if len(nodes) > 0 {
talosConfig.Contexts[config.ClusterName.ValueString()].Nodes = nodes
}
talosConfigStringBytes, err := talosConfig.Bytes()
if err != nil {
resp.Diagnostics.AddError("failed to serialize talos config", err.Error())
return
}
result := talosClientConfigurationEphemeralResourceModel{
ClusterName: config.ClusterName,
MachineSecrets: config.MachineSecrets,
NotBefore: config.NotBefore,
CrtTTL: config.CrtTTL,
Endpoints: config.Endpoints,
Nodes: config.Nodes,
TalosConfig: basetypes.NewStringValue(string(talosConfigStringBytes)),
ClientConfiguration: cc,
}
diags = resp.Result.Set(ctx, &result)
resp.Diagnostics.Append(diags...)
}
================================================
FILE: pkg/talos/talos_client_configuration_ephemeral_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"regexp"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
// TestAccTalosClientConfigurationEphemeralResourceFromMachineSecrets tests that the ephemeral
// resource generates talos_config from machine_secrets.
func TestAccTalosClientConfigurationEphemeralResourceFromMachineSecrets(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_client_configuration" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoints = ["10.0.0.1"]
nodes = ["10.0.0.2"]
}
provider "echo" {
data = {
cluster_name = ephemeral.talos_client_configuration.this.cluster_name
talos_config = ephemeral.talos_client_configuration.this.talos_config
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("talos_config"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("cluster_name"), knownvalue.StringExact("test-cluster")),
},
},
},
})
}
// TestAccTalosClientConfigurationEphemeralResourceCustomTTL tests that not_before + crt_ttl
// are accepted and the resource opens without error.
func TestAccTalosClientConfigurationEphemeralResourceCustomTTL(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_client_configuration" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
not_before = "2024-01-01T00:00:00Z"
crt_ttl = "8760h"
}
provider "echo" {
data = {
talos_config = ephemeral.talos_client_configuration.this.talos_config
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("talos_config"), knownvalue.NotNull()),
},
},
},
})
}
// TestAccTalosClientConfigurationEphemeralResourceCrtTTLInvalid tests that an invalid
// crt_ttl value is rejected with an error.
func TestAccTalosClientConfigurationEphemeralResourceCrtTTLInvalid(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_client_configuration" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
crt_ttl = "not-a-duration"
}
provider "echo" {
data = {}
}
resource "echo" "test" {}
`,
ExpectError: regexp.MustCompile(`invalid crt_ttl`),
},
},
})
}
// TestAccTalosClientConfigurationEphemeralResourceDeterminism tests that two opens with
// identical inputs (CA-pinned, no not_before) produce byte-identical talos_config.
func TestAccTalosClientConfigurationEphemeralResourceDeterminism(t *testing.T) {
t.Parallel()
cfg := `
resource "talos_machine_secrets" "this" {}
ephemeral "talos_client_configuration" "this" {
cluster_name = "test-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
endpoints = ["10.0.0.1"]
}
provider "echo" {
data = {
talos_config = ephemeral.talos_client_configuration.this.talos_config
}
}
resource "echo" "test" {}
`
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: cfg,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("talos_config"), knownvalue.NotNull()),
},
},
{
Config: cfg,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
},
},
})
}
// TestAccTalosClientConfigurationEphemeralResourceDeterminismWithNotBefore tests that two
// opens with a terraform_data-sourced not_before produce byte-identical talos_config.
func TestAccTalosClientConfigurationEphemeralResourceDeterminismWithNotBefore(t *testing.T) {
t.Parallel()
cfg := `
resource "talos_machine_secrets" "this" {}
resource "terraform_data" "client_config_nbf" {
input = "2024-01-01T00:00:00Z"
}
ephemeral "talos_client_configuration" "this" {
cluster_name = "test-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
not_before = terraform_data.client_config_nbf.output
crt_ttl = "87600h"
}
provider "echo" {
data = {
talos_config = ephemeral.talos_client_configuration.this.talos_config
}
}
resource "echo" "test" {}
`
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: cfg,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("talos_config"), knownvalue.NotNull()),
},
},
{
Config: cfg,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
},
},
})
}
// TestAccTalosClientConfigurationEphemeralResourceInvalidNotBefore tests that an invalid
// not_before is rejected with an error.
func TestAccTalosClientConfigurationEphemeralResourceInvalidNotBefore(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_client_configuration" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
not_before = "not-a-timestamp"
}
provider "echo" {
data = {}
}
resource "echo" "test" {}
`,
ExpectError: regexp.MustCompile(`invalid not_before`),
},
},
})
}
// TestAccTalosClientConfigurationEphemeralResourceClientConfigurationOutput tests that
// client_configuration is populated with ca_certificate, client_certificate, and client_key.
func TestAccTalosClientConfigurationEphemeralResourceClientConfigurationOutput(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_client_configuration" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
}
provider "echo" {
data = {
ca_certificate = ephemeral.talos_client_configuration.this.client_configuration.ca_certificate
client_certificate = ephemeral.talos_client_configuration.this.client_configuration.client_certificate
client_key = ephemeral.talos_client_configuration.this.client_configuration.client_key
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("ca_certificate"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("client_certificate"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("client_key"), knownvalue.NotNull()),
},
},
},
})
}
================================================
FILE: pkg/talos/talos_cluster_health_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"fmt"
"slices"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/talos/pkg/cluster"
"github.com/siderolabs/talos/pkg/cluster/check"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/client"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
)
type talosClusterHealthDataSource struct{}
var _ datasource.DataSource = &talosClusterHealthDataSource{}
type talosClusterHealthDataSourceModelV0 struct {
ID types.String `tfsdk:"id"`
Endpoints types.List `tfsdk:"endpoints"`
ControlPlaneNodes types.List `tfsdk:"control_plane_nodes"`
WorkerNodes types.List `tfsdk:"worker_nodes"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
SkipKubernetesChecks types.Bool `tfsdk:"skip_kubernetes_checks"`
}
type clusterNodes struct {
nodesByType map[machine.Type][]cluster.NodeInfo
nodes []cluster.NodeInfo
}
func newClusterNodes(controlPlaneNodes, workerNodes []string) (*clusterNodes, error) {
controlPlaneNodeInfos, err := cluster.IPsToNodeInfos(controlPlaneNodes)
if err != nil {
return nil, err
}
workerNodeInfos, err := cluster.IPsToNodeInfos(workerNodes)
if err != nil {
return nil, err
}
nodesByType := make(map[machine.Type][]cluster.NodeInfo)
nodesByType[machine.TypeControlPlane] = controlPlaneNodeInfos
nodesByType[machine.TypeWorker] = workerNodeInfos
return &clusterNodes{
nodes: slices.Concat(controlPlaneNodeInfos, workerNodeInfos),
nodesByType: nodesByType,
}, nil
}
// Nodes returns cluster nodeinfos.
func (c *clusterNodes) Nodes() []cluster.NodeInfo {
return c.nodes
}
// NodesByType returns cluster nodeinfos by type.
func (c *clusterNodes) NodesByType(t machine.Type) []cluster.NodeInfo {
return c.nodesByType[t]
}
type reporter struct {
lastLine string
s strings.Builder
}
func newReporter() *reporter {
return &reporter{}
}
// Update implements the conditions.Reporter interface.
func (r *reporter) Update(condition conditions.Condition) {
if condition.String() != r.lastLine {
fmt.Fprintf(&r.s, "waiting for %s\n", condition.String())
r.lastLine = condition.String()
}
}
// String returns the string representation of the reporter.
func (r *reporter) String() string {
return r.s.String()
}
// NewTalosClusterHealthDataSource implements the datasource.DataSource interface.
func NewTalosClusterHealthDataSource() datasource.DataSource {
return &talosClusterHealthDataSource{}
}
func (d *talosClusterHealthDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_cluster_health"
}
func (d *talosClusterHealthDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Checks the health of a Talos cluster",
MarkdownDescription: "Waits for the Talos cluster to be healthy. Can be used as a dependency before running other operations on the cluster.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"endpoints": schema.ListAttribute{
Required: true,
ElementType: types.StringType,
Description: "endpoints to use for the health check client. Use at least one control plane endpoint.",
},
"control_plane_nodes": schema.ListAttribute{
Required: true,
ElementType: types.StringType,
Description: "List of control plane nodes to check for health.",
},
"worker_nodes": schema.ListAttribute{
Optional: true,
ElementType: types.StringType,
Description: "List of worker nodes to check for health.",
},
"skip_kubernetes_checks": schema.BoolAttribute{
Optional: true,
Description: "Skip Kubernetes component checks, this is useful to check if the nodes has finished booting up and kubelet is running. Default is false.",
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Required: true,
Description: "The client configuration data",
},
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Read: true,
}),
},
}
}
func (d *talosClusterHealthDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state talosClusterHealthDataSourceModelV0
diags := req.Config.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var (
endpoints []string
controlPlaneNodes []string
workerNodes []string
)
resp.Diagnostics.Append(state.Endpoints.ElementsAs(ctx, &endpoints, true)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(state.ControlPlaneNodes.ElementsAs(ctx, &controlPlaneNodes, true)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(state.WorkerNodes.ElementsAs(ctx, &workerNodes, true)...)
if resp.Diagnostics.HasError() {
return
}
readTimeout, diags := state.Timeouts.Read(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
talosConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
state.ClientConfiguration.CA.ValueString(),
state.ClientConfiguration.Cert.ValueString(),
state.ClientConfiguration.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
c, err := client.New(ctx, client.WithConfig(talosConfig), client.WithEndpoints(endpoints...))
if err != nil {
resp.Diagnostics.AddError("failed to create talos client", err.Error())
return
}
defer c.Close() //nolint:errcheck
clientProvider := &cluster.ConfigClientProvider{
DefaultClient: c,
}
defer clientProvider.Close() //nolint:errcheck
nodeInfos, err := newClusterNodes(controlPlaneNodes, workerNodes)
if err != nil {
resp.Diagnostics.AddError("failed to generate node infos", err.Error())
return
}
clusterState := struct {
cluster.ClientProvider
cluster.K8sProvider
cluster.Info
}{
ClientProvider: clientProvider,
K8sProvider: &cluster.KubernetesClient{
ClientProvider: clientProvider,
},
Info: nodeInfos,
}
// Run cluster readiness checks
checkCtx, checkCtxCancel := context.WithTimeout(ctx, readTimeout)
defer checkCtxCancel()
reporter := newReporter()
checks := check.PreBootSequenceChecks()
if !state.SkipKubernetesChecks.ValueBool() {
checks = check.DefaultClusterChecks()
}
if err := check.Wait(checkCtx, &clusterState, checks, reporter); err != nil {
resp.Diagnostics.AddWarning("failed checks", reporter.String())
resp.Diagnostics.AddError("cluster health check failed", err.Error())
return
}
state.ID = basetypes.NewStringValue("cluster_health")
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_cluster_health_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccTalosClusterHealthDataSource(t *testing.T) {
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosClusterHealthDataSourceConfig("talos", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_cluster_health.this", "id", "cluster_health"),
),
},
// make sure there are no changes
{
Config: testAccTalosClusterHealthDataSourceConfig("talos", rName),
PlanOnly: true,
},
},
})
}
func testAccTalosClusterHealthDataSourceConfig(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: true,
WithRetrieveKubeConfig: true,
WithClusterHealth: true,
}
return config.render()
}
================================================
FILE: pkg/talos/talos_cluster_health_ephemeral_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/siderolabs/talos/pkg/cluster"
"github.com/siderolabs/talos/pkg/cluster/check"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/client"
)
var _ ephemeral.EphemeralResource = &talosClusterHealthEphemeralResource{}
type talosClusterHealthEphemeralResource struct{}
type talosClusterHealthEphemeralResourceModel struct {
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
Endpoints types.List `tfsdk:"endpoints"`
ControlPlaneNodes types.List `tfsdk:"control_plane_nodes"`
WorkerNodes types.List `tfsdk:"worker_nodes"`
Timeout types.String `tfsdk:"timeout"`
SkipKubernetesChecks types.Bool `tfsdk:"skip_kubernetes_checks"`
}
type healthReporter struct {
lastLine string
s strings.Builder
}
func newHealthReporter() *healthReporter {
return &healthReporter{}
}
// Update implements the conditions.Reporter interface.
func (r *healthReporter) Update(condition conditions.Condition) {
if condition.String() != r.lastLine {
fmt.Fprintf(&r.s, "waiting for %s\n", condition.String())
r.lastLine = condition.String()
}
}
// String returns the string representation of the reporter.
func (r *healthReporter) String() string {
return r.s.String()
}
// NewTalosClusterHealthEphemeralResource implements the ephemeral.EphemeralResource interface.
func NewTalosClusterHealthEphemeralResource() ephemeral.EphemeralResource {
return &talosClusterHealthEphemeralResource{}
}
func (r *talosClusterHealthEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_cluster_health"
}
func (r *talosClusterHealthEphemeralResource) Schema(_ context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Checks the health of a Talos cluster. This is an ephemeral resource that does not persist secrets in Terraform state.",
Attributes: map[string]schema.Attribute{
"endpoints": schema.ListAttribute{
Required: true,
ElementType: types.StringType,
Description: "endpoints to use for the health check client. Use at least one control plane endpoint.",
},
"control_plane_nodes": schema.ListAttribute{
Required: true,
ElementType: types.StringType,
Description: "List of control plane nodes to check for health.",
},
"worker_nodes": schema.ListAttribute{
Optional: true,
ElementType: types.StringType,
Description: "List of worker nodes to check for health.",
},
"skip_kubernetes_checks": schema.BoolAttribute{
Optional: true,
Description: "Skip Kubernetes component checks, this is useful to check if the nodes has finished booting up and kubelet is running. Default is false.",
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Required: true,
Description: "The client configuration data",
},
"timeout": schema.StringAttribute{
Optional: true,
Description: "Timeout for the health check. Defaults to 10m. Valid time units are 'ns', 'us' (or 'µs'), 'ms', 's', 'm', 'h'.",
},
},
}
}
func (r *talosClusterHealthEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var config talosClusterHealthEphemeralResourceModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var (
endpoints []string
controlPlaneNodes []string
workerNodes []string
)
resp.Diagnostics.Append(config.Endpoints.ElementsAs(ctx, &endpoints, true)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(config.ControlPlaneNodes.ElementsAs(ctx, &controlPlaneNodes, true)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(config.WorkerNodes.ElementsAs(ctx, &workerNodes, true)...)
if resp.Diagnostics.HasError() {
return
}
// Parse timeout
timeout := 10 * time.Minute
if !config.Timeout.IsNull() && !config.Timeout.IsUnknown() {
var err error
timeout, err = time.ParseDuration(config.Timeout.ValueString())
if err != nil {
resp.Diagnostics.AddError("Invalid timeout", fmt.Sprintf("Unable to parse timeout: %s", err.Error()))
return
}
}
talosConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
config.ClientConfiguration.CA.ValueString(),
config.ClientConfiguration.Cert.ValueString(),
config.ClientConfiguration.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
c, err := client.New(ctx, client.WithConfig(talosConfig), client.WithEndpoints(endpoints...))
if err != nil {
resp.Diagnostics.AddError("failed to create talos client", err.Error())
return
}
defer c.Close() //nolint:errcheck
clientProvider := &cluster.ConfigClientProvider{
DefaultClient: c,
}
defer clientProvider.Close() //nolint:errcheck
nodeInfos, err := newClusterNodes(controlPlaneNodes, workerNodes)
if err != nil {
resp.Diagnostics.AddError("failed to generate node infos", err.Error())
return
}
clusterState := struct {
cluster.ClientProvider
cluster.K8sProvider
cluster.Info
}{
ClientProvider: clientProvider,
K8sProvider: &cluster.KubernetesClient{
ClientProvider: clientProvider,
},
Info: nodeInfos,
}
// Run cluster readiness checks
checkCtx, checkCtxCancel := context.WithTimeout(ctx, timeout)
defer checkCtxCancel()
reporter := newHealthReporter()
checks := check.PreBootSequenceChecks()
if !config.SkipKubernetesChecks.ValueBool() {
checks = check.DefaultClusterChecks()
}
if err := check.Wait(checkCtx, &clusterState, checks, reporter); err != nil {
resp.Diagnostics.AddWarning("failed checks", reporter.String())
resp.Diagnostics.AddError("cluster health check failed", err.Error())
return
}
// Set result - ephemeral resources can set a result or just complete successfully
resp.Diagnostics.Append(resp.Result.Set(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_cluster_kubeconfig_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"time"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/siderolabs/talos/pkg/machinery/client"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/client-go/tools/clientcmd"
)
type talosClusterKubeConfigDataSource struct{}
type talosClusterKubeConfigDataSourceModelV0 struct { //nolint:govet
ID types.String `tfsdk:"id"`
Node types.String `tfsdk:"node"`
Endpoint types.String `tfsdk:"endpoint"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
KubeConfigRaw types.String `tfsdk:"kubeconfig_raw"`
KubernetesClientConfiguration kubernetesClientConfiguration `tfsdk:"kubernetes_client_configuration"`
Wait types.Bool `tfsdk:"wait"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
var _ datasource.DataSource = &talosClusterKubeConfigDataSource{}
// NewTalosClusterKubeConfigDataSource implements the datasource.DataSource interface.
func NewTalosClusterKubeConfigDataSource() datasource.DataSource {
return &talosClusterKubeConfigDataSource{}
}
func (d *talosClusterKubeConfigDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_cluster_kubeconfig"
}
func (d *talosClusterKubeConfigDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
DeprecationMessage: "Use `talos_cluster_kubeconfig` resource instead. This data source will be removed in the next minor version of the provider.",
Description: "Retrieves the kubeconfig for a Talos cluster",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"node": schema.StringAttribute{
Required: true,
Description: "controlplane node to retrieve the kubeconfig from",
},
"endpoint": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "endpoint to use for the talosclient. If not set, the node value will be used",
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Required: true,
Description: "The client configuration data",
},
"wait": schema.BoolAttribute{
Optional: true,
Description: "Wait for the kubernetes api to be available",
DeprecationMessage: "This attribute is deprecated and no-op. Will be removed in a future version. Use talos_cluster_health instead.",
},
"kubeconfig_raw": schema.StringAttribute{
Computed: true,
Description: "The raw kubeconfig",
Sensitive: true,
},
"kubernetes_client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"host": schema.StringAttribute{
Computed: true,
Description: "The kubernetes host",
},
"ca_certificate": schema.StringAttribute{
Computed: true,
Description: "The kubernetes CA certificate",
},
"client_certificate": schema.StringAttribute{
Computed: true,
Description: "The kubernetes client certificate",
},
"client_key": schema.StringAttribute{
Computed: true,
Sensitive: true,
Description: "The kubernetes client key",
},
},
Computed: true,
Description: "The kubernetes client configuration",
},
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Read: true,
}),
},
}
}
// Read implements the datasource.DataSource interface.
func (d *talosClusterKubeConfigDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var state talosClusterKubeConfigDataSourceModelV0
diags = obj.As(ctx, &state, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
talosConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
state.ClientConfiguration.CA.ValueString(),
state.ClientConfiguration.Cert.ValueString(),
state.ClientConfiguration.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
if state.Endpoint.IsNull() {
state.Endpoint = state.Node
}
readTimeout, diags := state.Timeouts.Read(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctxDeadline, cancel := context.WithTimeout(ctx, readTimeout)
defer cancel()
if retryErr := retry.RetryContext(ctxDeadline, readTimeout, func() *retry.RetryError {
if clientOpErr := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosConfig, func(nodeCtx context.Context, c *client.Client) error {
kubeConfigBytes, clientErr := c.Kubeconfig(nodeCtx)
if clientErr != nil {
return clientErr
}
state.KubeConfigRaw = basetypes.NewStringValue(string(kubeConfigBytes))
return nil
}); clientOpErr != nil {
if s := status.Code(clientOpErr); s == codes.InvalidArgument {
return retry.NonRetryableError(clientOpErr)
}
return retry.RetryableError(clientOpErr)
}
return nil
}); retryErr != nil {
resp.Diagnostics.AddError("failed to retrieve kubeconfig", retryErr.Error())
return
}
kubeConfig, err := clientcmd.Load([]byte(state.KubeConfigRaw.ValueString()))
if err != nil {
resp.Diagnostics.AddError("failed to parse kubeconfig", err.Error())
return
}
clusterName := kubeConfig.Contexts[kubeConfig.CurrentContext].Cluster
authName := kubeConfig.Contexts[kubeConfig.CurrentContext].AuthInfo
state.KubernetesClientConfiguration = kubernetesClientConfiguration{
Host: basetypes.NewStringValue(kubeConfig.Clusters[clusterName].Server),
CACertificate: basetypes.NewStringValue(bytesToBase64(kubeConfig.Clusters[clusterName].CertificateAuthorityData)),
ClientCertificate: basetypes.NewStringValue(bytesToBase64(kubeConfig.AuthInfos[authName].ClientCertificateData)),
ClientKey: basetypes.NewStringValue(bytesToBase64(kubeConfig.AuthInfos[authName].ClientKeyData)),
}
state.ID = basetypes.NewStringValue(clusterName)
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_cluster_kubeconfig_ephemeral_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
stdlibx509 "crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
"golang.org/x/crypto/hkdf"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
var _ ephemeral.EphemeralResource = &talosClusterKubeConfigEphemeralResource{}
type talosClusterKubeConfigEphemeralResource struct{}
type talosClusterKubeConfigEphemeralResourceModel struct {
MachineSecrets machineSecrets `tfsdk:"machine_secrets"`
ClusterName types.String `tfsdk:"cluster_name"`
Endpoint types.String `tfsdk:"endpoint"`
NotBefore types.String `tfsdk:"not_before"`
CrtTTL types.String `tfsdk:"crt_ttl"`
KubeConfigRaw types.String `tfsdk:"kubeconfig_raw"`
KubernetesClientConfiguration kubernetesClientConfiguration `tfsdk:"kubernetes_client_configuration"`
}
// NewTalosClusterKubeConfigEphemeralResource implements the ephemeral.EphemeralResource interface.
func NewTalosClusterKubeConfigEphemeralResource() ephemeral.EphemeralResource {
return &talosClusterKubeConfigEphemeralResource{}
}
func (r *talosClusterKubeConfigEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_cluster_kubeconfig"
}
func (r *talosClusterKubeConfigEphemeralResource) Schema(_ context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generate a kubeconfig for a Talos cluster from machine secrets. " +
"This is an ephemeral resource that does not persist secrets in Terraform state. " +
"The admin client certificate is generated with pinned timestamps so kubeconfig_raw " +
"is byte-identical on every open as long as machine_secrets and not_before are unchanged.",
Attributes: map[string]schema.Attribute{
"machine_secrets": machineSecretsSchemaAttribute(),
"cluster_name": schema.StringAttribute{
Required: true,
Description: "The name of the cluster; embedded in the kubeconfig context and cluster names",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"endpoint": schema.StringAttribute{
Required: true,
Description: "The Kubernetes API server URL to embed in the kubeconfig (e.g. https://1.2.3.4:6443)",
},
"not_before": schema.StringAttribute{
Optional: true,
Description: "RFC3339 timestamp to use as the NotBefore field of the generated admin client certificate. " +
"When set, the certificate validity starts at this time and ends at not_before + crt_ttl. " +
"Persist this value in a terraform_data resource so it is stable across plans and the " +
"generated kubeconfig_raw is byte-identical on every open. " +
"When omitted, the certificate uses the K8s CA's own NotBefore/NotAfter timestamps.",
Validators: []validator.String{
rfc3339Valid(),
},
},
"crt_ttl": schema.StringAttribute{
Optional: true,
Description: "The lifetime of the generated admin client certificate as a Go duration string " +
"(e.g. \"8760h\" for 1 year, \"87600h\" for 10 years). Defaults to \"87600h\" (10 years). " +
"Only used when not_before is set; when not_before is omitted the cert uses the K8s CA's NotAfter directly.",
Validators: []validator.String{
goDurationValid(),
},
},
"kubeconfig_raw": schema.StringAttribute{
Computed: true,
Description: "The raw kubeconfig",
Sensitive: true,
},
"kubernetes_client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"host": schema.StringAttribute{
Computed: true,
Description: "The kubernetes host",
},
"ca_certificate": schema.StringAttribute{
Computed: true,
Description: "The kubernetes CA certificate",
},
"client_certificate": schema.StringAttribute{
Computed: true,
Description: "The kubernetes client certificate",
},
"client_key": schema.StringAttribute{
Computed: true,
Sensitive: true,
Description: "The kubernetes client key",
},
},
Computed: true,
Description: "The kubernetes client configuration",
},
},
}
}
func (r *talosClusterKubeConfigEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosClusterKubeConfigEphemeralResourceModel
diags = obj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
secretsBundle, err := machineSecretsToSecretsBundle(talosMachineSecretsResourceModelV1{
MachineSecrets: config.MachineSecrets,
})
if err != nil {
resp.Diagnostics.AddError("failed to convert machine secrets to secrets bundle", err.Error())
return
}
notBefore, notAfter, tsErr := resolveClientConfigTimestamps(config.NotBefore.ValueString(), config.CrtTTL.ValueString(), secretsBundle.Certs.K8s.Crt)
if tsErr != nil {
resp.Diagnostics.AddError(tsErr.summary, tsErr.detail)
return
}
kc, err := GenerateKubeconfig(secretsBundle, config.ClusterName.ValueString(), config.Endpoint.ValueString(), notBefore, notAfter)
if err != nil {
resp.Diagnostics.AddError("failed to generate kubeconfig", err.Error())
return
}
result := talosClusterKubeConfigEphemeralResourceModel{
MachineSecrets: config.MachineSecrets,
ClusterName: config.ClusterName,
Endpoint: config.Endpoint,
NotBefore: config.NotBefore,
CrtTTL: config.CrtTTL,
KubeConfigRaw: basetypes.NewStringValue(kc.Raw),
KubernetesClientConfiguration: kubernetesClientConfiguration{
Host: basetypes.NewStringValue(config.Endpoint.ValueString()),
CACertificate: basetypes.NewStringValue(bytesToBase64(secretsBundle.Certs.K8s.Crt)),
ClientCertificate: basetypes.NewStringValue(bytesToBase64(kc.ClientCertPEM)),
ClientKey: basetypes.NewStringValue(bytesToBase64(kc.ClientKeyPEM)),
},
}
diags = resp.Result.Set(ctx, &result)
resp.Diagnostics.Append(diags...)
}
// KubeconfigResult holds the generated kubeconfig and its components.
type KubeconfigResult struct {
Raw string // Full kubeconfig YAML
ClientCertPEM []byte // PEM-encoded admin client certificate
ClientKeyPEM []byte // PEM-encoded admin client private key
}
// GenerateKubeconfig generates a kubeconfig from the provided secrets bundle.
//
// The admin client certificate and key are derived deterministically using HKDF
// (RFC 5869) seeded from the K8s CA private key and all cert-relevant inputs.
// Same inputs always produce byte-identical output — no crypto/rand is used.
func GenerateKubeconfig(bundle *secrets.Bundle, clusterName, endpoint string, notBefore, notAfter time.Time) (*KubeconfigResult, error) {
// Parse CA certificate and private key from PEM.
caCertBlock, _ := pem.Decode(bundle.Certs.K8s.Crt)
if caCertBlock == nil {
return nil, fmt.Errorf("error decoding K8s CA certificate PEM")
}
caCert, err := stdlibx509.ParseCertificate(caCertBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("error parsing K8s CA certificate: %w", err)
}
caKeyBlock, _ := pem.Decode(bundle.Certs.K8s.Key)
if caKeyBlock == nil {
return nil, fmt.Errorf("error decoding K8s CA private key PEM")
}
caKey, err := stdlibx509.ParseECPrivateKey(caKeyBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("error parsing K8s CA private key: %w", err)
}
// Build a deterministic byte stream via HKDF (RFC 5869). The secret is the
// CA private key (stable in machine_secrets); the info string binds all
// inputs that affect the cert so different parameters produce different keys.
info := fmt.Sprintf("talos-kubeconfig:v1:%s:%s:%d:%d", clusterName, endpoint, notBefore.Unix(), notAfter.Unix())
deterministicReader := hkdf.New(sha256.New, bundle.Certs.K8s.Key, []byte("talos-kubeconfig-v1"), []byte(info))
// Derive admin client ECDSA private key deterministically from HKDF output.
// ecdsa.ParseRawPrivateKey constructs a key from raw bytes without using
// crypto/rand (unlike ecdsa.GenerateKey which ignores the reader in Go 1.26+).
adminKeyBytes := make([]byte, 32) // P-256 key size
if _, err = deterministicReader.Read(adminKeyBytes); err != nil {
return nil, fmt.Errorf("error deriving admin key bytes: %w", err)
}
adminKey, err := ecdsa.ParseRawPrivateKey(elliptic.P256(), adminKeyBytes)
if err != nil {
// The HKDF output might produce an invalid scalar (0 or >= n).
// In practice this is astronomically unlikely for P-256 but handle it.
return nil, fmt.Errorf("error parsing derived admin key: %w", err)
}
// Deterministic serial number from the same HKDF stream.
serialBytes := make([]byte, 16)
if _, err = deterministicReader.Read(serialBytes); err != nil {
return nil, fmt.Errorf("error deriving serial number: %w", err)
}
serialNumber := new(big.Int).SetBytes(serialBytes)
// Build and sign the admin client certificate.
template := &stdlibx509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "admin",
Organization: []string{"system:masters"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: stdlibx509.KeyUsageDigitalSignature | stdlibx509.KeyUsageKeyEncipherment,
ExtKeyUsage: []stdlibx509.ExtKeyUsage{
stdlibx509.ExtKeyUsageClientAuth,
},
}
// Use RFC 6979 deterministic signer for the CA signature. Go 1.26+ ignores
// the io.Reader in ecdsa.Sign and always uses system randomness, so we must
// implement deterministic ECDSA ourselves via a custom crypto.Signer.
caSigner := &deterministicECDSASigner{key: caKey}
// The rand reader is unused: serial number is set explicitly and signing
// goes through our deterministic crypto.Signer.
certDER, err := stdlibx509.CreateCertificate(nil, template, caCert, &adminKey.PublicKey, caSigner)
if err != nil {
return nil, fmt.Errorf("error signing admin certificate: %w", err)
}
// PEM-encode the client cert and key.
clientCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
adminKeyDER, err := stdlibx509.MarshalECPrivateKey(adminKey)
if err != nil {
return nil, fmt.Errorf("error marshaling admin private key: %w", err)
}
clientKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: adminKeyDER})
// Assemble kubeconfig.
cfg := clientcmdapi.Config{
APIVersion: "v1",
Kind: "Config",
Clusters: map[string]*clientcmdapi.Cluster{
clusterName: {
Server: endpoint,
CertificateAuthorityData: bundle.Certs.K8s.Crt,
},
},
AuthInfos: map[string]*clientcmdapi.AuthInfo{
"admin@" + clusterName: {
ClientCertificateData: clientCertPEM,
ClientKeyData: clientKeyPEM,
},
},
Contexts: map[string]*clientcmdapi.Context{
"admin@" + clusterName: {
Cluster: clusterName,
Namespace: "default",
AuthInfo: "admin@" + clusterName,
},
},
CurrentContext: "admin@" + clusterName,
}
marshaled, err := clientcmd.Write(cfg)
if err != nil {
return nil, fmt.Errorf("error marshaling kubeconfig: %w", err)
}
return &KubeconfigResult{
Raw: string(marshaled),
ClientCertPEM: clientCertPEM,
ClientKeyPEM: clientKeyPEM,
}, nil
}
================================================
FILE: pkg/talos/talos_cluster_kubeconfig_ephemeral_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"regexp"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
// TestAccTalosClusterKubeconfigEphemeralResourceBasic tests that the ephemeral resource
// generates kubeconfig_raw and that kubernetes_client_configuration.host matches the endpoint.
func TestAccTalosClusterKubeconfigEphemeralResourceBasic(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.0.0.1:6443"
}
provider "echo" {
data = {
kubeconfig_raw = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
host = ephemeral.talos_cluster_kubeconfig.this.kubernetes_client_configuration.host
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("kubeconfig_raw"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("host"), knownvalue.StringExact("https://10.0.0.1:6443")),
},
},
},
})
}
// TestAccTalosClusterKubeconfigEphemeralResourceKubernetesClientConfigurationFields tests
// that all kubernetes_client_configuration fields are populated.
func TestAccTalosClusterKubeconfigEphemeralResourceKubernetesClientConfigurationFields(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.0.0.1:6443"
}
provider "echo" {
data = {
host = ephemeral.talos_cluster_kubeconfig.this.kubernetes_client_configuration.host
ca_certificate = ephemeral.talos_cluster_kubeconfig.this.kubernetes_client_configuration.ca_certificate
client_certificate = ephemeral.talos_cluster_kubeconfig.this.kubernetes_client_configuration.client_certificate
client_key = ephemeral.talos_cluster_kubeconfig.this.kubernetes_client_configuration.client_key
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("host"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("ca_certificate"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("client_certificate"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("client_key"), knownvalue.NotNull()),
},
},
},
})
}
// TestAccTalosClusterKubeconfigEphemeralResourceDeterminism tests that two opens with
// identical inputs (CA-pinned, no not_before) produce byte-identical kubeconfig_raw.
func TestAccTalosClusterKubeconfigEphemeralResourceDeterminism(t *testing.T) {
t.Parallel()
cfg := `
resource "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "test-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
endpoint = "https://10.0.0.1:6443"
}
provider "echo" {
data = {
kubeconfig_raw = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
}
}
resource "echo" "test" {}
`
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: cfg,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("kubeconfig_raw"), knownvalue.NotNull()),
},
},
{
Config: cfg,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
},
},
})
}
// TestAccTalosClusterKubeconfigEphemeralResourceDeterminismWithNotBefore tests that two
// opens with a terraform_data-sourced not_before produce byte-identical kubeconfig_raw.
func TestAccTalosClusterKubeconfigEphemeralResourceDeterminismWithNotBefore(t *testing.T) {
t.Parallel()
cfg := `
resource "talos_machine_secrets" "this" {}
resource "terraform_data" "kubeconfig_nbf" {
input = "2024-01-01T00:00:00Z"
}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "test-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
endpoint = "https://10.0.0.1:6443"
not_before = terraform_data.kubeconfig_nbf.output
crt_ttl = "87600h"
}
provider "echo" {
data = {
kubeconfig_raw = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
}
}
resource "echo" "test" {}
`
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: cfg,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("kubeconfig_raw"), knownvalue.NotNull()),
},
},
{
Config: cfg,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
},
},
})
}
// TestAccTalosClusterKubeconfigEphemeralResourceCustomTTL tests that not_before + crt_ttl
// are accepted and the resource opens without error.
func TestAccTalosClusterKubeconfigEphemeralResourceCustomTTL(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.0.0.1:6443"
not_before = "2024-01-01T00:00:00Z"
crt_ttl = "8760h"
}
provider "echo" {
data = {
kubeconfig_raw = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("kubeconfig_raw"), knownvalue.NotNull()),
},
},
},
})
}
// TestAccTalosClusterKubeconfigEphemeralResourceInvalidTTL tests that an invalid crt_ttl
// is rejected with an error.
func TestAccTalosClusterKubeconfigEphemeralResourceInvalidTTL(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.0.0.1:6443"
not_before = "2024-01-01T00:00:00Z"
crt_ttl = "not-a-duration"
}
provider "echo" {
data = {}
}
resource "echo" "test" {}
`,
ExpectError: regexp.MustCompile(`invalid crt_ttl`),
},
},
})
}
// TestAccTalosClusterKubeconfigEphemeralResourceInvalidNotBefore tests that an invalid
// not_before is rejected with an error.
func TestAccTalosClusterKubeconfigEphemeralResourceInvalidNotBefore(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "test-cluster"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
endpoint = "https://10.0.0.1:6443"
not_before = "not-a-timestamp"
}
provider "echo" {
data = {}
}
resource "echo" "test" {}
`,
ExpectError: regexp.MustCompile(`invalid not_before`),
},
},
})
}
================================================
FILE: pkg/talos/talos_cluster_kubeconfig_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"time"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/siderolabs/talos/pkg/machinery/client"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/client-go/tools/clientcmd"
)
type talosClusterKubeConfigResource struct{}
var (
_ resource.Resource = &talosClusterKubeConfigResource{}
_ resource.ResourceWithModifyPlan = &talosClusterKubeConfigResource{}
_ resource.ResourceWithUpgradeState = &talosClusterKubeConfigResource{}
)
type talosClusterKubeConfigResourceModelV0 struct {
ID types.String `tfsdk:"id"`
Node types.String `tfsdk:"node"`
Endpoint types.String `tfsdk:"endpoint"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
KubeConfigRaw types.String `tfsdk:"kubeconfig_raw"`
KubernetesClientConfiguration kubernetesClientConfiguration `tfsdk:"kubernetes_client_configuration"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
type talosClusterKubeConfigResourceModelV1 struct {
ID types.String `tfsdk:"id"`
Node types.String `tfsdk:"node"`
Endpoint types.String `tfsdk:"endpoint"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
KubeConfigRaw types.String `tfsdk:"kubeconfig_raw"`
KubernetesClientConfiguration kubernetesClientConfiguration `tfsdk:"kubernetes_client_configuration"`
CertificateRenewalDuration types.String `tfsdk:"certificate_renewal_duration"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
type kubernetesClientConfiguration struct {
Host types.String `tfsdk:"host"`
CACertificate types.String `tfsdk:"ca_certificate"`
ClientCertificate types.String `tfsdk:"client_certificate"`
ClientKey types.String `tfsdk:"client_key"`
}
// NewTalosClusterKubeConfigResource implements the resource.Resource interface.
func NewTalosClusterKubeConfigResource() resource.Resource {
return &talosClusterKubeConfigResource{}
}
func (r *talosClusterKubeConfigResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_cluster_kubeconfig"
}
func (r *talosClusterKubeConfigResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Version: 1,
Description: "Retrieves the kubeconfig for a Talos cluster",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"node": schema.StringAttribute{
Required: true,
Description: "controlplane node to retrieve the kubeconfig from",
},
"endpoint": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "endpoint to use for the talosclient. If not set, the node value will be used",
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Required: true,
Description: "The client configuration data",
},
"kubeconfig_raw": schema.StringAttribute{
Computed: true,
Description: "The raw kubeconfig",
Sensitive: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"kubernetes_client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"host": schema.StringAttribute{
Computed: true,
Description: "The kubernetes host",
},
"ca_certificate": schema.StringAttribute{
Computed: true,
Description: "The kubernetes CA certificate",
},
"client_certificate": schema.StringAttribute{
Computed: true,
Description: "The kubernetes client certificate",
},
"client_key": schema.StringAttribute{
Computed: true,
Sensitive: true,
Description: "The kubernetes client key",
},
},
Computed: true,
Description: "The kubernetes client configuration",
PlanModifiers: []planmodifier.Object{
objectplanmodifier.UseStateForUnknown(),
},
},
"certificate_renewal_duration": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The duration in hours before the certificate is renewed, defaults to 720h. Must be a valid duration string",
Default: stringdefault.StaticString("720h"),
},
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
Update: true,
}),
},
}
}
// Create implements the resource.Resource interface.
func (r *talosClusterKubeConfigResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var state talosClusterKubeConfigResourceModelV1
diags = obj.As(ctx, &state, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
talosConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
state.ClientConfiguration.CA.ValueString(),
state.ClientConfiguration.Cert.ValueString(),
state.ClientConfiguration.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
if state.Endpoint.IsNull() {
state.Endpoint = state.Node
}
readTimeout, diags := state.Timeouts.Create(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctxDeadline, cancel := context.WithTimeout(ctx, readTimeout)
defer cancel()
if retryErr := retry.RetryContext(ctxDeadline, readTimeout, func() *retry.RetryError {
if clientOpErr := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosConfig, func(nodeCtx context.Context, c *client.Client) error {
kubeConfigBytes, clientErr := c.Kubeconfig(nodeCtx)
if clientErr != nil {
return clientErr
}
state.KubeConfigRaw = basetypes.NewStringValue(string(kubeConfigBytes))
return nil
}); clientOpErr != nil {
if s := status.Code(clientOpErr); s == codes.InvalidArgument {
return retry.NonRetryableError(clientOpErr)
}
return retry.RetryableError(clientOpErr)
}
return nil
}); retryErr != nil {
resp.Diagnostics.AddError("failed to retrieve kubeconfig", retryErr.Error())
return
}
kubeConfig, err := clientcmd.Load([]byte(state.KubeConfigRaw.ValueString()))
if err != nil {
resp.Diagnostics.AddError("failed to parse kubeconfig", err.Error())
return
}
clusterName := kubeConfig.Contexts[kubeConfig.CurrentContext].Cluster
authName := kubeConfig.Contexts[kubeConfig.CurrentContext].AuthInfo
state.KubernetesClientConfiguration = kubernetesClientConfiguration{
Host: basetypes.NewStringValue(kubeConfig.Clusters[clusterName].Server),
CACertificate: basetypes.NewStringValue(bytesToBase64(kubeConfig.Clusters[clusterName].CertificateAuthorityData)),
ClientCertificate: basetypes.NewStringValue(bytesToBase64(kubeConfig.AuthInfos[authName].ClientCertificateData)),
ClientKey: basetypes.NewStringValue(bytesToBase64(kubeConfig.AuthInfos[authName].ClientKeyData)),
}
state.ID = basetypes.NewStringValue(clusterName)
var planObj types.Object
diags = req.Plan.Get(ctx, &planObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var planState talosClusterKubeConfigResourceModelV1
diags = planObj.As(ctx, &planState, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if state.CertificateRenewalDuration.IsNull() || state.CertificateRenewalDuration.IsUnknown() {
state.CertificateRenewalDuration = planState.CertificateRenewalDuration
}
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *talosClusterKubeConfigResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
}
func (r *talosClusterKubeConfigResource) Read(_ context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) {
}
// ModifyPlan implements the resource.ResourceWithModifyPlan interface.
//
//nolint:gocyclo,cyclop
func (r *talosClusterKubeConfigResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
// delete is a no-op
if req.Plan.Raw.IsNull() {
return
}
var configObj types.Object
diags := req.Config.Get(ctx, &configObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosClusterKubeConfigResourceModelV1
diags = configObj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// if either endpoint or node is unknown return early
if config.Endpoint.IsUnknown() || config.Node.IsUnknown() {
return
}
var planObj types.Object
diags = req.Plan.Get(ctx, &planObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var planState talosClusterKubeConfigResourceModelV1
diags = configObj.As(ctx, &planState, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if planState.Endpoint.IsUnknown() || planState.Endpoint.IsNull() {
diags = resp.Plan.SetAttribute(ctx, path.Root("endpoint"), planState.Node.ValueString())
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
}
kubernetesClientConfigPath := path.Root("kubernetes_client_configuration")
var obj types.Object
resp.Diagnostics.Append(req.State.GetAttribute(ctx, kubernetesClientConfigPath, &obj)...)
if resp.Diagnostics.HasError() {
return
}
var kubernetesClientConfig kubernetesClientConfiguration
resp.Diagnostics.Append(obj.As(ctx, &kubernetesClientConfig, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})...)
if resp.Diagnostics.HasError() {
return
}
if kubernetesClientConfig.ClientCertificate.IsNull() || kubernetesClientConfig.ClientCertificate.IsUnknown() {
return
}
kubernetesClientCertificate := kubernetesClientConfig.ClientCertificate.ValueString()
kubernetesClientCertificateBytes, err := base64ToBytes(kubernetesClientCertificate)
if err != nil {
resp.Diagnostics.AddError("failed to decode kubernetes client certificate", err.Error())
return
}
block, _ := pem.Decode(kubernetesClientCertificateBytes)
if block == nil {
resp.Diagnostics.AddError("failed to decode kubernetes client certificate", "failed to decode PEM block")
return
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
resp.Diagnostics.AddError("failed to parse kubernetes client certificate", err.Error())
return
}
var exisitingStateObj types.Object
diags = req.State.Get(ctx, &exisitingStateObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var existingState talosClusterKubeConfigResourceModelV1
diags = exisitingStateObj.As(ctx, &existingState, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if planState.CertificateRenewalDuration.IsNull() || planState.CertificateRenewalDuration.IsUnknown() {
planState.CertificateRenewalDuration = existingState.CertificateRenewalDuration
}
renewalDuration, err := time.ParseDuration(planState.CertificateRenewalDuration.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed to parse certificate renewal duration in plan", err.Error())
return
}
// check if NotAfter expires in the given duration
if x509Cert.NotAfter.Before(OverridableTimeFunc().Add(renewalDuration)) {
tflog.Info(ctx, fmt.Sprintf("kubernetes client certificate expires in %s, needs regeneration", existingState.CertificateRenewalDuration.ValueString()))
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("host"), types.StringUnknown())...)
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("client_certificate"), types.StringUnknown())...)
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("client_key"), types.StringUnknown())...)
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("ca_certificate"), types.StringUnknown())...)
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("kubeconfig_raw"), types.StringUnknown())...)
if resp.Diagnostics.HasError() {
return
}
}
}
func (r *talosClusterKubeConfigResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: &schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"node": schema.StringAttribute{
Required: true,
},
"endpoint": schema.StringAttribute{
Optional: true,
Computed: true,
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
},
"client_certificate": schema.StringAttribute{
Required: true,
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
},
},
Required: true,
},
"kubeconfig_raw": schema.StringAttribute{
Computed: true,
Sensitive: true,
},
"kubernetes_client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"host": schema.StringAttribute{
Computed: true,
},
"ca_certificate": schema.StringAttribute{
Computed: true,
},
"client_certificate": schema.StringAttribute{
Computed: true,
},
"client_key": schema.StringAttribute{
Computed: true,
Sensitive: true,
},
},
Computed: true,
},
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
Update: true,
}),
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var obj types.Object
diags := req.State.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
var priorStateData talosClusterKubeConfigResourceModelV0
diags = obj.As(ctx, &priorStateData, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
state := talosClusterKubeConfigResourceModelV1{
ID: priorStateData.ID,
Node: priorStateData.Node,
Endpoint: priorStateData.Endpoint,
ClientConfiguration: priorStateData.ClientConfiguration,
KubeConfigRaw: priorStateData.KubeConfigRaw,
KubernetesClientConfiguration: kubernetesClientConfiguration{
Host: priorStateData.KubernetesClientConfiguration.Host,
CACertificate: priorStateData.KubernetesClientConfiguration.CACertificate,
ClientCertificate: priorStateData.KubernetesClientConfiguration.ClientCertificate,
ClientKey: priorStateData.KubernetesClientConfiguration.ClientKey,
},
CertificateRenewalDuration: basetypes.NewStringValue("720h"),
Timeouts: priorStateData.Timeouts,
}
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
},
},
}
}
// Update implements the resource.ResourceWithModifyPlan interface.
//
//nolint:gocognit,gocyclo,cyclop
func (r *talosClusterKubeConfigResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var planObj types.Object
resp.Diagnostics.Append(req.Plan.Get(ctx, &planObj)...)
if resp.Diagnostics.HasError() {
return
}
var state talosClusterKubeConfigResourceModelV1
resp.Diagnostics.Append(planObj.As(ctx, &state, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
kubernetesClientConfigPath := path.Root("kubernetes_client_configuration")
var stateObj types.Object
resp.Diagnostics.Append(req.State.GetAttribute(ctx, kubernetesClientConfigPath, &stateObj)...)
if resp.Diagnostics.HasError() {
return
}
var kubernetesClientConfig kubernetesClientConfiguration
resp.Diagnostics.Append(stateObj.As(ctx, &kubernetesClientConfig, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})...)
if resp.Diagnostics.HasError() {
return
}
if kubernetesClientConfig.ClientCertificate.IsNull() || kubernetesClientConfig.ClientCertificate.IsUnknown() {
return
}
kubernetesClientCertificate := kubernetesClientConfig.ClientCertificate.ValueString()
kubernetesClientCertificateBytes, err := base64ToBytes(kubernetesClientCertificate)
if err != nil {
resp.Diagnostics.AddError("failed to decode kubernetes client certificate", err.Error())
return
}
block, _ := pem.Decode(kubernetesClientCertificateBytes)
if block == nil {
resp.Diagnostics.AddError("failed to decode kubernetes client certificate", "failed to decode PEM block")
return
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
resp.Diagnostics.AddError("failed to parse kubernetes client certificate", err.Error())
return
}
renewalDuration, err := time.ParseDuration(state.CertificateRenewalDuration.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed to parse certificate renewal duration", err.Error())
return
}
// check if NotAfter expires in the given duration
if x509Cert.NotAfter.Before(OverridableTimeFunc().Add(renewalDuration)) {
tflog.Info(ctx, fmt.Sprintf("kubernetes client certificate expires in %s, regenerating", state.CertificateRenewalDuration.ValueString()))
talosConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
state.ClientConfiguration.CA.ValueString(),
state.ClientConfiguration.Cert.ValueString(),
state.ClientConfiguration.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
if state.Endpoint.IsNull() {
state.Endpoint = state.Node
}
updateTimeout, diags := state.Timeouts.Update(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctxDeadline, cancel := context.WithTimeout(ctx, updateTimeout)
defer cancel()
if retryErr := retry.RetryContext(ctxDeadline, updateTimeout, func() *retry.RetryError {
if clientOpErr := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosConfig, func(nodeCtx context.Context, c *client.Client) error {
kubeConfigBytes, clientErr := c.Kubeconfig(nodeCtx)
if clientErr != nil {
return clientErr
}
state.KubeConfigRaw = basetypes.NewStringValue(string(kubeConfigBytes))
return nil
}); clientOpErr != nil {
if s := status.Code(clientOpErr); s == codes.InvalidArgument {
return retry.NonRetryableError(clientOpErr)
}
return retry.RetryableError(clientOpErr)
}
return nil
}); retryErr != nil {
resp.Diagnostics.AddError("failed to retrieve kubeconfig", retryErr.Error())
return
}
kubeConfig, err := clientcmd.Load([]byte(state.KubeConfigRaw.ValueString()))
if err != nil {
resp.Diagnostics.AddError("failed to parse kubeconfig", err.Error())
return
}
clusterName := kubeConfig.Contexts[kubeConfig.CurrentContext].Cluster
authName := kubeConfig.Contexts[kubeConfig.CurrentContext].AuthInfo
state.KubernetesClientConfiguration = kubernetesClientConfiguration{
Host: basetypes.NewStringValue(kubeConfig.Clusters[clusterName].Server),
CACertificate: basetypes.NewStringValue(bytesToBase64(kubeConfig.Clusters[clusterName].CertificateAuthorityData)),
ClientCertificate: basetypes.NewStringValue(bytesToBase64(kubeConfig.AuthInfos[authName].ClientCertificateData)),
ClientKey: basetypes.NewStringValue(bytesToBase64(kubeConfig.AuthInfos[authName].ClientKeyData)),
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("host"), &state.KubernetesClientConfiguration.Host)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("client_certificate"), &state.KubernetesClientConfiguration.ClientCertificate)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("client_key"), &state.KubernetesClientConfiguration.ClientKey)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("kubernetes_client_configuration").AtName("ca_certificate"), &state.KubernetesClientConfiguration.CACertificate)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("kubeconfig_raw"), &state.KubeConfigRaw)...)
if resp.Diagnostics.HasError() {
return
}
}
}
================================================
FILE: pkg/talos/talos_cluster_kubeconfig_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"time"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/siderolabs/terraform-provider-talos/pkg/talos"
)
func TestAccTalosClusterKubeconfigResource(t *testing.T) {
testTime := time.Now()
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.Test(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosClusterKubeconfigResourceConfig(rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_cluster_kubeconfig.this", "id", "example-cluster"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "node"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "endpoint"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "client_configuration.client_key"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubeconfig_raw"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.host"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.client_key"),
),
},
// test kubeconfig regeneration
{
PreConfig: func() {
talos.OverridableTimeFunc = func() time.Time {
return testTime.AddDate(0, 12, 5)
}
},
Config: testAccTalosClusterKubeconfigResourceConfig(rName),
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
},
})
talos.OverridableTimeFunc = func() time.Time {
return testTime
}
resource.ParallelTest(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosClusterKubeconfigResourceConfig(rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_cluster_kubeconfig.this", "id", "example-cluster"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "node"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "endpoint"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "client_configuration.client_key"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubeconfig_raw"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.host"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_cluster_kubeconfig.this", "kubernetes_client_configuration.client_key"),
),
},
// make sure there are no changes
{
Config: testAccTalosClusterKubeconfigResourceConfig(rName),
PlanOnly: true,
},
},
})
}
func testAccTalosClusterKubeconfigResourceConfig(rName string) string {
config := dynamicConfig{
Provider: "talos",
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: true,
WithRetrieveKubeConfig: true,
}
return config.render()
}
================================================
FILE: pkg/talos/talos_image_factory_extensions_versions_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"fmt"
"slices"
"strings"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/image-factory/pkg/client"
)
type talosImageFactoryExtensionsVersionsDataSource struct {
imageFactoryClient *client.Client
}
type talosImageFactoryExtensionsVersionsDataSourceModelV0 struct {
ID types.String `tfsdk:"id"`
TalosVersion types.String `tfsdk:"talos_version"`
Filters *talosImageFactoryExtensionsVersionsFilter `tfsdk:"filters"`
ExactFilters *talosImageFactoryExtensionsVersionsFilter `tfsdk:"exact_filters"`
ExtensionsInfo []extensionInfo `tfsdk:"extensions_info"`
}
type talosImageFactoryExtensionsVersionsFilter struct {
Names types.List `tfsdk:"names"`
}
type extensionInfo struct {
Name types.String `tfsdk:"name"`
Ref types.String `tfsdk:"ref"`
Digest types.String `tfsdk:"digest"`
Author types.String `tfsdk:"author"`
Description types.String `tfsdk:"description"`
}
var _ datasource.DataSourceWithConfigure = &talosImageFactoryExtensionsVersionsDataSource{}
// NewTalosImageFactoryExtensionsVersionsDataSource implements the datasource.DataSource interface.
func NewTalosImageFactoryExtensionsVersionsDataSource() datasource.DataSource {
return &talosImageFactoryExtensionsVersionsDataSource{}
}
func (d *talosImageFactoryExtensionsVersionsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_image_factory_extensions_versions"
}
func (d *talosImageFactoryExtensionsVersionsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "The image factory extensions versions data source provides a list of available extensions for a specific talos version from the image factory.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"talos_version": schema.StringAttribute{
Required: true,
Description: "The talos version to get extensions for.",
},
"filters": schema.SingleNestedAttribute{
Optional: true,
Description: "The filter to apply to the extensions list.",
Attributes: map[string]schema.Attribute{
"names": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "The name of the extension to filter by.",
},
},
},
"exact_filters": schema.SingleNestedAttribute{
Optional: true,
Description: "The filter to apply to the extensions list.",
Attributes: map[string]schema.Attribute{
"names": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "The exact name match of the extension to filter by.",
},
},
},
"extensions_info": schema.ListAttribute{
ElementType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"name": types.StringType,
"ref": types.StringType,
"digest": types.StringType,
"author": types.StringType,
"description": types.StringType,
},
},
Computed: true,
Description: "The list of available extensions for the specified talos version.",
},
},
}
}
func (d *talosImageFactoryExtensionsVersionsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
imageFactoryClient, ok := req.ProviderData.(*client.Client)
if !ok {
resp.Diagnostics.AddError(
"failed to get image factory client",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.imageFactoryClient = imageFactoryClient
}
//nolint:gocyclo,cyclop,gocognit
func (d *talosImageFactoryExtensionsVersionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if d.imageFactoryClient == nil {
resp.Diagnostics.AddError("image factory client is not configured", "Please report this issue to the provider developers.")
return
}
var config talosImageFactoryExtensionsVersionsDataSourceModelV0
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
if config.TalosVersion.IsNull() || config.TalosVersion.IsUnknown() {
return
}
extensionsInfo, err := d.imageFactoryClient.ExtensionsVersions(ctx, config.TalosVersion.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed to get talos extensions versions", err.Error())
return
}
var exactNames, names []string
if config.ExactFilters != nil && !config.ExactFilters.Names.IsNull() && !config.ExactFilters.Names.IsUnknown() {
resp.Diagnostics.Append(config.ExactFilters.Names.ElementsAs(ctx, &exactNames, true)...)
if resp.Diagnostics.HasError() {
return
}
}
if config.Filters != nil && !config.Filters.Names.IsNull() && !config.Filters.Names.IsUnknown() {
resp.Diagnostics.Append(config.Filters.Names.ElementsAs(ctx, &names, true)...)
if resp.Diagnostics.HasError() {
return
}
}
if len(exactNames) > 0 || len(names) > 0 {
extensionsInfo = xslices.Filter(extensionsInfo, func(e client.ExtensionInfo) bool {
if len(exactNames) > 0 {
if slices.Contains(exactNames, e.Name) {
return true
}
}
if len(names) > 0 {
for _, n := range names {
if strings.Contains(e.Name, n) {
return true
}
}
}
return false
})
}
tfExtensionsInfo := xslices.Map(extensionsInfo, func(e client.ExtensionInfo) extensionInfo {
return extensionInfo{
Name: types.StringValue(e.Name),
Ref: types.StringValue(e.Ref),
Digest: types.StringValue(e.Digest),
Author: types.StringValue(e.Author),
Description: types.StringValue(e.Description),
}
})
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), "extensions_info")...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("extensions_info"), &tfExtensionsInfo)...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_image_factory_extensions_versions_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccTalosImageFactoryExtensionsVersionsDataSource(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosImageFactoryExtensionsVersionsDataSourceConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_extensions_versions.this", "extensions_info.0.name", "siderolabs/amdgpu-firmware"),
),
},
{
Config: testAccTalosImageFactoryExtensionsVersionsDataSourceConfigWithFilters(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_extensions_versions.this", "extensions_info.#", "5"),
resource.TestCheckResourceAttr("data.talos_image_factory_extensions_versions.this", "extensions_info.0.name", "siderolabs/nvidia-container-toolkit"),
),
},
{
Config: testAccTalosImageFactoryExtensionsVersionsDataSourceConfigWithExactFilters(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_extensions_versions.this", "extensions_info.#", "1"),
resource.TestCheckResourceAttr("data.talos_image_factory_extensions_versions.this", "extensions_info.0.name", "siderolabs/tailscale"),
),
},
},
})
}
func testAccTalosImageFactoryExtensionsVersionsDataSourceConfig() string {
return `
provider "talos" {}
data "talos_image_factory_extensions_versions" "this" {
talos_version = "v1.7.0"
}
`
}
func testAccTalosImageFactoryExtensionsVersionsDataSourceConfigWithFilters() string {
return `
provider "talos" {}
data "talos_image_factory_extensions_versions" "this" {
talos_version = "v1.7.0"
filters = {
names = [
"nvidia",
"tailscale"
]
}
}
`
}
func testAccTalosImageFactoryExtensionsVersionsDataSourceConfigWithExactFilters() string {
return `
provider "talos" {}
data "talos_image_factory_extensions_versions" "this" {
talos_version = "v1.7.0"
exact_filters = {
names = [
"nvidia",
"siderolabs/tailscale"
]
}
}
`
}
================================================
FILE: pkg/talos/talos_image_factory_overlays_versions_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/image-factory/pkg/client"
)
type talosImageFactoryOverlaysVersionsDataSource struct {
imageFactoryClient *client.Client
}
type talosImageFactoryOverlaysVersionsDataSourceModelV0 struct {
ID types.String `tfsdk:"id"`
TalosVersion types.String `tfsdk:"talos_version"`
Filters *talosImageFactoryOverlaysVersionsFilter `tfsdk:"filters"`
OverlaysInfo []overlayInfo `tfsdk:"overlays_info"`
}
type talosImageFactoryOverlaysVersionsFilter struct {
Name types.String `tfsdk:"name"`
}
type overlayInfo struct {
Name types.String `tfsdk:"name"`
Image types.String `tfsdk:"image"`
Ref types.String `tfsdk:"ref"`
Digest types.String `tfsdk:"digest"`
}
var _ datasource.DataSourceWithConfigure = &talosImageFactoryOverlaysVersionsDataSource{}
// NewTalosImageFactoryOverlaysVersionsDataSource implements the datasource.DataSource interface.
func NewTalosImageFactoryOverlaysVersionsDataSource() datasource.DataSource {
return &talosImageFactoryOverlaysVersionsDataSource{}
}
func (d *talosImageFactoryOverlaysVersionsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_image_factory_overlays_versions"
}
func (d *talosImageFactoryOverlaysVersionsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "The image factory overlays versions data source provides a list of available overlays for a specific talos version from the image factory.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"talos_version": schema.StringAttribute{
Required: true,
Description: "The talos version to get overlays for.",
},
"filters": schema.SingleNestedAttribute{
Optional: true,
Description: "The filter to apply to the overlays list.",
Attributes: map[string]schema.Attribute{
"name": schema.StringAttribute{
Optional: true,
Description: "The name of the overlay to filter by.",
},
},
},
"overlays_info": schema.ListAttribute{
ElementType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"name": types.StringType,
"image": types.StringType,
"ref": types.StringType,
"digest": types.StringType,
},
},
Computed: true,
Description: "The list of available extensions for the specified talos version.",
},
},
}
}
func (d *talosImageFactoryOverlaysVersionsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
imageFactoryClient, ok := req.ProviderData.(*client.Client)
if !ok {
resp.Diagnostics.AddError(
"failed to get image factory client",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.imageFactoryClient = imageFactoryClient
}
func (d *talosImageFactoryOverlaysVersionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if d.imageFactoryClient == nil {
resp.Diagnostics.AddError("image factory client is not configured", "Please report this issue to the provider developers.")
return
}
var config talosImageFactoryOverlaysVersionsDataSourceModelV0
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
if config.TalosVersion.IsNull() || config.TalosVersion.IsUnknown() {
return
}
overlaysInfo, err := d.imageFactoryClient.OverlaysVersions(ctx, config.TalosVersion.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed to get talos overlays versions", err.Error())
return
}
if config.Filters != nil && !config.Filters.Name.IsNull() && !config.Filters.Name.IsUnknown() {
overlaysInfo = xslices.Filter(overlaysInfo, func(e client.OverlayInfo) bool {
return strings.Contains(e.Name, config.Filters.Name.ValueString())
})
}
tfOverlaysInfo := xslices.Map(overlaysInfo, func(e client.OverlayInfo) overlayInfo {
return overlayInfo{
Name: types.StringValue(e.Name),
Image: types.StringValue(e.Image),
Ref: types.StringValue(e.Ref),
Digest: types.StringValue(e.Digest),
}
})
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), "overlays_info")...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("overlays_info"), &tfOverlaysInfo)...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_image_factory_overlays_versions_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccTalosImageFactoryOverlaysVersionsDataSource(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosImageFactoryOverlaysVersionsDataSourceConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_overlays_versions.this", "overlays_info.0.name", "rpi_generic"),
),
},
{
Config: testAccTalosImageFactoryOverlaysVersionsDataSourceConfigWithFilters(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_overlays_versions.this", "overlays_info.#", "1"),
resource.TestCheckResourceAttr("data.talos_image_factory_overlays_versions.this", "overlays_info.0.name", "rock4cplus"),
),
},
},
})
}
func testAccTalosImageFactoryOverlaysVersionsDataSourceConfig() string {
return `
provider "talos" {}
data "talos_image_factory_overlays_versions" "this" {
talos_version = "v1.7.0"
}
`
}
func testAccTalosImageFactoryOverlaysVersionsDataSourceConfigWithFilters() string {
return `
provider "talos" {}
data "talos_image_factory_overlays_versions" "this" {
talos_version = "v1.7.0"
filters = {
name = "rock4cplus"
}
}
`
}
================================================
FILE: pkg/talos/talos_image_factory_schematic_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/image-factory/pkg/client"
"github.com/siderolabs/image-factory/pkg/schematic"
"go.yaml.in/yaml/v4"
)
type talosImageFactorySchematicResource struct {
imageFactoryClient *client.Client
}
var (
_ resource.Resource = &talosImageFactorySchematicResource{}
_ resource.ResourceWithConfigure = &talosImageFactorySchematicResource{}
)
var schematicAttributeMarkdownDescription = `
The schematic yaml respresentation to generate the image.
If not set, a vanilla Talos image schematic will be generated.
> Refer to [image-factory](https://github.com/siderolabs/image-factory?tab=readme-ov-file#post-schematics) for the schema.
`
type talosImageFactorySchematicResourceModelV0 struct {
ID types.String `tfsdk:"id"`
Schematic types.String `tfsdk:"schematic"`
}
// NewTalosImageFactorySchematicResource implements the resource.Resource interface.
func NewTalosImageFactorySchematicResource() resource.Resource {
return &talosImageFactorySchematicResource{}
}
func (r *talosImageFactorySchematicResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_image_factory_schematic"
}
func (r *talosImageFactorySchematicResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "The image factory schematic resource allows you to create a schematic for a Talos image.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "The unique ID of the schematic, returned from Image Factory.",
},
"schematic": schema.StringAttribute{
Optional: true,
MarkdownDescription: schematicAttributeMarkdownDescription,
},
},
}
}
func (r *talosImageFactorySchematicResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
imageFactoryClient, ok := req.ProviderData.(*client.Client)
if !ok {
resp.Diagnostics.AddError(
"failed to get image factory client",
"Expected *client.Client, got: %T. Please report this issue to the provider developers.",
)
return
}
r.imageFactoryClient = imageFactoryClient
}
func (r *talosImageFactorySchematicResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var config talosImageFactorySchematicResourceModelV0
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
var schematic schematic.Schematic
if err := yaml.Unmarshal([]byte(config.Schematic.ValueString()), &schematic); err != nil {
resp.Diagnostics.AddError("failed to unmarshal schematic", err.Error())
return
}
schematicID, err := r.imageFactoryClient.SchematicCreate(ctx, schematic)
if err != nil {
resp.Diagnostics.AddError("failed to create schematic", err.Error())
return
}
config.ID = basetypes.NewStringValue(schematicID)
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *talosImageFactorySchematicResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
}
func (r *talosImageFactorySchematicResource) Read(_ context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) {
}
func (r *talosImageFactorySchematicResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan talosImageFactorySchematicResourceModelV0
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var schematic schematic.Schematic
if err := yaml.Unmarshal([]byte(plan.Schematic.ValueString()), &schematic); err != nil {
resp.Diagnostics.AddError("failed to unmarshal schematic", err.Error())
return
}
schematicID, err := r.imageFactoryClient.SchematicCreate(ctx, schematic)
if err != nil {
resp.Diagnostics.AddError("failed to update schematic", err.Error())
return
}
plan.ID = basetypes.NewStringValue(schematicID)
// Set state to fully populated data
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_image_factory_schematic_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccTalosImageFactorySchematicResource(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// vanilla image
{
Config: testAccTalosTalosImageFactorySchematicConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_image_factory_schematic.this", "id", "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"),
),
},
// empty schematic
{
Config: testAccTalosTalosImageFactorySchematicEmptySchematicConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_image_factory_schematic.this", "id", "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"),
),
},
// empty customization
{
Config: testAccTalosTalosImageFactorySchematicEmptyCustomizationConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_image_factory_schematic.this", "id", "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"),
),
},
// vanilla image
{
Config: testAccTalosTalosImageFactorySchematicConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_image_factory_schematic.this", "id", "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"),
),
},
// known extension
{
Config: testAccTalosTalosImageFactorySchematicKnownExtensionConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_image_factory_schematic.this", "id", "d01dbf04a51b44a41d62b7c9692da0a74889277651600da6b602582654e4b402"),
),
},
},
})
}
func testAccTalosTalosImageFactorySchematicConfig() string {
return `
provider "talos" {}
resource "talos_image_factory_schematic" "this" {}
`
}
func testAccTalosTalosImageFactorySchematicEmptySchematicConfig() string {
return `
provider "talos" {}
resource "talos_image_factory_schematic" "this" {
schematic = yamlencode({})
}
`
}
func testAccTalosTalosImageFactorySchematicEmptyCustomizationConfig() string {
return `
provider "talos" {}
resource "talos_image_factory_schematic" "this" {
schematic = yamlencode(
{
customization = {}
}
)
}
`
}
func testAccTalosTalosImageFactorySchematicKnownExtensionConfig() string {
return `
provider "talos" {}
resource "talos_image_factory_schematic" "this" {
schematic = yamlencode(
{
customization = {
systemExtensions = {
officialExtensions = ["siderolabs/amdgpu-firmware"]
}
}
}
)
}
`
}
================================================
FILE: pkg/talos/talos_image_factory_urls_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"fmt"
"net/url"
"slices"
"strings"
"text/template"
"github.com/blang/semver/v4"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/image-factory/pkg/client"
"github.com/siderolabs/talos/pkg/machinery/platforms"
)
type talosImageFactoryURLSDataSource struct {
imageFactoryClient *client.Client
}
var _ datasource.DataSource = &talosImageFactoryURLSDataSource{}
var (
metalPlatforms = []string{"metal"}
cloudPlatforms = xslices.Map(platforms.CloudPlatforms(), func(platform platforms.Platform) string { return platform.Name })
sbcs = xslices.Map(platforms.SBCs(), func(platform platforms.SBC) string { return platform.Name })
allPlatforms = slices.Concat(metalPlatforms, cloudPlatforms)
platformMarkdownDescriptionTemplate = `
The platform for which the URLs are generated.
#### Metal
- metal
#### Cloud Platforms
{{- range .FactoryPlatforms }}
- {{ . }}
{{- end }}
`
sbcMarkdownDescriptionTemplate = `
The SBC's (Single Board Copmuters) for which the url are generated.
#### Single Board Computers
{{- range .SBCs }}
- {{ . }}
{{- end }}
`
)
var (
_ datasource.DataSource = &talosImageFactoryURLSDataSource{}
_ datasource.DataSourceWithConfigure = &talosImageFactoryURLSDataSource{}
)
type talosImageFactoryURLSDataSourceModelV0 struct {
ID types.String `tfsdk:"id"`
Architecture types.String `tfsdk:"architecture"`
TalosVersion types.String `tfsdk:"talos_version"`
SchematicID types.String `tfsdk:"schematic_id"`
Platform types.String `tfsdk:"platform"`
SBC types.String `tfsdk:"sbc"`
URLs urls `tfsdk:"urls"`
}
type urls struct {
Installer types.String `tfsdk:"installer"`
InstallerSecureboot types.String `tfsdk:"installer_secureboot"`
ISO types.String `tfsdk:"iso"`
ISOSecureboot types.String `tfsdk:"iso_secureboot"`
DiskImage types.String `tfsdk:"disk_image"`
DiskImageSecureboot types.String `tfsdk:"disk_image_secureboot"`
PXE types.String `tfsdk:"pxe"`
Kernel types.String `tfsdk:"kernel"`
KernelCommandLine types.String `tfsdk:"kernel_command_line"`
Initramfs types.String `tfsdk:"initramfs"`
UKI types.String `tfsdk:"uki"`
}
// NewTalosImageFactoryURLSDataSource implements the datasource.Datasource interface.
func NewTalosImageFactoryURLSDataSource() datasource.DataSource {
return &talosImageFactoryURLSDataSource{}
}
func (d *talosImageFactoryURLSDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_image_factory_urls"
}
func (d *talosImageFactoryURLSDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
var platformMarkdownDescription strings.Builder
template.Must(template.New("platformMarkdownDescription").Parse(platformMarkdownDescriptionTemplate)).Execute(&platformMarkdownDescription, struct { //nolint:errcheck
FactoryPlatforms []string
}{
FactoryPlatforms: cloudPlatforms,
})
var sbcMarkdownDescription strings.Builder
template.Must(template.New("sbcMarkdownDescription").Parse(sbcMarkdownDescriptionTemplate)).Execute(&sbcMarkdownDescription, struct { //nolint:errcheck
SBCs []string
}{
SBCs: sbcs,
})
resp.Schema = schema.Schema{
Description: "Generates URLs for different assets supported by the Talos image factory.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"architecture": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The platform architecture for which the URLs are generated. Defaults to amd64.",
Validators: []validator.String{
stringvalidator.OneOf("amd64", "arm64"),
},
},
"talos_version": schema.StringAttribute{
Required: true,
Description: "The Talos version for which the URLs are generated.",
},
"schematic_id": schema.StringAttribute{
Required: true,
Description: "The schematic ID for which the URLs are generated.",
},
"platform": schema.StringAttribute{
Optional: true,
MarkdownDescription: platformMarkdownDescription.String(),
Validators: []validator.String{
stringvalidator.All(
stringvalidator.OneOf(allPlatforms...),
stringvalidator.ExactlyOneOf(path.Expressions{
path.MatchRoot("sbc"),
}...),
),
},
},
"sbc": schema.StringAttribute{
Optional: true,
MarkdownDescription: sbcMarkdownDescription.String(),
Validators: []validator.String{
stringvalidator.All(
stringvalidator.OneOf(sbcs...),
stringvalidator.ExactlyOneOf(path.Expressions{
path.MatchRoot("platform"),
}...),
),
},
},
"urls": schema.SingleNestedAttribute{
Computed: true,
Description: "The URLs for different assets supported by the Talos image factory. If the URL is not available for a specific asset, it will be an empty string.",
Attributes: map[string]schema.Attribute{
"installer": schema.StringAttribute{
Computed: true,
Description: "The URL for the installer image.",
},
"installer_secureboot": schema.StringAttribute{
Computed: true,
Description: "The URL for the installer image with secure boot.",
},
"iso": schema.StringAttribute{
Computed: true,
Description: "The URL for the ISO image.",
},
"iso_secureboot": schema.StringAttribute{
Computed: true,
Description: "The URL for the ISO image with secure boot.",
},
"disk_image": schema.StringAttribute{
Computed: true,
Description: "The URL for the disk image.",
},
"disk_image_secureboot": schema.StringAttribute{
Computed: true,
Description: "The URL for the disk image with secure boot.",
},
"pxe": schema.StringAttribute{
Computed: true,
Description: "The URL for the PXE image.",
},
"kernel": schema.StringAttribute{
Computed: true,
Description: "The URL for the kernel image.",
},
"kernel_command_line": schema.StringAttribute{
Computed: true,
Description: "The URL for the kernel command line.",
},
"initramfs": schema.StringAttribute{
Computed: true,
Description: "The URL for the initramfs image.",
},
"uki": schema.StringAttribute{
Computed: true,
Description: "The URL for the UKI image.",
},
},
},
},
}
}
func (d *talosImageFactoryURLSDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
imageFactoryClient, ok := req.ProviderData.(*client.Client)
if !ok {
resp.Diagnostics.AddError(
"failed to get image factory client",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.imageFactoryClient = imageFactoryClient
}
func (d *talosImageFactoryURLSDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if d.imageFactoryClient == nil {
resp.Diagnostics.AddError("image factory client is not configured", "Please report this issue to the provider developers.")
return
}
var obj types.Object
resp.Diagnostics.Append(req.Config.Get(ctx, &obj)...)
if resp.Diagnostics.HasError() {
return
}
var config talosImageFactoryURLSDataSourceModelV0
resp.Diagnostics.Append(obj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})...)
if resp.Diagnostics.HasError() {
return
}
if config.Architecture.IsNull() {
config.Architecture = basetypes.NewStringValue("amd64")
}
architecture := config.Architecture.ValueString()
platform := config.Platform.ValueString()
talosVersion := "v" + strings.TrimPrefix(config.TalosVersion.ValueString(), "v")
if _, err := semver.ParseTolerant(talosVersion); err != nil {
resp.Diagnostics.AddError("talos_version is not valid", "The provided version is not a valid semantic version.")
return
}
schematicID := config.SchematicID.ValueString()
uri, err := url.Parse(d.imageFactoryClient.BaseURL())
if err != nil {
resp.Diagnostics.AddError("failed to parse image factory base URL", err.Error())
return
}
urlsData := urls{
Installer: basetypes.NewStringValue(fmt.Sprintf("%s/%s-installer/%s:%s", uri.Host, platform, schematicID, talosVersion)),
}
switch platform {
case "metal":
platformData := platforms.MetalPlatform()
urlsData.InstallerSecureboot = basetypes.NewStringValue(fmt.Sprintf("%s/%s-installer-secureboot/%s:%s", uri.Host, platform, schematicID, talosVersion))
urlsData.ISO = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.ISOPath(architecture)))
urlsData.ISOSecureboot = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.SecureBootISOPath(architecture)))
urlsData.DiskImage = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.DiskImageDefaultPath(architecture)))
urlsData.DiskImageSecureboot = basetypes.NewStringValue(
fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.SecureBootDiskImageDefaultPath(architecture)),
)
urlsData.PXE = basetypes.NewStringValue(fmt.Sprintf("%s://pxe.%s/pxe/%s/%s/%s", uri.Scheme, uri.Host, schematicID, talosVersion, platformData.PXEScriptPath(architecture)))
urlsData.Kernel = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.KernelPath(architecture)))
urlsData.KernelCommandLine = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.CmdlinePath(architecture)))
urlsData.Initramfs = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.InitramfsPath(architecture)))
urlsData.UKI = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData.SecureBootUKIPath(architecture)))
case "": // empty platform means it's an SBC
urlsData.Installer = basetypes.NewStringValue(fmt.Sprintf("%s/metal-installer/%s:%s", uri.Host, schematicID, talosVersion))
urlsData.DiskImage = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/metal-arm64.raw.xz", d.imageFactoryClient.BaseURL(), schematicID, talosVersion))
default:
platformData := xslices.Filter(platforms.CloudPlatforms(), func(p platforms.Platform) bool { return p.Name == platform })
if len(platformData) != 1 {
resp.Diagnostics.AddError("failed to find platform", platform)
return
}
if platformData[0].SecureBootSupported {
urlsData.InstallerSecureboot = basetypes.NewStringValue(fmt.Sprintf("%s/%s-installer-secureboot/%s:%s", uri.Host, platform, schematicID, talosVersion))
}
for _, bootMethod := range platformData[0].BootMethods {
switch bootMethod {
case platforms.BootMethodDiskImage:
urlsData.DiskImage = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData[0].DiskImageDefaultPath(architecture))) //nolint:lll
if platformData[0].SecureBootSupported {
urlsData.DiskImageSecureboot = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData[0].SecureBootDiskImageDefaultPath(architecture))) //nolint:lll
}
case platforms.BootMethodPXE:
urlsData.PXE = basetypes.NewStringValue(fmt.Sprintf("%s://pxe.%s/pxe/%s/%s/%s", uri.Scheme, uri.Host, schematicID, talosVersion, platformData[0].PXEScriptPath(architecture))) //nolint:lll
case platforms.BootMethodISO:
urlsData.ISO = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData[0].ISOPath(architecture)))
if platformData[0].SecureBootSupported {
urlsData.ISOSecureboot = basetypes.NewStringValue(fmt.Sprintf("%s/image/%s/%s/%s", d.imageFactoryClient.BaseURL(), schematicID, talosVersion, platformData[0].SecureBootISOPath(architecture))) //nolint:lll
}
}
}
}
config.ID = basetypes.NewStringValue(config.ID.ValueString())
config.URLs = urlsData
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_image_factory_urls_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"regexp"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccTalosImageFactoryURLsDataSource(t *testing.T) {
resource.Test(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosImageFactoryURLsBothSBCAndPlatformNotSetConfig(),
ExpectError: regexp.MustCompile("Invalid Attribute Combination"),
},
{
Config: testAccTalosImageFactoryURLsBothSBCAndPlatformSetConfig(),
ExpectError: regexp.MustCompile("Invalid Attribute Combination"),
},
// Invalid Version
{
Config: testAccTalosImageFactoryURLsInvalidVersionConfig(),
ExpectError: regexp.MustCompile("talos_version is not valid"),
},
},
})
//nolint:lll
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// metal platform
{
Config: testAccTalosImageFactoryURLsMetalPlatformConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer", "factory.talos.dev/metal-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.7.5"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer_secureboot", "factory.talos.dev/metal-installer-secureboot/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.7.5"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.iso", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-amd64.iso"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.iso_secureboot", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-amd64-secureboot.iso"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-amd64.raw.zst"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image_secureboot", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-amd64-secureboot.raw.zst"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.pxe", "https://pxe.factory.talos.dev/pxe/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-amd64"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.kernel", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/kernel-amd64"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.kernel_command_line", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/cmdline-metal-amd64"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.initramfs", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/initramfs-amd64.xz"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.uki", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-amd64-secureboot-uki.efi"),
),
},
// metal platform arm64
{
Config: testAccTalosImageFactoryURLsMetalPlatformArm64Config(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer", "factory.talos.dev/metal-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.7.5"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer_secureboot", "factory.talos.dev/metal-installer-secureboot/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.7.5"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.iso", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-arm64.iso"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.iso_secureboot", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-arm64-secureboot.iso"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-arm64.raw.zst"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image_secureboot", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-arm64-secureboot.raw.zst"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.pxe", "https://pxe.factory.talos.dev/pxe/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-arm64"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.kernel", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/kernel-arm64"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.kernel_command_line", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/cmdline-metal-arm64"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.initramfs", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/initramfs-arm64.xz"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.uki", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/metal-arm64-secureboot-uki.efi"),
),
},
// aws platform
{
Config: testAccTalosImageFactoryURLsAWSPlatformConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer", "factory.talos.dev/aws-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.7.5"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.installer_secureboot"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.iso"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.iso_secureboot"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/aws-amd64.raw.xz"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image_secureboot"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.pxe"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.kernel"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.kernel_command_line"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.initramfs"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.uki"),
),
},
// nocloud platform
{
Config: testAccTalosImageFactoryURLsNoCloudPlatformConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer", "factory.talos.dev/nocloud-installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.7.5"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer_secureboot", "factory.talos.dev/nocloud-installer-secureboot/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.7.5"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.iso", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/nocloud-amd64.iso"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.iso_secureboot", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/nocloud-amd64-secureboot.iso"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/nocloud-amd64.raw.xz"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image_secureboot", "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/nocloud-amd64-secureboot.raw.xz"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.pxe", "https://pxe.factory.talos.dev/pxe/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/nocloud-amd64"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.kernel"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.kernel_command_line"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.initramfs"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.uki"),
),
},
// rpigeneric sbc
{
Config: testAccTalosImageFactoryURLsSBCConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.installer", "factory.talos.dev/metal-installer/ee21ef4a5ef808a9b7484cc0dda0f25075021691c8c09a276591eedb638ea1f9:v1.7.5"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.installer_secureboot"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.iso"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.iso_secureboot"),
resource.TestCheckResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image", "https://factory.talos.dev/image/ee21ef4a5ef808a9b7484cc0dda0f25075021691c8c09a276591eedb638ea1f9/v1.7.5/metal-arm64.raw.xz"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.disk_image_secureboot"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.pxe"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.kernel"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.kernel_command_line"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.initramfs"),
resource.TestCheckNoResourceAttr("data.talos_image_factory_urls.this", "urls.uki"),
),
},
},
})
}
func testAccTalosImageFactoryURLsBothSBCAndPlatformNotSetConfig() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
talos_version = "v1.7.0"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
}
`
}
func testAccTalosImageFactoryURLsBothSBCAndPlatformSetConfig() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
talos_version = "v1.7.0"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "metal"
sbc = "rpi_generic"
}
`
}
func testAccTalosImageFactoryURLsMetalPlatformConfig() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
talos_version = "1.7.5"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "metal"
}
`
}
func testAccTalosImageFactoryURLsMetalPlatformArm64Config() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
architecture = "arm64"
talos_version = "v1.7.5"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "metal"
}
`
}
func testAccTalosImageFactoryURLsAWSPlatformConfig() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
talos_version = "v1.7.5"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "aws"
}
`
}
func testAccTalosImageFactoryURLsNoCloudPlatformConfig() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
talos_version = "v1.7.5"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "nocloud"
}
`
}
func testAccTalosImageFactoryURLsSBCConfig() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
talos_version = "v1.7.5"
schematic_id = "ee21ef4a5ef808a9b7484cc0dda0f25075021691c8c09a276591eedb638ea1f9"
sbc = "rpi_generic"
}
`
}
func testAccTalosImageFactoryURLsInvalidVersionConfig() string {
return `
provider "talos" {}
data "talos_image_factory_urls" "this" {
talos_version = "invalid_version"
schematic_id = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"
platform = "metal"
}
`
}
================================================
FILE: pkg/talos/talos_image_factory_versions_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"fmt"
"strings"
"github.com/blang/semver/v4"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/image-factory/pkg/client"
)
type talosImageFactoryVersionsDataSource struct {
imageFactoryClient *client.Client
}
type talosImageFactoryVersionsDataSourceModelV0 struct {
ID types.String `tfsdk:"id"`
Filters *talosImageFactoryVersionsFilter `tfsdk:"filters"`
TalosVersions []string `tfsdk:"talos_versions"`
}
type talosImageFactoryVersionsFilter struct {
StableVersionOnly types.Bool `tfsdk:"stable_versions_only"`
}
var _ datasource.DataSourceWithConfigure = &talosImageFactoryVersionsDataSource{}
// NewTalosImageFactoryVersionsDataSource implements the datasource.DataSource interface.
func NewTalosImageFactoryVersionsDataSource() datasource.DataSource {
return &talosImageFactoryVersionsDataSource{}
}
func (d *talosImageFactoryVersionsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_image_factory_versions"
}
func (d *talosImageFactoryVersionsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "The image factory versions data source provides a list of available talos versions from the image factory.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"talos_versions": schema.ListAttribute{
ElementType: types.StringType,
Computed: true,
Description: "The list of available talos versions.",
},
"filters": schema.SingleNestedAttribute{
Optional: true,
Description: "The filter to apply to the overlays list.",
Attributes: map[string]schema.Attribute{
"stable_versions_only": schema.BoolAttribute{
Optional: true,
Description: "If set to true, only stable versions will be returned. If set to false, all versions will be returned.",
},
},
},
},
}
}
func (d *talosImageFactoryVersionsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
imageFactoryClient, ok := req.ProviderData.(*client.Client)
if !ok {
resp.Diagnostics.AddError(
"failed to get image factory client",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.imageFactoryClient = imageFactoryClient
}
func (d *talosImageFactoryVersionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if d.imageFactoryClient == nil {
resp.Diagnostics.AddError("image factory client is not configured", "Please report this issue to the provider developers.")
return
}
versions, err := d.imageFactoryClient.Versions(ctx)
if err != nil {
resp.Diagnostics.AddError("failed to get talos versions", err.Error())
return
}
var config talosImageFactoryVersionsDataSourceModelV0
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
if config.Filters != nil && config.Filters.StableVersionOnly.ValueBool() {
var filteredVersions []string
for _, version := range versions {
semVer, err := semver.Parse(strings.TrimPrefix(version, "v"))
if err != nil {
resp.Diagnostics.AddError("failed to parse talos version", err.Error())
return
}
if len(semVer.Pre) > 0 {
continue
}
filteredVersions = append(filteredVersions, version)
}
if resp.Diagnostics.HasError() {
return
}
versions = filteredVersions
}
state := talosImageFactoryVersionsDataSourceModelV0{
ID: basetypes.NewStringValue("talos_versions"),
TalosVersions: versions,
}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_image_factory_versions_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
)
func TestAccTalosImageFactoryVersionsDataSource(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosImageFactoryVersionsDataSourceConfig(),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_image_factory_versions.this", "talos_versions.0", "v1.2.0"),
),
},
{
Config: testAccTalosImageFactoryVersionsDataSourceWithFilterConfig(),
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownOutputValue("talos_version", knownvalue.StringExact("v1.13.0")),
},
},
},
})
}
func testAccTalosImageFactoryVersionsDataSourceConfig() string {
return `
provider "talos" {}
data "talos_image_factory_versions" "this" {}
`
}
func testAccTalosImageFactoryVersionsDataSourceWithFilterConfig() string {
return `
provider "talos" {}
data "talos_image_factory_versions" "this" {
filters = {
stable_versions_only = true
}
}
output "talos_version" {
value = data.talos_image_factory_versions.this.talos_versions[length(data.talos_image_factory_versions.this.talos_versions) - 1]
}
`
}
================================================
FILE: pkg/talos/talos_machine_bootstrap_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"time"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/client"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type talosMachineBootstrapResource struct{}
var (
_ resource.Resource = &talosMachineBootstrapResource{}
_ resource.ResourceWithModifyPlan = &talosMachineBootstrapResource{}
_ resource.ResourceWithUpgradeState = &talosMachineBootstrapResource{}
_ resource.ResourceWithImportState = &talosMachineBootstrapResource{}
)
type talosMachineBootstrapResourceModelV0 struct {
ID types.String `tfsdk:"id"`
Endpoint types.String `tfsdk:"endpoint"`
Node types.String `tfsdk:"node"`
TalosConfig types.String `tfsdk:"talos_config"`
}
type talosMachineBootstrapResourceModelV1 struct {
ID types.String `tfsdk:"id"`
Endpoint types.String `tfsdk:"endpoint"`
Node types.String `tfsdk:"node"`
ClientConfiguration basetypes.ObjectValue `tfsdk:"client_configuration"`
ClientConfigurationWO basetypes.ObjectValue `tfsdk:"client_configuration_wo"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
// NewTalosMachineBootstrapResource implements the resource.Resource interface.
func NewTalosMachineBootstrapResource() resource.Resource {
return &talosMachineBootstrapResource{}
}
func (r *talosMachineBootstrapResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_machine_bootstrap"
}
func (r *talosMachineBootstrapResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var config talosMachineBootstrapResourceModelV1
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
clientConfigSet := !config.ClientConfiguration.IsNull()
clientConfigWOSet := !config.ClientConfigurationWO.IsNull()
if !clientConfigSet && !clientConfigWOSet {
resp.Diagnostics.AddError(
"Missing client configuration",
"Exactly one of client_configuration or client_configuration_wo must be set",
)
}
if clientConfigSet && clientConfigWOSet {
resp.Diagnostics.AddError(
"Conflicting client configuration",
"Only one of client_configuration or client_configuration_wo can be set, not both",
)
}
}
// getClientConfiguration returns the effective client configuration,
// preferring the write-only attribute if set.
func getBootstrapClientConfiguration(state *talosMachineBootstrapResourceModelV1) (config basetypes.ObjectValue, diagMsg string) {
woIsNull := state.ClientConfigurationWO.IsNull()
woIsUnknown := state.ClientConfigurationWO.IsUnknown()
regularIsNull := state.ClientConfiguration.IsNull()
// Prefer write-only if available and known
if !woIsNull && !woIsUnknown {
return state.ClientConfigurationWO, ""
}
// If write-only was provided but is still unknown, that's a problem
if !woIsNull && woIsUnknown {
return basetypes.NewObjectNull(map[string]attr.Type{
"ca_certificate": types.StringType,
"client_certificate": types.StringType,
"client_key": types.StringType,
}), "client_configuration_wo is still unknown (ephemeral value not yet resolved)"
}
// Fall back to regular client_configuration
if !regularIsNull {
return state.ClientConfiguration, ""
}
// Both are null
return basetypes.NewObjectNull(map[string]attr.Type{
"ca_certificate": types.StringType,
"client_certificate": types.StringType,
"client_key": types.StringType,
}), "both client_configuration and client_configuration_wo are null"
}
func (r *talosMachineBootstrapResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Version: 1,
Description: "The machine bootstrap resource allows you to bootstrap a Talos node.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "This is a unique identifier for the machine ",
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"endpoint": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The endpoint of the machine to bootstrap",
},
"node": schema.StringAttribute{
Required: true,
Description: "The name of the node to bootstrap",
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Optional: true,
Description: "The client configuration data",
},
"client_configuration_wo": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
WriteOnly: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
WriteOnly: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
WriteOnly: true,
Description: "The client key",
},
},
Optional: true,
WriteOnly: true,
Description: "The client configuration data (write-only). Use this instead of client_configuration when using ephemeral resources. Requires Terraform 1.11+",
},
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
}),
},
}
}
func (r *talosMachineBootstrapResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var state talosMachineBootstrapResourceModelV1
diags := req.Plan.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// CRITICAL: Write-only attributes are NOT in Plan, only in Config!
var configState talosMachineBootstrapResourceModelV1
configDiags := req.Config.Get(ctx, &configState)
resp.Diagnostics.Append(configDiags...)
if configDiags.HasError() {
return
}
// Use write-only client_configuration from Config
if !configState.ClientConfigurationWO.IsNull() {
state.ClientConfigurationWO = configState.ClientConfigurationWO
}
clientConfig, configDiag := getBootstrapClientConfiguration(&state)
if configDiag != "" {
resp.Diagnostics.AddError(
"Client configuration issue",
configDiag,
)
return
}
ca, cert, key, errMsg, ok := getClientConfigurationValues(ctx, clientConfig)
if !ok {
resp.Diagnostics.AddError(
"Error extracting client configuration",
errMsg,
)
return
}
talosClientConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
ca,
cert,
key,
)
if err != nil {
resp.Diagnostics.AddError(
"Error converting config to talos client config",
err.Error(),
)
return
}
createTimeout, diags := state.Timeouts.Create(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctxDeadline, cancel := context.WithTimeout(ctx, createTimeout)
defer cancel()
if err := retry.RetryContext(ctxDeadline, createTimeout, func() *retry.RetryError {
if err := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosClientConfig, func(nodeCtx context.Context, c *client.Client) error {
return c.Bootstrap(nodeCtx, &machineapi.BootstrapRequest{})
}); err != nil {
if s := status.Code(err); s == codes.InvalidArgument {
return retry.NonRetryableError(err)
}
return retry.RetryableError(err)
}
return nil
}); err != nil {
resp.Diagnostics.AddError(
"Error bootstrapping node",
err.Error(),
)
return
}
state.ID = basetypes.NewStringValue("machine_bootstrap")
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *talosMachineBootstrapResource) Read(_ context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) {
}
func (r *talosMachineBootstrapResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var state talosMachineBootstrapResourceModelV1
diags := req.Plan.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *talosMachineBootstrapResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
}
func (r *talosMachineBootstrapResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
// delete is a no-op
if req.Plan.Raw.IsNull() {
return
}
var configObj types.Object
diags := req.Config.Get(ctx, &configObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosMachineBootstrapResourceModelV1
diags = configObj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// if either endpoint or node is unknown return early
if config.Endpoint.IsUnknown() || config.Node.IsUnknown() {
return
}
var planObj types.Object
diags = req.Plan.Get(ctx, &planObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var planState talosMachineBootstrapResourceModelV1
diags = configObj.As(ctx, &planState, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if planState.Endpoint.IsUnknown() || planState.Endpoint.IsNull() {
diags = resp.Plan.SetAttribute(ctx, path.Root("endpoint"), planState.Node.ValueString())
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
}
}
func (r *talosMachineBootstrapResource) UpgradeState(_ context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: &schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"endpoint": schema.StringAttribute{
Required: true,
},
"node": schema.StringAttribute{
Required: true,
},
"talos_config": schema.StringAttribute{
Required: true,
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var priorStateData talosMachineBootstrapResourceModelV0
diags := req.State.Get(ctx, &priorStateData)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
timeout, diag := basetypes.NewObjectValue(map[string]attr.Type{
"create": types.StringType,
}, map[string]attr.Value{
"create": basetypes.NewStringNull(),
})
resp.Diagnostics.Append(diag...)
if resp.Diagnostics.HasError() {
return
}
// Create null client configuration with proper type
clientConfig := basetypes.NewObjectNull(map[string]attr.Type{
"ca_certificate": types.StringType,
"client_certificate": types.StringType,
"client_key": types.StringType,
})
state := talosMachineBootstrapResourceModelV1{
ID: basetypes.NewStringValue("machine_bootstrap"),
Endpoint: priorStateData.Endpoint,
Node: priorStateData.Node,
ClientConfiguration: clientConfig,
ClientConfigurationWO: clientConfig,
Timeouts: timeouts.Value{
Object: timeout,
},
}
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
},
},
}
}
func (r *talosMachineBootstrapResource) ImportState(ctx context.Context, _ resource.ImportStateRequest, resp *resource.ImportStateResponse) {
timeout, diag := basetypes.NewObjectValue(map[string]attr.Type{
"create": types.StringType,
}, map[string]attr.Value{
"create": basetypes.NewStringNull(),
})
resp.Diagnostics.Append(diag...)
if resp.Diagnostics.HasError() {
return
}
// Create null client configuration with proper type
clientConfig := basetypes.NewObjectNull(map[string]attr.Type{
"ca_certificate": types.StringType,
"client_certificate": types.StringType,
"client_key": types.StringType,
})
state := talosMachineBootstrapResourceModelV1{
ID: basetypes.NewStringValue("machine_bootstrap"),
ClientConfiguration: clientConfig,
ClientConfigurationWO: clientConfig,
Timeouts: timeouts.Value{
Object: timeout,
},
}
// Set state to fully populated data
diags := resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_machine_bootstrap_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"fmt"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccTalosMachineBootstrapResource(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // import can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
// import the resource
Config: testAccTalosMachineBootstrapResourceConfigImport("10.5.0.2"),
ResourceName: "talos_machine_bootstrap.this",
ImportStateId: "this",
ImportState: true,
ImportStatePersist: true,
},
// verify state is correct after import
{
Config: testAccTalosMachineBootstrapResourceConfigImport("10.5.0.2"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_bootstrap.this", "id", "machine_bootstrap"),
resource.TestCheckResourceAttr("talos_machine_bootstrap.this", "node", "10.5.0.2"),
resource.TestCheckResourceAttr("talos_machine_bootstrap.this", "endpoint", "10.5.0.2"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "client_configuration.client_key"),
),
},
},
})
}
func TestAccTalosMachineBootstrapResourceUpgrade(t *testing.T) {
// ref: https://github.com/hashicorp/terraform-plugin-testing/pull/118
t.Skip("skipping until TF test framework has a way to remove state resource")
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
Steps: []resource.TestStep{
// create TF config with v0.1.2 of the talos provider
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.1.2",
Source: "siderolabs/talos",
},
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
Config: testAccTalosMachineBootstrapResourceConfigV0("talosv1", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckNoResourceAttr("talos_client_configuration", "this"),
resource.TestCheckNoResourceAttr("talos_machine_configuration_controlplane", "this"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply", "id", "this"),
),
},
// now test state migration with the latest version of the provider
{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
Config: testAccTalosMachineBootstrapResourceConfigV1("talos", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_bootstrap.this", "id", "machine_bootstrap"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "node"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "endpoint"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_bootstrap.this", "client_configuration.client_key"),
),
},
// ensure there is no diff
{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
Config: testAccTalosMachineBootstrapResourceConfigV1("talos", rName),
PlanOnly: true,
},
},
})
}
func testAccTalosMachineBootstrapResourceConfigV0(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: true,
}
return config.render()
}
func testAccTalosMachineBootstrapResourceConfigV1(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: true,
}
return config.render()
}
func testAccTalosMachineBootstrapResourceConfigImport(node string) string {
return fmt.Sprintf(`
resource "talos_machine_secrets" "this" {}
resource "talos_machine_bootstrap" "this" {
node = "%s"
client_configuration = talos_machine_secrets.this.client_configuration
}
`, node)
}
================================================
FILE: pkg/talos/talos_machine_configuration_apply_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"crypto/sha256"
"crypto/tls"
"encoding/hex"
"errors"
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/action"
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/client"
"github.com/siderolabs/talos/pkg/machinery/config/configpatcher"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type talosMachineConfigurationApplyResource struct{}
var (
_ resource.Resource = &talosMachineConfigurationApplyResource{}
_ resource.ResourceWithModifyPlan = &talosMachineConfigurationApplyResource{}
_ resource.ResourceWithUpgradeState = &talosMachineConfigurationApplyResource{}
_ resource.ResourceWithValidateConfig = &talosMachineConfigurationApplyResource{}
)
var onDestroyMarkDownDescription = `Actions to be taken on destroy, if *reset* is not set this is a no-op.
> Note: Any changes to *on_destroy* block has to be applied first by running *terraform apply* first,
then a subsequent *terraform destroy* for the changes to take effect due to limitations in Terraform provider framework.
`
type talosMachineConfigurationApplyResourceModelV0 struct {
Mode types.String `tfsdk:"mode"`
Node types.String `tfsdk:"node"`
Endpoint types.String `tfsdk:"endpoint"`
TalosConfig types.String `tfsdk:"talos_config"`
MachineConfiguration types.String `tfsdk:"machine_configuration"`
ConfigPatches types.List `tfsdk:"config_patches"`
}
type talosMachineConfigurationApplyResourceModelV1 struct { //nolint:govet
ID types.String `tfsdk:"id"`
ApplyMode types.String `tfsdk:"apply_mode"`
ResolvedApplyMode types.String `tfsdk:"resolved_apply_mode"`
Node types.String `tfsdk:"node"`
Endpoint types.String `tfsdk:"endpoint"`
ClientConfiguration basetypes.ObjectValue `tfsdk:"client_configuration"`
ClientConfigurationWO basetypes.ObjectValue `tfsdk:"client_configuration_wo"`
MachineConfigurationInput types.String `tfsdk:"machine_configuration_input"`
MachineConfigurationInputWO types.String `tfsdk:"machine_configuration_input_wo"`
OnDestroy *onDestroyOptions `tfsdk:"on_destroy"`
MachineConfiguration types.String `tfsdk:"machine_configuration"`
MachineConfigurationHash types.String `tfsdk:"machine_configuration_hash"`
ConfigPatches types.List `tfsdk:"config_patches"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
type onDestroyOptions struct {
Reset bool `tfsdk:"reset"`
Graceful bool `tfsdk:"graceful"`
Reboot bool `tfsdk:"reboot"`
}
// NewTalosMachineConfigurationApplyResource implements the resource.Resource interface.
func NewTalosMachineConfigurationApplyResource() resource.Resource {
return &talosMachineConfigurationApplyResource{}
}
func (p *talosMachineConfigurationApplyResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_machine_configuration_apply"
}
func (p *talosMachineConfigurationApplyResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Version: 1,
Description: "The machine configuration apply resource allows to apply machine configuration to a node",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "This is a unique identifier for the machine ",
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"apply_mode": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The mode of the apply operation. Use 'staged_if_needing_reboot' for automatic reboot prevention: " +
"performs a dry-run and uses 'staged' mode if reboot is needed, 'auto' otherwise",
Validators: []validator.String{
stringvalidator.OneOf("auto", "reboot", "no_reboot", "staged", "staged_if_needing_reboot"),
},
Default: stringdefault.StaticString("auto"),
},
"resolved_apply_mode": schema.StringAttribute{
Computed: true,
Description: "The actual apply mode used. When apply_mode is 'staged_if_needing_reboot', " +
"shows the resolved mode ('auto' or 'staged') based on dry-run analysis. Equals apply_mode for other modes.",
},
"node": schema.StringAttribute{
Required: true,
Description: "The name of the node to bootstrap",
},
"endpoint": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The endpoint of the machine to bootstrap",
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Optional: true,
Description: "The client configuration data",
},
"client_configuration_wo": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
WriteOnly: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
WriteOnly: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
WriteOnly: true,
Description: "The client key",
},
},
Optional: true,
WriteOnly: true,
Description: "The client configuration data (write-only). Use this instead of client_configuration when using ephemeral resources. Requires Terraform 1.11+",
},
"machine_configuration_input": schema.StringAttribute{
Description: "The machine configuration to apply",
Optional: true,
Sensitive: true,
},
"machine_configuration_input_wo": schema.StringAttribute{
Description: "The machine configuration to apply (write-only). Use this instead of machine_configuration_input when using ephemeral resources. Requires Terraform 1.11+",
Optional: true,
WriteOnly: true,
},
"on_destroy": schema.SingleNestedAttribute{
Description: "Actions to be taken on destroy, if `reset` is not set this is a no-op.",
MarkdownDescription: onDestroyMarkDownDescription,
Optional: true,
Attributes: map[string]schema.Attribute{
"reset": schema.BoolAttribute{
Description: "Reset the machine to the initial state (STATE and EPHEMERAL will be wiped). Default false",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
},
"graceful": schema.BoolAttribute{
Description: "Graceful indicates whether node should leave etcd before the upgrade, it also enforces etcd checks before leaving. Default true",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
},
"reboot": schema.BoolAttribute{
Description: "Reboot indicates whether node should reboot or halt after resetting. Default false",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
},
},
},
"machine_configuration": schema.StringAttribute{
Description: "The generated machine configuration after applying patches",
Computed: true,
Sensitive: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"machine_configuration_hash": schema.StringAttribute{
Description: "SHA256 hex digest of the rendered machine configuration (input plus patches). " +
"Persisted in state so that changes to machine_configuration_input_wo — which is write-only " +
"and itself invisible to state — still surface as plan diffs.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"config_patches": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "The list of config patches to apply",
},
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
Update: true,
Delete: true,
}),
},
}
}
func (p *talosMachineConfigurationApplyResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var config talosMachineConfigurationApplyResourceModelV1
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
inputSet := !config.MachineConfigurationInput.IsNull()
inputWOSet := !config.MachineConfigurationInputWO.IsNull()
if !inputSet && !inputWOSet {
resp.Diagnostics.AddError(
"Missing machine configuration input",
"Exactly one of machine_configuration_input or machine_configuration_input_wo must be set",
)
}
if inputSet && inputWOSet {
resp.Diagnostics.AddError(
"Conflicting machine configuration input",
"Only one of machine_configuration_input or machine_configuration_input_wo can be set, not both",
)
}
clientConfigSet := !config.ClientConfiguration.IsNull()
clientConfigWOSet := !config.ClientConfigurationWO.IsNull()
if !clientConfigSet && !clientConfigWOSet {
resp.Diagnostics.AddError(
"Missing client configuration",
"Exactly one of client_configuration or client_configuration_wo must be set",
)
}
if clientConfigSet && clientConfigWOSet {
resp.Diagnostics.AddError(
"Conflicting client configuration",
"Only one of client_configuration or client_configuration_wo can be set, not both",
)
}
}
// getMachineConfigurationInput returns the effective machine configuration input value,
// preferring the write-only attribute if set.
func getMachineConfigurationInput(state *talosMachineConfigurationApplyResourceModelV1) types.String {
if !state.MachineConfigurationInputWO.IsNull() && !state.MachineConfigurationInputWO.IsUnknown() {
return state.MachineConfigurationInputWO
}
return state.MachineConfigurationInput
}
// computeMachineConfiguration applies patches to the input configuration and returns the result.
func computeMachineConfiguration(state *talosMachineConfigurationApplyResourceModelV1) (string, error) {
machineConfigInput := getMachineConfigurationInput(state)
if machineConfigInput.IsNull() {
return "", fmt.Errorf("machine configuration input is null")
}
configPatches, err := configPatchesAsStrings(state.ConfigPatches)
if err != nil {
return "", err
}
patches, err := configpatcher.LoadPatches(configPatches)
if err != nil {
return "", fmt.Errorf("error loading config patches: %w", err)
}
cfg, err := configpatcher.Apply(configpatcher.WithBytes([]byte(machineConfigInput.ValueString())), patches)
if err != nil {
return "", fmt.Errorf("error applying config patches: %w", err)
}
cfgBytes, err := cfg.Bytes()
if err != nil {
return "", fmt.Errorf("error converting config to bytes: %w", err)
}
return string(cfgBytes), nil
}
// configPatchesAsStrings extracts a []string from a types.List of strings. It returns
// an error if the list itself is unknown (caller cannot render in that case) or if any
// element is unknown; null values are skipped. A null or empty list yields an empty slice.
func configPatchesAsStrings(list types.List) ([]string, error) {
if list.IsUnknown() {
return nil, fmt.Errorf("config_patches list is unknown")
}
if list.IsNull() {
return nil, nil
}
elements := list.Elements()
result := make([]string, 0, len(elements))
for _, elem := range elements {
s, ok := elem.(basetypes.StringValue)
if !ok {
return nil, fmt.Errorf("config_patches element is not a string: %T", elem)
}
if s.IsUnknown() {
return nil, fmt.Errorf("config_patches element is unknown")
}
if s.IsNull() {
continue
}
result = append(result, s.ValueString())
}
return result, nil
}
// getClientConfiguration returns the effective client configuration,
// preferring the write-only attribute if set.
func getClientConfiguration(state *talosMachineConfigurationApplyResourceModelV1) (config basetypes.ObjectValue, diagMsg string) {
woIsNull := state.ClientConfigurationWO.IsNull()
woIsUnknown := state.ClientConfigurationWO.IsUnknown()
regularIsNull := state.ClientConfiguration.IsNull()
// Prefer write-only if available and known
if !woIsNull && !woIsUnknown {
return state.ClientConfigurationWO, ""
}
// If write-only was provided but is still unknown, that's a problem
if !woIsNull && woIsUnknown {
return basetypes.NewObjectNull(map[string]attr.Type{
"ca_certificate": types.StringType,
"client_certificate": types.StringType,
"client_key": types.StringType,
}), "client_configuration_wo is still unknown (ephemeral value not yet resolved)"
}
// Fall back to regular client_configuration
if !regularIsNull {
return state.ClientConfiguration, ""
}
// Both are null
return basetypes.NewObjectNull(map[string]attr.Type{
"ca_certificate": types.StringType,
"client_certificate": types.StringType,
"client_key": types.StringType,
}), "both client_configuration and client_configuration_wo are null"
}
// getClientConfigurationValues extracts the client configuration values from the ObjectValue.
// Returns empty strings and error message if extraction fails.
// Handles both properly-typed ObjectValues and plain maps with correct keys (from Vault reconstruction).
func getClientConfigurationValues(ctx context.Context, clientConfig basetypes.ObjectValue) (ca, cert, key, errMsg string, ok bool) {
if clientConfig.IsUnknown() {
return "", "", "", "client configuration is unknown", false
}
if clientConfig.IsNull() {
return "", "", "", "client configuration is null", false
}
var config clientConfiguration
if diags := clientConfig.As(ctx, &config, basetypes.ObjectAsOptions{}); diags.HasError() {
// Fallback: Extract values directly from the ObjectValue's attributes
// This handles cases where the object is a plain map with correct keys (e.g., reconstructed from Vault)
// without full schema metadata attached at runtime
attrs := clientConfig.Attributes()
caAttr, caOk := attrs["ca_certificate"]
certAttr, certOk := attrs["client_certificate"]
keyAttr, keyOk := attrs["client_key"]
if !caOk || !certOk || !keyOk {
missingKeys := []string{}
if !caOk {
missingKeys = append(missingKeys, "ca_certificate")
}
if !certOk {
missingKeys = append(missingKeys, "client_certificate")
}
if !keyOk {
missingKeys = append(missingKeys, "client_key")
}
return "", "", "", fmt.Sprintf("missing required keys: %v (available keys: %v)", missingKeys, getMapKeys(attrs)), false
}
// Type assert to StringValue and extract string
caVal, caIsString := caAttr.(basetypes.StringValue)
certVal, certIsString := certAttr.(basetypes.StringValue)
keyVal, keyIsString := keyAttr.(basetypes.StringValue)
if !caIsString || !certIsString || !keyIsString {
wrongTypes := []string{}
if !caIsString {
wrongTypes = append(wrongTypes, fmt.Sprintf("ca_certificate is %T", caAttr))
}
if !certIsString {
wrongTypes = append(wrongTypes, fmt.Sprintf("client_certificate is %T", certAttr))
}
if !keyIsString {
wrongTypes = append(wrongTypes, fmt.Sprintf("client_key is %T", keyAttr))
}
return "", "", "", fmt.Sprintf("wrong types for client configuration attributes: %s", strings.Join(wrongTypes, ", ")), false
}
return caVal.ValueString(), certVal.ValueString(), keyVal.ValueString(), "", true
}
return config.CA.ValueString(), config.Cert.ValueString(), config.Key.ValueString(), "", true
}
// getMapKeys returns the keys of a map for diagnostic purposes.
func getMapKeys(m map[string]attr.Value) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
func (p *talosMachineConfigurationApplyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { //nolint:dupl
var state talosMachineConfigurationApplyResourceModelV1
diags := req.Plan.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// CRITICAL: Write-only attributes are NOT in Plan, only in Config!
// We need to read write-only attributes from Config
var configState talosMachineConfigurationApplyResourceModelV1
configDiags := req.Config.Get(ctx, &configState)
resp.Diagnostics.Append(configDiags...)
if configDiags.HasError() {
return
}
// Use write-only attributes from Config, everything else from Plan
if !configState.ClientConfigurationWO.IsNull() {
state.ClientConfigurationWO = configState.ClientConfigurationWO
}
if !configState.MachineConfigurationInputWO.IsNull() {
state.MachineConfigurationInputWO = configState.MachineConfigurationInputWO
}
clientConfig, configDiag := getClientConfiguration(&state)
if configDiag != "" {
resp.Diagnostics.AddError(
"Client configuration issue",
configDiag,
)
return
}
ca, cert, key, errMsg, ok := getClientConfigurationValues(ctx, clientConfig)
if !ok {
resp.Diagnostics.AddError(
"Error reading client configuration",
errMsg,
)
return
}
talosClientConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
ca,
cert,
key,
)
if err != nil {
resp.Diagnostics.AddError(
"Error converting config to talos client config",
err.Error(),
)
return
}
createTimeout, diags := state.Timeouts.Create(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctxDeadline, cancel := context.WithTimeout(ctx, createTimeout)
defer cancel()
effectiveMode := getEffectiveMode(&state)
// Get the machine configuration to apply
// When using write-only inputs, machine_configuration is not stored in state,
// so we need to compute it here from the input and patches
machineConfigToApply := state.MachineConfiguration.ValueString()
if state.MachineConfiguration.IsNull() && !state.MachineConfigurationInputWO.IsNull() {
// Compute configuration from write-only input and patches
computed, err := computeMachineConfiguration(&state)
if err != nil {
resp.Diagnostics.AddError(
"Error computing machine configuration",
err.Error(),
)
return
}
machineConfigToApply = computed
}
if err := retry.RetryContext(ctxDeadline, createTimeout, func() *retry.RetryError {
if err := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosClientConfig, func(nodeCtx context.Context, c *client.Client) error {
_, err := c.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
Mode: machineapi.ApplyConfigurationRequest_Mode(machineapi.ApplyConfigurationRequest_Mode_value[strings.ToUpper(effectiveMode)]),
Data: []byte(machineConfigToApply),
})
if err != nil {
return err
}
return nil
}); err != nil {
if s := status.Code(err); s == codes.InvalidArgument {
return retry.NonRetryableError(err)
}
return retry.RetryableError(err)
}
return nil
}); err != nil {
resp.Diagnostics.AddError(
"Error applying configuration",
err.Error(),
)
return
}
state.ID = basetypes.NewStringValue("machine_configuration_apply")
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (p *talosMachineConfigurationApplyResource) Read(_ context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) {
}
func (p *talosMachineConfigurationApplyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { //nolint:dupl
var state talosMachineConfigurationApplyResourceModelV1
diags := req.Plan.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// CRITICAL: Write-only attributes are NOT in Plan, only in Config!
var configState talosMachineConfigurationApplyResourceModelV1
configDiags := req.Config.Get(ctx, &configState)
resp.Diagnostics.Append(configDiags...)
if configDiags.HasError() {
return
}
// Use write-only attributes from Config
if !configState.ClientConfigurationWO.IsNull() {
state.ClientConfigurationWO = configState.ClientConfigurationWO
}
if !configState.MachineConfigurationInputWO.IsNull() {
state.MachineConfigurationInputWO = configState.MachineConfigurationInputWO
}
clientConfig, configDiag := getClientConfiguration(&state)
if configDiag != "" {
resp.Diagnostics.AddError(
"Client configuration issue",
configDiag,
)
return
}
ca, cert, key, errMsg, ok := getClientConfigurationValues(ctx, clientConfig)
if !ok {
resp.Diagnostics.AddError(
"Error reading client configuration",
errMsg,
)
return
}
talosClientConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
ca,
cert,
key,
)
if err != nil {
resp.Diagnostics.AddError(
"Error converting config to talos client config",
err.Error(),
)
return
}
updateTimeout, diags := state.Timeouts.Update(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctxDeadline, cancel := context.WithTimeout(ctx, updateTimeout)
defer cancel()
effectiveMode := getEffectiveMode(&state)
// Get the machine configuration to apply
// When using write-only inputs, machine_configuration is not stored in state,
// so we need to compute it here from the input and patches
machineConfigToApply := state.MachineConfiguration.ValueString()
if state.MachineConfiguration.IsNull() && !state.MachineConfigurationInputWO.IsNull() {
// Compute configuration from write-only input and patches
computed, err := computeMachineConfiguration(&state)
if err != nil {
resp.Diagnostics.AddError(
"Error computing machine configuration",
err.Error(),
)
return
}
machineConfigToApply = computed
}
if err := retry.RetryContext(ctxDeadline, updateTimeout, func() *retry.RetryError {
if err := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosClientConfig, func(nodeCtx context.Context, c *client.Client) error {
_, err := c.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
Mode: machineapi.ApplyConfigurationRequest_Mode(machineapi.ApplyConfigurationRequest_Mode_value[strings.ToUpper(effectiveMode)]),
Data: []byte(machineConfigToApply),
})
if err != nil {
return err
}
return nil
}); err != nil {
if s := status.Code(err); s == codes.InvalidArgument {
return retry.NonRetryableError(err)
}
return retry.RetryableError(err)
}
return nil
}); err != nil {
resp.Diagnostics.AddError(
"Error applying configuration",
err.Error(),
)
return
}
state.ID = basetypes.NewStringValue("machine_configuration_apply")
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func getEffectiveMode(state *talosMachineConfigurationApplyResourceModelV1) string {
effectiveMode := state.ResolvedApplyMode.ValueString()
if effectiveMode == "" || state.ResolvedApplyMode.IsNull() {
effectiveMode = state.ApplyMode.ValueString()
}
return effectiveMode
}
func (p *talosMachineConfigurationApplyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state talosMachineConfigurationApplyResourceModelV1
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
if state.OnDestroy != nil && state.OnDestroy.Reset {
// NOTE: During Delete, write-only attributes are not available (not in state)
// If using client_configuration_wo, the reset on destroy won't work
// Users must use client_configuration (non-write-only) if they need on_destroy.reset
clientConfig, configDiag := getClientConfiguration(&state)
if configDiag != "" {
resp.Diagnostics.AddError(
"Client configuration issue during destroy",
fmt.Sprintf("%s\n\nNote: If you're using client_configuration_wo (write-only), "+
"it's not available during destroy. Use client_configuration instead if you need on_destroy.reset functionality.",
configDiag),
)
return
}
ca, cert, key, errMsg, ok := getClientConfigurationValues(ctx, clientConfig)
if !ok {
resp.Diagnostics.AddError(
"Error reading client configuration",
errMsg,
)
return
}
talosClientConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
ca,
cert,
key,
)
if err != nil {
resp.Diagnostics.AddError(
"Error converting config to talos client config",
err.Error(),
)
return
}
deleteTimeout, diags := state.Timeouts.Delete(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
resetRequest := &machineapi.ResetRequest{
Graceful: state.OnDestroy.Graceful,
Reboot: state.OnDestroy.Reboot,
SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{
{
Label: "STATE",
Wipe: true,
},
{
Label: "EPHEMERAL",
Wipe: true,
},
},
}
actionFn := func(ctx context.Context, c *client.Client) (string, error) {
return resetGetActorID(ctx, c, resetRequest)
}
var postCheckFn func(context.Context, *client.Client, string) error
if state.OnDestroy.Reboot {
postCheckFn = func(ctx context.Context, c *client.Client, preActionBootID string) error {
insecureClient, err := client.New(
ctx,
client.WithTLSConfig(&tls.Config{
InsecureSkipVerify: true,
}),
client.WithEndpoints(state.Endpoint.ValueString()),
)
if err != nil {
return err
}
_, err = insecureClient.Disks(client.WithNode(ctx, state.Node.ValueString()))
// if we can get into maintenance mode, reset has succeeded
if err == nil {
return nil
}
// try to get the boot ID in the normal mode to see if the node has rebooted
return action.BootIDChangedPostCheckFn(ctx, c, preActionBootID)
}
}
if err := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosClientConfig, func(_ context.Context, c *client.Client) error {
executor := newClientExecutor(c, []string{state.Node.ValueString()})
return action.NewTracker(
executor,
action.StopAllServicesEventFn,
actionFn,
action.WithPostCheck(postCheckFn),
action.WithDebug(false),
action.WithTimeout(deleteTimeout),
).Run()
}); err != nil {
resp.Diagnostics.AddError("Error resetting machine", err.Error())
return
}
}
}
func setResolvedApplyMode(ctx context.Context, resp *resource.ModifyPlanResponse, mode string) {
diags := resp.Plan.SetAttribute(ctx, path.Root("resolved_apply_mode"), mode)
resp.Diagnostics.Append(diags...)
}
func dryRunNeedsReboot(cfgBytes []byte, needsReboot *bool) func(context.Context, *client.Client) error {
return func(nodeCtx context.Context, c *client.Client) error {
applyResp, err := c.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
Mode: machineapi.ApplyConfigurationRequest_AUTO,
Data: cfgBytes,
DryRun: true,
})
if err != nil {
return err
}
if len(applyResp.Messages) > 0 {
*needsReboot = (applyResp.Messages[0].Mode == machineapi.ApplyConfigurationRequest_REBOOT)
}
return nil
}
}
func (p *talosMachineConfigurationApplyResource) handleRebootPrevention(
ctx context.Context,
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse,
planState *talosMachineConfigurationApplyResourceModelV1,
cfgBytes []byte,
) {
applyMode := strings.ToLower(planState.ApplyMode.ValueString())
if applyMode == "" || planState.ApplyMode.IsNull() || planState.ApplyMode.IsUnknown() {
applyMode = "auto"
}
if applyMode != "staged_if_needing_reboot" {
setResolvedApplyMode(ctx, resp, applyMode)
return
}
// Cannot perform dry-run if node address is unknown (computed from another resource)
if planState.Node.IsUnknown() {
setResolvedApplyMode(ctx, resp, "auto")
return
}
// For updates: avoid unnecessary dry-run if configuration hasn't changed
if !req.State.Raw.IsNull() {
var currentState talosMachineConfigurationApplyResourceModelV1
diags := req.State.Get(ctx, ¤tState)
if diags.HasError() {
return
}
if currentState.MachineConfiguration.Equal(types.StringValue(string(cfgBytes))) {
if !currentState.ResolvedApplyMode.IsNull() && currentState.ResolvedApplyMode.ValueString() != "" {
setResolvedApplyMode(ctx, resp, currentState.ResolvedApplyMode.ValueString())
return
}
}
}
endpoint := planState.Endpoint.ValueString()
if endpoint == "" || planState.Endpoint.IsNull() || planState.Endpoint.IsUnknown() {
endpoint = planState.Node.ValueString()
}
// Cannot perform dry-run if client configuration is unknown (from ephemeral resource)
clientConfig, configDiag := getClientConfiguration(planState)
if configDiag != "" {
// If configuration is not available (unknown/null), fall back to auto mode
setResolvedApplyMode(ctx, resp, "auto")
return
}
ca, cert, key, _, ok := getClientConfigurationValues(ctx, clientConfig)
if !ok {
setResolvedApplyMode(ctx, resp, "auto")
return
}
talosClientConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
ca,
cert,
key,
)
if err != nil {
resp.Diagnostics.AddWarning(
"Cannot check reboot requirement",
fmt.Sprintf("Node %s: %v. Using 'auto' mode (may reboot).", planState.Node.ValueString(), err),
)
setResolvedApplyMode(ctx, resp, "auto")
return
}
var needsReboot bool
err = talosClientOp(ctx, endpoint, planState.Node.ValueString(), talosClientConfig,
dryRunNeedsReboot(cfgBytes, &needsReboot),
)
if err != nil {
resp.Diagnostics.AddWarning(
"Cannot check reboot requirement",
fmt.Sprintf("Node %s: %v. Using 'auto' mode (may reboot).", planState.Node.ValueString(), err),
)
setResolvedApplyMode(ctx, resp, "auto")
return
}
if needsReboot {
setResolvedApplyMode(ctx, resp, "staged")
resp.Diagnostics.AddWarning(
"Reboot prevented - using staged mode",
fmt.Sprintf("Node %s: Configuration requires reboot. Using 'staged' mode. Manually reboot with: talosctl reboot --nodes %s",
planState.Node.ValueString(), planState.Node.ValueString()),
)
} else {
setResolvedApplyMode(ctx, resp, "auto")
}
}
func (p *talosMachineConfigurationApplyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { //nolint:gocyclo,cyclop
// delete is a no-op
if req.Plan.Raw.IsNull() {
return
}
var configObj types.Object
diags := req.Config.Get(ctx, &configObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosMachineConfigurationApplyResourceModelV1
diags = configObj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// if either endpoint or node is unknown return early
if config.Endpoint.IsUnknown() || config.Node.IsUnknown() || config.MachineConfiguration.IsUnknown() {
return
}
var planObj types.Object
diags = req.Plan.Get(ctx, &planObj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var planState talosMachineConfigurationApplyResourceModelV1
diags = configObj.As(ctx, &planState, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if planState.Endpoint.IsUnknown() || planState.Endpoint.IsNull() {
diags = resp.Plan.SetAttribute(ctx, path.Root("endpoint"), planState.Node.ValueString())
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
}
machineConfigInput := getMachineConfigurationInput(&planState)
// Only compute machine_configuration if inputs are available
// If inputs are unknown (e.g., ephemeral values not yet resolved), the UseStateForUnknown
// plan modifier will preserve the prior state value to prevent drift
if !machineConfigInput.IsUnknown() && !machineConfigInput.IsNull() {
// If the whole config_patches list is unknown (e.g. `[for x in : ...]`)
// or any element is unknown, we can't render — return so UseStateForUnknown wins.
if planState.ConfigPatches.IsUnknown() {
return
}
configPatches, err := configPatchesAsStrings(planState.ConfigPatches)
if err != nil {
// err is only returned when an element is Unknown — bail out like before.
return
}
patches, err := configpatcher.LoadPatches(configPatches)
if err != nil {
resp.Diagnostics.AddError(
"Error loading config patches",
err.Error(),
)
return
}
cfg, err := configpatcher.Apply(configpatcher.WithBytes([]byte(machineConfigInput.ValueString())), patches)
if err != nil {
resp.Diagnostics.AddError(
"Error applying config patches",
err.Error(),
)
return
}
cfgBytes, err := cfg.Bytes()
if err != nil {
resp.Diagnostics.AddError(
"Error converting config to bytes",
err.Error(),
)
return
}
p.setPlanMachineConfiguration(ctx, resp, &planState, cfgBytes)
if resp.Diagnostics.HasError() {
return
}
p.handleRebootPrevention(ctx, req, resp, &planState, cfgBytes)
}
}
// setPlanMachineConfiguration sets the machine_configuration attribute in the plan.
// When write-only inputs are used, it sets the value to null to avoid storing secrets in state.
// It also always sets machine_configuration_hash — a SHA256 fingerprint of the rendered
// config — so that changes to write-only inputs (invisible to state) surface as plan diffs.
func (p *talosMachineConfigurationApplyResource) setPlanMachineConfiguration(
ctx context.Context,
resp *resource.ModifyPlanResponse,
planState *talosMachineConfigurationApplyResourceModelV1,
cfgBytes []byte,
) {
sum := sha256.Sum256(cfgBytes)
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("machine_configuration_hash"), hex.EncodeToString(sum[:]))...)
// When using write-only inputs (_wo variants), don't populate the computed
// machine_configuration to prevent secrets from being stored in state.
if !planState.MachineConfigurationInputWO.IsNull() {
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("machine_configuration"), types.StringNull())...)
return
}
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("machine_configuration"), string(cfgBytes))...)
}
func (p *talosMachineConfigurationApplyResource) UpgradeState(_ context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: &schema.Schema{
Attributes: map[string]schema.Attribute{
"mode": schema.StringAttribute{
Optional: true,
},
"endpoint": schema.StringAttribute{
Required: true,
},
"node": schema.StringAttribute{
Required: true,
},
"talos_config": schema.StringAttribute{
Required: true,
},
"machine_configuration": schema.StringAttribute{
Required: true,
},
"config_patches": schema.ListAttribute{
Optional: true,
ElementType: types.StringType,
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var priorStateData talosMachineConfigurationApplyResourceModelV0
diags := req.State.Get(ctx, &priorStateData)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
var patches []string
diags = append(diags, priorStateData.ConfigPatches.ElementsAs(ctx, &patches, true)...)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
configPatchesElems := make([]attr.Value, len(patches))
for i, patch := range patches {
configPatchesElems[i] = basetypes.NewStringValue(patch)
}
configPatches, listDiags := basetypes.NewListValue(types.StringType, configPatchesElems)
resp.Diagnostics.Append(listDiags...)
if resp.Diagnostics.HasError() {
return
}
timeout, diag := basetypes.NewObjectValue(map[string]attr.Type{
"create": types.StringType,
"update": types.StringType,
}, map[string]attr.Value{
"create": basetypes.NewStringNull(),
"update": basetypes.NewStringNull(),
})
resp.Diagnostics.Append(diag...)
if resp.Diagnostics.HasError() {
return
}
state := talosMachineConfigurationApplyResourceModelV1{
ID: basetypes.NewStringValue("machine_configuration_apply"),
ApplyMode: priorStateData.Mode,
Node: priorStateData.Node,
Endpoint: priorStateData.Endpoint,
MachineConfigurationInput: priorStateData.MachineConfiguration,
ConfigPatches: configPatches,
Timeouts: timeouts.Value{
Object: timeout,
},
}
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
},
},
}
}
func resetGetActorID(ctx context.Context, c *client.Client, req *machineapi.ResetRequest) (string, error) {
resp, err := c.ResetGenericWithResponse(ctx, req)
if err != nil {
return "", err
}
if len(resp.GetMessages()) == 0 {
return "", errors.New("no messages returned from action run")
}
return resp.GetMessages()[0].GetActorId(), nil
}
type clientExecutor struct {
c *client.Client
nodes []string
}
func newClientExecutor(c *client.Client, nodes []string) *clientExecutor {
return &clientExecutor{
c: c,
nodes: nodes,
}
}
func (c *clientExecutor) WithClient(action func(context.Context, *client.Client) error, _ ...grpc.DialOption) error {
ctx := client.WithNodes(context.Background(), c.nodes...)
return action(ctx, c.c)
}
func (c *clientExecutor) NodeList() []string {
return c.nodes
}
================================================
FILE: pkg/talos/talos_machine_configuration_apply_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"fmt"
"os"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
"github.com/siderolabs/talos/pkg/machinery/gendata"
)
func TestAccTalosMachineConfigurationApplyResource(t *testing.T) {
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosMachineConfigurationApplyResourceConfig("talos", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "id", "machine_configuration_apply"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "apply_mode", "auto"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "node"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "endpoint"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "client_configuration.client_key"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "machine_configuration_input"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "machine_configuration"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "config_patches.#", "1"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "config_patches.0", "\"machine\":\n \"install\":\n \"disk\": \"/dev/vda\"\n"),
),
},
// ensure there is no diff
{
Config: testAccTalosMachineConfigurationApplyResourceConfig("talos", rName),
PlanOnly: true,
},
},
})
}
// TestAccTalosMachineConfigurationApplyResourceAutoStaged tests the "staged_if_needing_reboot" apply mode.
//
// Note on local vs CI environment:
// During local development, the node IP was sometimes unknown during the plan phase,
// preventing the dry-run from being performed. However, in CI, the libvirt setup
// allows the node IP to be known immediately, enabling the dry-run to execute.
// Since the configuration requires a reboot, the dry-run correctly resolves to
// "staged" mode to prevent uncontrolled reboots.
func TestAccTalosMachineConfigurationApplyResourceAutoStaged(t *testing.T) {
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosMachineConfigurationApplyResourceConfigWithAutoStaged("talos", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "id", "machine_configuration_apply"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "apply_mode", "staged_if_needing_reboot"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "resolved_apply_mode", "staged"),
),
},
},
})
}
// logApplyModeState returns a TestCheckFunc that logs the apply_mode and resolved_apply_mode
// attributes of the staged_if_needing_reboot resource for debugging upgrade tests.
func logApplyModeState(t *testing.T, stepName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources["talos_machine_configuration_apply.staged_if_needing_reboot"]
if !ok {
t.Logf("[%s] Resource not found in state", stepName)
return nil
}
t.Logf("[%s] apply_mode = %q", stepName, rs.Primary.Attributes["apply_mode"])
resolvedApplyMode, exists := rs.Primary.Attributes["resolved_apply_mode"]
switch {
case !exists:
t.Logf("[%s] resolved_apply_mode = ", stepName)
case resolvedApplyMode == "":
t.Logf("[%s] resolved_apply_mode = ", stepName)
default:
t.Logf("[%s] resolved_apply_mode = %q", stepName, resolvedApplyMode)
}
return nil
}
}
// TestAccTalosMachineConfigurationApplyResourceUpgradeWithResolvedApplyModeBug tests the bug in v0.10.1.
//
// Bug scenario: v0.10.0 → v0.10.1
// - v0.10.0: staged_if_needing_reboot and resolved_apply_mode don't exist.
// - v0.10.1: add staged_if_needing_reboot, resolved_apply_mode appears but is EMPTY (this is the bug).
func TestAccTalosMachineConfigurationApplyResourceUpgradeWithResolvedApplyModeBug(t *testing.T) {
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
Steps: []resource.TestStep{
// Step 1: v0.10.0 - staged_if_needing_reboot doesn't exist, use default apply_mode
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.10.0",
Source: "siderolabs/talos",
},
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
Config: testAccTalosMachineConfigurationApplyResourceConfigAutoStagedUpgrade(rName, "auto"),
Check: resource.ComposeAggregateTestCheckFunc(
logApplyModeState(t, "v0.10.0 - baseline"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "apply_mode", "auto"),
),
},
// Step 2: v0.10.1 - switch to staged_if_needing_reboot, resolved_apply_mode is EMPTY (bug)
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.10.1",
Source: "siderolabs/talos",
},
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
Config: testAccTalosMachineConfigurationApplyResourceConfigAutoStagedUpgrade(rName, "staged_if_needing_reboot"),
Check: resource.ComposeAggregateTestCheckFunc(
logApplyModeState(t, "v0.10.1 - BUG: resolved_apply_mode is empty"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "apply_mode", "staged_if_needing_reboot"),
// Bug: resolved_apply_mode is empty here because config didn't change
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "resolved_apply_mode", ""),
),
},
},
})
}
// TestAccTalosMachineConfigurationApplyResourceUpgradeWithResolvedApplyModeFix tests the fix for empty resolved_apply_mode.
//
// Fix scenario: v0.10.0 → current version
// - v0.10.0: staged_if_needing_reboot and resolved_apply_mode don't exist.
// - Current version: resolved_apply_mode is correctly computed (not empty).
func TestAccTalosMachineConfigurationApplyResourceUpgradeWithResolvedApplyModeFix(t *testing.T) {
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
Steps: []resource.TestStep{
// Step 1: v0.10.0 - staged_if_needing_reboot doesn't exist, use default apply_mode
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.10.0",
Source: "siderolabs/talos",
},
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
Config: testAccTalosMachineConfigurationApplyResourceConfigAutoStagedUpgrade(rName, "auto"),
Check: resource.ComposeAggregateTestCheckFunc(
logApplyModeState(t, "v0.10.0 - baseline"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "apply_mode", "auto"),
),
},
// Step 2: Current version - switch to staged_if_needing_reboot, resolved_apply_mode is correctly computed
{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Config: testAccTalosMachineConfigurationApplyResourceConfigAutoStagedUpgrade(rName, "staged_if_needing_reboot"),
Check: resource.ComposeAggregateTestCheckFunc(
logApplyModeState(t, "current version - FIX: resolved_apply_mode is computed"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.staged_if_needing_reboot", "apply_mode", "staged_if_needing_reboot"),
// Fix: resolved_apply_mode should now be computed (not empty)
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.staged_if_needing_reboot", "resolved_apply_mode"),
),
},
},
})
}
func TestAccTalosMachineConfigurationApplyResourceUpgrade(t *testing.T) {
// ref: https://github.com/hashicorp/terraform-plugin-testing/pull/118
t.Skip("skipping until TF test framework has a way to remove state resource")
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
Steps: []resource.TestStep{
// create TF config with v0.1.2 of the talos provider
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.1.2",
Source: "siderolabs/talos",
},
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
Config: testAccTalosMachineConfigurationApplyResourceConfigV0("talosv1", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckNoResourceAttr("talos_client_configuration", "this"),
resource.TestCheckNoResourceAttr("talos_machine_configuration_controlplane", "this"),
),
},
// now test state migration with the latest version of the provider
{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Config: testAccTalosMachineConfigurationApplyResourceConfigV1("talos", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "id", "machine_configuration_apply"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "apply_mode", "auto"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "node"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "endpoint"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "client_configuration.client_key"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "machine_configuration_input"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "machine_configuration"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "config_patches.#", "1"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "config_patches.0", "\"machine\":\n \"install\":\n \"disk\": \"/dev/vda\"\n"),
),
},
// ensure there is no diff
{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Config: testAccTalosMachineConfigurationApplyResourceConfigV1("talos", rName),
PlanOnly: true,
},
},
})
}
func testAccTalosMachineConfigurationApplyResourceConfig(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: false,
}
return config.render()
}
func testAccTalosMachineConfigurationApplyResourceConfigV0(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: false,
}
return config.render()
}
func testAccTalosMachineConfigurationApplyResourceConfigV1(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: false,
}
return config.render()
}
func testAccTalosMachineConfigurationApplyResourceConfigWithAutoStaged(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: true,
WithBootstrap: true,
WithRetrieveKubeConfig: true,
WithClusterHealth: true,
}
baseConfig := config.render()
return baseConfig + `
resource "talos_machine_configuration_apply" "staged_if_needing_reboot" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = libvirt_domain.cp.network_interface[0].addresses[0]
apply_mode = "staged_if_needing_reboot"
config_patches = [
yamlencode({
machine = {
files = [
{
path = "/var/etc/example-config.yaml"
permissions = 420 # 0644 in octal
op = "create"
content = "example: staged_if_needing_reboot test"
}
]
}
}),
]
depends_on = [data.talos_cluster_health.this]
}
`
}
// TestAccTalosMachineConfigurationApplyWithEphemeralClientConfigWO tests write-only attributes
// with ephemeral resources.
//
// This test uses ephemeral talos_machine_secrets and talos_machine_configuration WITHOUT
// persistence (not recommended for production - see docs/guides/using_ephemeral_resources.md).
// Secrets regenerate on each Open, so the rendered machine configuration — and therefore
// machine_configuration_hash — differs between plans. ExpectNonEmptyPlan is true to reflect
// this documented anti-pattern; production usage should persist secrets in a secret manager,
// which keeps the hash stable across runs.
//
// The test validates:
// - Write-only attributes work correctly with ephemeral inputs
// - Resource creation succeeds with ephemeral values
// - Write-only attributes are not stored in state
// - machine_configuration_hash IS populated in state (hash fingerprint, not a secret)
// - Hash drift surfaces when non-persisted ephemeral secrets regenerate (correct behavior
// that was previously hidden by the write-only invisibility to state)
func TestAccTalosMachineConfigurationApplyWithEphemeralClientConfigWO(t *testing.T) {
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_11_0),
},
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosMachineConfigurationApplyWithEphemeralClientConfigWOConfig(rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "id", "machine_configuration_apply"),
resource.TestCheckResourceAttr("talos_machine_configuration_apply.this", "apply_mode", "auto"),
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "node"),
// machine_configuration should NOT be in state when using write-only inputs
resource.TestCheckNoResourceAttr("talos_machine_configuration_apply.this", "machine_configuration"),
// machine_configuration_hash IS in state — it's a SHA256 fingerprint, not a secret
resource.TestCheckResourceAttrSet("talos_machine_configuration_apply.this", "machine_configuration_hash"),
// client_configuration_wo should not be in state (write-only)
resource.TestCheckNoResourceAttr("talos_machine_configuration_apply.this", "client_configuration_wo"),
// machine_configuration_input_wo should not be in state (write-only)
resource.TestCheckNoResourceAttr("talos_machine_configuration_apply.this", "machine_configuration_input_wo"),
// client_configuration should not be set (using WO variant)
resource.TestCheckNoResourceAttr("talos_machine_configuration_apply.this", "client_configuration"),
// machine_configuration_input should not be set (using WO variant)
resource.TestCheckNoResourceAttr("talos_machine_configuration_apply.this", "machine_configuration_input"),
),
// Drift on non-persisted ephemeral secrets: each Open regenerates secrets,
// which changes the rendered machine configuration, which changes the hash.
// This is the correct behavior for this anti-pattern; persist secrets in
// production and the hash stays stable.
ExpectNonEmptyPlan: true,
},
},
})
}
func testAccTalosMachineConfigurationApplyResourceConfigAutoStagedUpgrade(rName, applyMode string) string {
config := dynamicConfig{
Provider: "talos",
ResourceName: rName,
WithApplyConfig: false,
WithBootstrap: false,
}
baseConfig := config.render()
return baseConfig + `
resource "talos_machine_configuration_apply" "staged_if_needing_reboot" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = libvirt_domain.cp.network_interface[0].addresses[0]
apply_mode = "` + applyMode + `"
}
`
}
func testAccTalosMachineConfigurationApplyWithEphemeralClientConfigWOConfig(rName string) string {
cpuMode := cpuModeHostPassthrough
if os.Getenv("CI") != "" {
cpuMode = cpuModeHostModel
}
isoURL := fmt.Sprintf("https://github.com/siderolabs/talos/releases/download/%s/metal-amd64.iso", gendata.VersionTag)
return fmt.Sprintf(`
# Generate ephemeral machine secrets (NOT persisted - causes expected drift)
# In production, these should be persisted in a secret manager as documented
ephemeral "talos_machine_secrets" "this" {}
# Generate ephemeral machine configuration
ephemeral "talos_machine_configuration" "this" {
cluster_name = "test-cluster"
cluster_endpoint = "https://${libvirt_domain.cp.network_interface[0].addresses[0]}:6443"
machine_type = "controlplane"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
talos_version = "%[3]s"
kubernetes_version = "1.32.2"
config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/vda"
}
}
})
]
}
# Create libvirt VM
resource "libvirt_volume" "cp" {
name = "%[1]s"
size = 6442450944
}
resource "libvirt_domain" "cp" {
name = "%[1]s"
firmware = "/usr/share/OVMF/OVMF_CODE.fd"
nvram {
file = "/var/lib/libvirt/qemu/nvram/%[1]s_VARS.fd"
template = "/usr/share/OVMF/OVMF_VARS_4M.fd"
}
lifecycle {
ignore_changes = [
cpu,
nvram,
disk["url"],
firmware,
]
}
cpu {
mode = "%[2]s"
}
console {
type = "pty"
target_port = "0"
}
graphics {
type = "vnc"
listen_type = "address"
}
disk {
url = "%[4]s"
}
disk {
volume_id = libvirt_volume.cp.id
}
boot_device {
dev = ["cdrom"]
}
network_interface {
network_name = "default"
wait_for_lease = true
}
vcpu = "2"
memory = "4096"
}
# Apply configuration using write-only ephemeral attributes
# This tests the actual use case: ephemeral inputs -> write-only attributes -> no secrets in state
resource "talos_machine_configuration_apply" "this" {
client_configuration_wo = ephemeral.talos_machine_secrets.this.client_configuration
machine_configuration_input_wo = ephemeral.talos_machine_configuration.this.machine_configuration
node = libvirt_domain.cp.network_interface[0].addresses[0]
endpoint = libvirt_domain.cp.network_interface[0].addresses[0]
}
`, rName, cpuMode, gendata.VersionTag, isoURL)
}
// TestAccTalosMachineConfigurationApplyConfigPatchesUnknownList is a regression test for
// a model-type bug: config_patches is declared as []types.String in the resource model,
// which cannot hold a whole-list-unknown value. This surfaces when config_patches is
// derived from a for-expression over an unknown value (e.g. a data source response body),
// producing:
//
// Received unknown value, however the target type cannot handle unknown values.
// Path: config_patches
// Target Type: []basetypes.StringValue
// Suggested Type: basetypes.ListValue
//
// The test uses terraform_data (a built-in resource) whose output attribute is unknown
// until apply; feeding it through jsondecode + for produces a whole-list-unknown
// config_patches during plan, which reproduces the failure without requiring libvirt.
func TestAccTalosMachineConfigurationApplyConfigPatchesUnknownList(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_11_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccTalosMachineConfigurationApplyConfigPatchesUnknownListConfig(),
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
},
})
}
func testAccTalosMachineConfigurationApplyConfigPatchesUnknownListConfig() string {
return `
resource "terraform_data" "source" {
input = "[\"a\",\"b\"]"
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = {
ca_certificate = "fake-ca"
client_certificate = "fake-cert"
client_key = "fake-key"
}
machine_configuration_input = "version: v1alpha1\nmachine:\n type: controlplane\n"
node = "127.0.0.1"
endpoint = "127.0.0.1"
config_patches = [
for item in jsondecode(terraform_data.source.output) : yamlencode({
machine = { install = { disk = "/dev/${item}" } }
})
]
}
`
}
================================================
FILE: pkg/talos/talos_machine_configuration_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/crypto/x509"
"github.com/siderolabs/talos/pkg/machinery/config/configpatcher"
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"golang.org/x/mod/semver"
)
type talosMachineConfigurationDataSourceModelV0 struct {
ID types.String `tfsdk:"id"`
ClusterName types.String `tfsdk:"cluster_name"`
ClusterEndpoint types.String `tfsdk:"cluster_endpoint"`
MachineType types.String `tfsdk:"machine_type"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
TalosVersion types.String `tfsdk:"talos_version"`
MachineSecrets machineSecrets `tfsdk:"machine_secrets"`
MachineConfiguration types.String `tfsdk:"machine_configuration"`
ConfigPatches types.List `tfsdk:"config_patches"`
Docs types.Bool `tfsdk:"docs"`
Examples types.Bool `tfsdk:"examples"`
}
type talosMachineConfigurationDataSource struct{}
var (
_ datasource.DataSource = &talosMachineConfigurationDataSource{}
_ datasource.DataSourceWithValidateConfig = &talosMachineConfigurationDataSource{}
)
// NewTalosMachineConfigurationDataSource implements the datasource.DataSource interface.
func NewTalosMachineConfigurationDataSource() datasource.DataSource {
return &talosMachineConfigurationDataSource{}
}
func (d *talosMachineConfigurationDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_machine_configuration"
}
func (d *talosMachineConfigurationDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generate a machine configuration for a node type",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"cluster_name": schema.StringAttribute{
Required: true,
Description: "The name of the talos kubernetes cluster",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"cluster_endpoint": schema.StringAttribute{
Required: true,
Description: "The endpoint of the talos kubernetes cluster",
},
"machine_secrets": machineSecretsSchemaAttribute(),
"machine_type": schema.StringAttribute{
Required: true,
Description: "The type of machine to generate the configuration for",
Validators: []validator.String{
stringvalidator.OneOf("controlplane", "worker"),
},
},
"config_patches": schema.ListAttribute{
Description: "The list of config patches to apply to the generated configuration",
Optional: true,
ElementType: types.StringType,
},
"kubernetes_version": schema.StringAttribute{
Description: "The version of kubernetes to use",
Optional: true,
},
"talos_version": schema.StringAttribute{
Description: "The Talos version contract used to generate the machine configuration. This does not control the installed Talos version. Use `config_patches` to set `machine.install.image` to the desired value. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`", // nolint:lll
Optional: true,
Validators: []validator.String{
talosVersionValid(),
},
},
"docs": schema.BoolAttribute{
Description: "Whether to generate documentation for the generated configuration. Defaults to false",
Optional: true,
},
"examples": schema.BoolAttribute{
Description: "Whether to generate examples for the generated configuration. Defaults to false",
Optional: true,
},
"machine_configuration": schema.StringAttribute{
Description: "The generated machine configuration",
Computed: true,
Sensitive: true,
},
},
}
}
func (d *talosMachineConfigurationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state talosMachineConfigurationDataSourceModelV0
diags := req.Config.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if !state.KubernetesVersion.IsUnknown() && state.KubernetesVersion.IsNull() {
state.KubernetesVersion = basetypes.NewStringValue(constants.DefaultKubernetesVersion)
}
if !state.TalosVersion.IsUnknown() && state.TalosVersion.IsNull() {
state.TalosVersion = basetypes.NewStringValue(semver.MajorMinor(gendata.VersionTag))
}
var machineType machine.Type
switch state.MachineType.ValueString() {
case "controlplane":
machineType = machine.TypeControlPlane
case "worker":
machineType = machine.TypeWorker
}
machineSecrets := &secrets.Bundle{
Clock: secrets.NewFixedClock(time.Now()),
Cluster: &secrets.Cluster{
ID: state.MachineSecrets.Cluster.ID.ValueString(),
Secret: state.MachineSecrets.Cluster.Secret.ValueString(),
},
Secrets: &secrets.Secrets{
BootstrapToken: state.MachineSecrets.Secrets.BootstrapToken.ValueString(),
SecretboxEncryptionSecret: state.MachineSecrets.Secrets.SecretboxEncryptionSecret.ValueString(),
},
TrustdInfo: &secrets.TrustdInfo{
Token: state.MachineSecrets.TrustdInfo.Token.ValueString(),
},
}
if !state.MachineSecrets.Secrets.AESCBCEncryptionSecret.IsNull() {
machineSecrets.Secrets.AESCBCEncryptionSecret = state.MachineSecrets.Secrets.AESCBCEncryptionSecret.ValueString()
}
machineSecretsCerts, err := machineSecretsCertsToSecretsBundleCerts(state.MachineSecrets.Certs)
if err != nil {
resp.Diagnostics.AddError(
"failed to convert machine secrets certs to secrets bundle certs",
err.Error(),
)
return
}
machineSecrets.Certs = machineSecretsCerts
var configPatches []string
resp.Diagnostics.Append(state.ConfigPatches.ElementsAs(ctx, &configPatches, true)...)
if resp.Diagnostics.HasError() {
return
}
genOptions := &machineConfigGenerateOptions{
machineType: machineType,
clusterName: state.ClusterName.ValueString(),
clusterEndpoint: state.ClusterEndpoint.ValueString(),
machineSecrets: machineSecrets,
configPatches: configPatches,
kubernetesVersion: state.KubernetesVersion.ValueString(),
talosVersion: state.TalosVersion.ValueString(),
docsEnabled: state.Docs.ValueBool(),
examplesEnabled: state.Examples.ValueBool(),
}
machineConfiguration, err := genOptions.generate()
if err != nil {
resp.Diagnostics.AddError(
"failed to generate machine configuration",
err.Error(),
)
return
}
state.MachineConfiguration = basetypes.NewStringValue(machineConfiguration)
state.ID = state.ClusterName
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (d *talosMachineConfigurationDataSource) ValidateConfig(ctx context.Context, req datasource.ValidateConfigRequest, resp *datasource.ValidateConfigResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var state talosMachineConfigurationDataSourceModelV0
diags = obj.As(ctx, &state, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
validateMachineConfigurationConfig(ctx, state.ClusterEndpoint, state.ConfigPatches, &resp.Diagnostics)
}
func validateMachineConfigurationConfig(ctx context.Context, clusterEndpoint types.String, configPatches types.List, diagnostics *diag.Diagnostics) {
if !clusterEndpoint.IsUnknown() && !clusterEndpoint.IsNull() {
if err := validateClusterEndpoint(clusterEndpoint.ValueString()); err != nil {
diagnostics.AddError(
"cluster_endpoint is invalid",
err.Error(),
)
}
}
var patches []string
loadConfigPatches := true
for _, el := range configPatches.Elements() {
if el.IsUnknown() || el.IsNull() {
loadConfigPatches = false
break
}
}
if loadConfigPatches {
diagnostics.Append(configPatches.ElementsAs(ctx, &patches, true)...)
if diagnostics.HasError() {
return
}
if _, err := configpatcher.LoadPatches(patches); err != nil {
diagnostics.AddError(
"config_patches are invalid",
err.Error(),
)
}
}
}
func machineSecretsSchemaAttribute() schema.SingleNestedAttribute {
return schema.SingleNestedAttribute{
Description: "The secrets for the talos cluster",
Attributes: map[string]schema.Attribute{
"cluster": schema.SingleNestedAttribute{
Description: "The cluster secrets",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Required: true,
Description: "The cluster id",
},
"secret": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The cluster secret",
},
},
Required: true,
},
"secrets": schema.SingleNestedAttribute{
Description: "The secrets for the talos kubernetes cluster",
Attributes: map[string]schema.Attribute{
"bootstrap_token": schema.StringAttribute{
Description: "The bootstrap token for the talos kubernetes cluster",
Required: true,
Sensitive: true,
},
"secretbox_encryption_secret": schema.StringAttribute{
Description: "The secretbox encryption secret for the talos kubernetes cluster",
Required: true,
Sensitive: true,
},
"aescbc_encryption_secret": schema.StringAttribute{
Description: "The aescbc encryption secret for the talos kubernetes cluster",
Optional: true,
Sensitive: true,
},
},
Required: true,
},
"trustdinfo": schema.SingleNestedAttribute{
Description: "The trustd info for the talos kubernetes cluster",
Attributes: map[string]schema.Attribute{
"token": schema.StringAttribute{
Description: "The trustd token for the talos kubernetes cluster",
Required: true,
Sensitive: true,
},
},
Required: true,
},
"certs": schema.SingleNestedAttribute{
Description: "The certs for the talos kubernetes cluster",
Attributes: map[string]schema.Attribute{
"etcd": certSchemaInput(),
"k8s": certSchemaInput(),
"k8s_aggregator": certSchemaInput(),
"k8s_serviceaccount": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"key": schema.StringAttribute{
Description: "The key for the k8s service account",
Required: true,
Sensitive: true,
},
},
Required: true,
},
"os": certSchemaInput(),
},
Required: true,
},
},
Required: true,
}
}
func certSchemaInput() schema.SingleNestedAttribute {
return schema.SingleNestedAttribute{
Description: "The certificate and key pair",
Attributes: map[string]schema.Attribute{
"cert": schema.StringAttribute{
Description: "certificate data",
Required: true,
},
"key": schema.StringAttribute{
Description: "key data",
Required: true,
Sensitive: true,
},
},
Required: true,
}
}
func machineSecretsCertsToSecretsBundleCerts(machineSecretsCerts machineSecretsCerts) (*secrets.Certs, error) {
etcdCertDataX509, err := certDataToX509PEMEncodedCertificateAndKey(machineSecretsCerts.Etcd.Cert.ValueString(), machineSecretsCerts.Etcd.Key.ValueString())
if err != nil {
return nil, err
}
k8sCertDataX509, err := certDataToX509PEMEncodedCertificateAndKey(machineSecretsCerts.K8s.Cert.ValueString(), machineSecretsCerts.K8s.Key.ValueString())
if err != nil {
return nil, err
}
k8sAggregatorCertDataX509, err := certDataToX509PEMEncodedCertificateAndKey(machineSecretsCerts.K8sAggregator.Cert.ValueString(), machineSecretsCerts.K8sAggregator.Key.ValueString())
if err != nil {
return nil, err
}
k8sServiceAccountCertDataX509, err := certDataToX509PEMEncodedKey(machineSecretsCerts.K8sServiceAccount.Key.ValueString())
if err != nil {
return nil, err
}
osCertDataX509, err := certDataToX509PEMEncodedCertificateAndKey(machineSecretsCerts.OS.Cert.ValueString(), machineSecretsCerts.OS.Key.ValueString())
if err != nil {
return nil, err
}
return &secrets.Certs{
Etcd: etcdCertDataX509,
K8s: k8sCertDataX509,
K8sAggregator: k8sAggregatorCertDataX509,
K8sServiceAccount: k8sServiceAccountCertDataX509,
OS: osCertDataX509,
}, nil
}
func certDataToX509PEMEncodedCertificateAndKey(cert, key string) (*x509.PEMEncodedCertificateAndKey, error) {
certBytes, err := base64ToBytes(cert)
if err != nil {
return nil, err
}
keyBytes, err := base64ToBytes(key)
if err != nil {
return nil, err
}
return &x509.PEMEncodedCertificateAndKey{
Key: keyBytes,
Crt: certBytes,
}, nil
}
func certDataToX509PEMEncodedKey(key string) (*x509.PEMEncodedKey, error) {
keyBytes, err := base64ToBytes(key)
if err != nil {
return nil, err
}
return &x509.PEMEncodedKey{
Key: keyBytes,
}, nil
}
================================================
FILE: pkg/talos/talos_machine_configuration_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"fmt"
"net/url"
"regexp"
"strings"
"testing"
"text/template"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"github.com/stretchr/testify/assert"
"go.yaml.in/yaml/v4"
"golang.org/x/mod/semver"
"github.com/siderolabs/terraform-provider-talos/pkg/talos"
)
func TestAccTalosMachineConfigurationDataSource(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test data source with default values
{
Config: testAccTalosMachineConfigurationDataSourceConfig("", "example-cluster", "controlplane", "https://cluster.local:6443", "", false, false, true, true),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "id", "example-cluster"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_name", "example-cluster"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_endpoint", "https://cluster.local:6443"),
resource.TestCheckResourceAttrSet("data.talos_machine_configuration.this", "machine_secrets.%"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "machine_type", "controlplane"),
resource.TestCheckNoResourceAttr("data.talos_machine_configuration.this", "config_patches"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "kubernetes_version", constants.DefaultKubernetesVersion),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "talos_version", semver.MajorMinor(gendata.VersionTag)),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "docs", "true"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "examples", "true"),
resource.TestCheckResourceAttrWith("data.talos_machine_configuration.this", "machine_configuration", func(value string) error {
return validateGeneratedTalosMachineConfig(
t,
"example-cluster",
"https://cluster.local:6443",
"/dev/sda",
constants.DefaultKubernetesVersion,
"controlplane",
value,
true,
true,
func(t *testing.T, config v1alpha1.Config) error {
assert.Empty(t, config.Cluster().AESCBCEncryptionSecret())
assert.NotEmpty(t, config.Cluster().SecretboxEncryptionSecret())
return nil
},
)
}),
),
},
// test data source with custom values
{
Config: testAccTalosMachineConfigurationDataSourceConfig("", "example-cluster-1", "controlplane", "https://cluster-1.local:6443", "v1.28.0", true, false, false, false),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "id", "example-cluster-1"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_name", "example-cluster-1"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_endpoint", "https://cluster-1.local:6443"),
resource.TestCheckResourceAttrSet("data.talos_machine_configuration.this", "machine_secrets.%"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "machine_type", "controlplane"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "config_patches.#", "3"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "config_patches.0", "\"machine\":\n \"install\":\n \"disk\": \"/dev/sdd\"\n"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "kubernetes_version", "v1.28.0"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "talos_version", semver.MajorMinor(gendata.VersionTag)),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "docs", "false"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "examples", "false"),
resource.TestCheckResourceAttrWith("data.talos_machine_configuration.this", "machine_configuration", func(value string) error {
return validateGeneratedTalosMachineConfig(
t,
"example-cluster-1",
"https://cluster-1.local:6443",
"/dev/sdd",
"1.28.0",
"controlplane",
value,
false,
false,
func(t *testing.T, config v1alpha1.Config) error {
assert.Equal(t, map[string]string{"foo": "bar"}, config.Machine().Sysfs())
assert.Equal(t, map[string][]string{"foo": {"bar"}}, config.Cluster().APIServer().ExtraArgs())
assert.Equal(t, "cp-test", config.Hostname())
assert.Empty(t, config.Cluster().AESCBCEncryptionSecret())
assert.NotEmpty(t, config.Cluster().SecretboxEncryptionSecret())
return nil
},
)
}),
),
},
// test data source for a worker node
{
Config: testAccTalosMachineConfigurationDataSourceConfig("", "example-cluster-2", "worker", "https://cluster-2.local:6443", "", false, false, true, false),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "id", "example-cluster-2"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_name", "example-cluster-2"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_endpoint", "https://cluster-2.local:6443"),
resource.TestCheckResourceAttrSet("data.talos_machine_configuration.this", "machine_secrets.%"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "machine_type", "worker"),
resource.TestCheckNoResourceAttr("data.talos_machine_configuration.this", "config_patches"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "kubernetes_version", constants.DefaultKubernetesVersion),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "talos_version", semver.MajorMinor(gendata.VersionTag)),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "docs", "true"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "examples", "false"),
resource.TestCheckResourceAttrWith("data.talos_machine_configuration.this", "machine_configuration", func(value string) error {
return validateGeneratedTalosMachineConfig(
t,
"example-cluster-2",
"https://cluster-2.local:6443",
"/dev/sda",
constants.DefaultKubernetesVersion,
"worker",
value,
true,
false,
nil,
)
}),
),
},
// test data source for talos v1.2 that has aescbc encryption
{
Config: testAccTalosMachineConfigurationDataSourceConfig("v1.2", "example-cluster-3", "controlplane", "https://cluster-3.local:6443", "", false, false, false, true),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "id", "example-cluster-3"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_name", "example-cluster-3"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "cluster_endpoint", "https://cluster-3.local:6443"),
resource.TestCheckResourceAttrSet("data.talos_machine_configuration.this", "machine_secrets.%"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "machine_type", "controlplane"),
resource.TestCheckNoResourceAttr("data.talos_machine_configuration.this", "config_patches"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "kubernetes_version", constants.DefaultKubernetesVersion),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "talos_version", "v1.2"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "docs", "false"),
resource.TestCheckResourceAttr("data.talos_machine_configuration.this", "examples", "true"),
resource.TestCheckResourceAttrWith("data.talos_machine_configuration.this", "machine_configuration", func(value string) error {
return validateGeneratedTalosMachineConfig(
t,
"example-cluster-3",
"https://cluster-3.local:6443",
"/dev/sda",
constants.DefaultKubernetesVersion,
"controlplane",
value,
false,
true,
func(t *testing.T, config v1alpha1.Config) error {
assert.NotEmpty(t, config.Cluster().AESCBCEncryptionSecret())
assert.Empty(t, config.Cluster().SecretboxEncryptionSecret())
return nil
},
)
}),
),
},
},
})
resource.Test(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test validating cluster endpoint
{
Config: testAccTalosMachineConfigurationDataSourceConfig("", "example-cluster-4", "controlplane", "cluster.local", "", false, false, true, true),
ExpectError: regexp.MustCompile("no scheme and port specified for the cluster endpoint URL\ntry: \"https://cluster.local:6443\""),
},
// test validating talos machine config features version
{
Config: testAccTalosMachineConfigurationDataSourceConfig("nil", "example-cluster-5", "controlplane", "https://cluster.local", "", false, false, true, true),
ExpectError: regexp.MustCompile("error parsing version \"vnil\""),
},
// test validating machine type
{
Config: testAccTalosMachineConfigurationDataSourceConfig("", "example-cluster-6", "control", "https://cluster.local", "", false, false, true, true),
ExpectError: regexp.MustCompile("Attribute machine_type value must be one of:"),
},
// test validating config patches at plan time
{
PlanOnly: true,
Config: testAccTalosMachineConfigurationDataSourceConfig("v1.3", "example-cluster-8", "controlplane", "https://cluster.local", "v1.23.0", true, true, true, true),
ExpectError: regexp.MustCompile(`error decoding document /v1alpha1/ \(line 1\): unknown keys found during`),
},
},
})
}
func testAccTalosMachineConfigurationDataSourceConfig(
talosConfigVersion,
clusterName,
machineType,
clusterEndpoint,
kubernetesVersion string,
configPatches,
invalidPatch,
docsEnabled,
examplesEnabled bool,
) string {
type templateConfigModel struct {
TalosVersion string
ClusterName string
ClusterEndpoint string
MachineType string
KubernetesVersion string
ConfigPatches bool
InvalidPatch bool
DocsEnabled bool
ExamplesEnabled bool
}
templateConfig := templateConfigModel{
TalosVersion: talosConfigVersion,
ClusterName: clusterName,
ClusterEndpoint: clusterEndpoint,
MachineType: machineType,
ConfigPatches: configPatches,
InvalidPatch: invalidPatch,
DocsEnabled: docsEnabled,
ExamplesEnabled: examplesEnabled,
KubernetesVersion: kubernetesVersion,
}
configTemplate := `
variable "talos_version" {
type = string
default = "{{ .TalosVersion }}"
}
resource "talos_machine_secrets" "this" {
{{ if .TalosVersion }}talos_version = var.talos_version{{ end }}
}
data "talos_machine_configuration" "this" {
cluster_name = "{{ .ClusterName }}"
cluster_endpoint = "{{ .ClusterEndpoint }}"
machine_type = "{{ .MachineType }}"
machine_secrets = talos_machine_secrets.this.machine_secrets
{{ if .TalosVersion }}talos_version = var.talos_version{{ end }}
{{ if .ConfigPatches }}config_patches = [
yamlencode({
machine = {
install = {
disk = "/dev/sdd"
}
}
}),
file("${path.module}/testdata/patch-strategic.yaml"),
{{ if .InvalidPatch }}file("${path.module}/testdata/patch-invalid.yaml"),{{ end }}
yamlencode({
machine = {
network = {
hostname = "cp-test"
}
}
})
]{{ end }}
docs = {{ .DocsEnabled }}
examples = {{ .ExamplesEnabled }}
{{ if .KubernetesVersion }}kubernetes_version = "{{ .KubernetesVersion }}"{{ end }}
}
`
var config strings.Builder
template.Must(template.New("tf_config").Parse(configTemplate)).Execute(&config, templateConfig) //nolint:errcheck
return config.String()
}
func validateGeneratedTalosMachineConfig(
t *testing.T,
clusterName,
endpoint,
installDisk,
k8sVersion,
machineType,
mc string,
docs,
examples bool,
extraChecks func(t *testing.T, config v1alpha1.Config) error,
) error {
var machineConfig v1alpha1.Config
if err := yaml.Unmarshal([]byte(mc), &machineConfig); err != nil {
return err
}
installDiskConfig := machineConfig.Machine().Install().Disk()
ep, err := url.Parse(endpoint)
if err != nil {
return err
}
switch machineType {
case "controlplane":
assert.Equal(t, machine.TypeControlPlane, machineConfig.Machine().Type())
assert.Equal(t, clusterName, machineConfig.Cluster().Name())
case "worker":
assert.Equal(t, machine.TypeWorker, machineConfig.Machine().Type())
}
assert.Equal(t, ep, machineConfig.Cluster().Endpoint())
assert.Equal(t, constants.DefaultDNSDomain, machineConfig.Cluster().Network().DNSDomain())
assert.Equal(t, installDisk, installDiskConfig)
assert.Equal(t, talos.GenerateInstallerImage(), machineConfig.Machine().Install().Image())
assert.Equal(t, fmt.Sprintf("ghcr.io/siderolabs/kubelet:v%s", k8sVersion), machineConfig.Machine().Kubelet().Image())
assert.Equal(t, "v1alpha1", machineConfig.ConfigVersion)
assert.True(t, machineConfig.Cluster().Discovery().Enabled())
if docs {
assert.Equal(t, "Indicates the schema used to decode the contents.", machineConfig.Doc().Field(0).Description)
} else {
assert.NotContains(t, mc, "Indicates the schema used to decode the contents.")
}
if examples {
// verifying there's examples
assert.Contains(t, mc, (`
# # Uncomment this to enable SANs.
# - 10.0.0.10
# - 172.16.0.10
# - 192.168.0.10
`))
} else {
// verifying there's no examples
assert.NotContains(t, mc, (`
# # Uncomment this to enable SANs.
# - 10.0.0.10
# - 172.16.0.10
# - 192.168.0.10
`))
}
if extraChecks != nil {
return extraChecks(t, machineConfig)
}
return nil
}
================================================
FILE: pkg/talos/talos_machine_configuration_ephemeral_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"golang.org/x/mod/semver"
)
var (
_ ephemeral.EphemeralResource = &talosMachineConfigurationEphemeralResource{}
_ ephemeral.EphemeralResourceWithValidateConfig = &talosMachineConfigurationEphemeralResource{}
)
type talosMachineConfigurationEphemeralResource struct{}
type talosMachineConfigurationEphemeralResourceModel struct {
ClusterName types.String `tfsdk:"cluster_name"`
ClusterEndpoint types.String `tfsdk:"cluster_endpoint"`
MachineType types.String `tfsdk:"machine_type"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
TalosVersion types.String `tfsdk:"talos_version"`
MachineSecrets machineSecrets `tfsdk:"machine_secrets"`
MachineConfiguration types.String `tfsdk:"machine_configuration"`
ConfigPatches types.List `tfsdk:"config_patches"`
Docs types.Bool `tfsdk:"docs"`
Examples types.Bool `tfsdk:"examples"`
}
// NewTalosMachineConfigurationEphemeralResource implements the ephemeral.EphemeralResource interface.
func NewTalosMachineConfigurationEphemeralResource() ephemeral.EphemeralResource {
return &talosMachineConfigurationEphemeralResource{}
}
func (r *talosMachineConfigurationEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_machine_configuration"
}
func (r *talosMachineConfigurationEphemeralResource) Schema(_ context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generate a machine configuration for a node type. This is an ephemeral resource that does not persist secrets in Terraform state.",
Attributes: map[string]schema.Attribute{
"cluster_name": schema.StringAttribute{
Required: true,
Description: "The name of the talos kubernetes cluster",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"cluster_endpoint": schema.StringAttribute{
Required: true,
Description: "The endpoint of the talos kubernetes cluster",
},
"machine_secrets": machineSecretsSchemaAttribute(),
"machine_type": schema.StringAttribute{
Required: true,
Description: "The type of machine to generate the configuration for",
Validators: []validator.String{
stringvalidator.OneOf("controlplane", "worker"),
},
},
"config_patches": schema.ListAttribute{
Description: "The list of config patches to apply to the generated configuration",
Optional: true,
ElementType: types.StringType,
},
"kubernetes_version": schema.StringAttribute{
Description: "The version of kubernetes to use",
Optional: true,
Computed: true,
},
"talos_version": schema.StringAttribute{
Description: "The Talos version contract used to generate the machine configuration. This does not control the installed Talos version. Use `config_patches` to set `machine.install.image` to the desired value. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`", // nolint:lll
Optional: true,
Computed: true,
Validators: []validator.String{
talosVersionValid(),
},
},
"docs": schema.BoolAttribute{
Description: "Whether to generate documentation for the generated configuration. Defaults to false",
Optional: true,
},
"examples": schema.BoolAttribute{
Description: "Whether to generate examples for the generated configuration. Defaults to false",
Optional: true,
},
"machine_configuration": schema.StringAttribute{
Description: "The generated machine configuration",
Computed: true,
Sensitive: true,
},
},
}
}
func (r *talosMachineConfigurationEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosMachineConfigurationEphemeralResourceModel
diags = obj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
kubernetesVersion := config.KubernetesVersion.ValueString()
if kubernetesVersion == "" {
kubernetesVersion = constants.DefaultKubernetesVersion
}
talosVersion := config.TalosVersion.ValueString()
if talosVersion == "" {
talosVersion = semver.MajorMinor(gendata.VersionTag)
}
var machineType machine.Type
switch config.MachineType.ValueString() {
case "controlplane":
machineType = machine.TypeControlPlane
case "worker":
machineType = machine.TypeWorker
}
machineSecretsBundle := &secrets.Bundle{
Clock: secrets.NewFixedClock(time.Now()),
Cluster: &secrets.Cluster{
ID: config.MachineSecrets.Cluster.ID.ValueString(),
Secret: config.MachineSecrets.Cluster.Secret.ValueString(),
},
Secrets: &secrets.Secrets{
BootstrapToken: config.MachineSecrets.Secrets.BootstrapToken.ValueString(),
SecretboxEncryptionSecret: config.MachineSecrets.Secrets.SecretboxEncryptionSecret.ValueString(),
},
TrustdInfo: &secrets.TrustdInfo{
Token: config.MachineSecrets.TrustdInfo.Token.ValueString(),
},
}
if !config.MachineSecrets.Secrets.AESCBCEncryptionSecret.IsNull() {
machineSecretsBundle.Secrets.AESCBCEncryptionSecret = config.MachineSecrets.Secrets.AESCBCEncryptionSecret.ValueString()
}
machineSecretsCerts, err := machineSecretsCertsToSecretsBundleCerts(config.MachineSecrets.Certs)
if err != nil {
resp.Diagnostics.AddError(
"failed to convert machine secrets certs to secrets bundle certs",
err.Error(),
)
return
}
machineSecretsBundle.Certs = machineSecretsCerts
var configPatches []string
resp.Diagnostics.Append(config.ConfigPatches.ElementsAs(ctx, &configPatches, true)...)
if resp.Diagnostics.HasError() {
return
}
genOptions := &machineConfigGenerateOptions{
machineType: machineType,
clusterName: config.ClusterName.ValueString(),
clusterEndpoint: config.ClusterEndpoint.ValueString(),
machineSecrets: machineSecretsBundle,
configPatches: configPatches,
kubernetesVersion: kubernetesVersion,
talosVersion: talosVersion,
docsEnabled: config.Docs.ValueBool(),
examplesEnabled: config.Examples.ValueBool(),
}
machineConfiguration, err := genOptions.generate()
if err != nil {
resp.Diagnostics.AddError(
"failed to generate machine configuration",
err.Error(),
)
return
}
result := talosMachineConfigurationEphemeralResourceModel{
ClusterName: config.ClusterName,
ClusterEndpoint: config.ClusterEndpoint,
MachineType: config.MachineType,
KubernetesVersion: basetypes.NewStringValue(kubernetesVersion),
TalosVersion: basetypes.NewStringValue(talosVersion),
MachineSecrets: config.MachineSecrets,
MachineConfiguration: basetypes.NewStringValue(machineConfiguration),
ConfigPatches: config.ConfigPatches,
Docs: config.Docs,
Examples: config.Examples,
}
diags = resp.Result.Set(ctx, &result)
resp.Diagnostics.Append(diags...)
}
func (r *talosMachineConfigurationEphemeralResource) ValidateConfig(ctx context.Context, req ephemeral.ValidateConfigRequest, resp *ephemeral.ValidateConfigResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosMachineConfigurationEphemeralResourceModel
diags = obj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
validateMachineConfigurationConfig(ctx, config.ClusterEndpoint, config.ConfigPatches, &resp.Diagnostics)
}
================================================
FILE: pkg/talos/talos_machine_configuration_ephemeral_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
// TestAccTalosMachineConfigurationEphemeralResource tests that the ephemeral
// resource generates machine configuration from machine secrets.
func TestAccTalosMachineConfigurationEphemeralResource(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_machine_configuration" "this" {
cluster_name = "test-cluster"
cluster_endpoint = "https://10.0.0.1:6443"
machine_type = "controlplane"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
}
provider "echo" {
data = {
machine_configuration = ephemeral.talos_machine_configuration.this.machine_configuration
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("machine_configuration"), knownvalue.NotNull()),
},
},
},
})
}
// TestAccTalosMachineConfigurationEphemeralResourceWorker tests generating
// a worker machine configuration.
func TestAccTalosMachineConfigurationEphemeralResourceWorker(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
ephemeral "talos_machine_configuration" "this" {
cluster_name = "test-cluster"
cluster_endpoint = "https://10.0.0.1:6443"
machine_type = "worker"
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
}
provider "echo" {
data = {
machine_configuration = ephemeral.talos_machine_configuration.this.machine_configuration
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("machine_configuration"), knownvalue.NotNull()),
},
},
},
})
}
================================================
FILE: pkg/talos/talos_machine_disks_data_source.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"errors"
"time"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/siderolabs/talos/pkg/machinery/cel"
"github.com/siderolabs/talos/pkg/machinery/cel/celenv"
"github.com/siderolabs/talos/pkg/machinery/client"
"github.com/siderolabs/talos/pkg/machinery/config/types/block/blockhelpers"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
//go:generate go run internal/gen/diskspec.go block.DiskSpec talos_machine_disks_data_source
func (e nodiskFoundError) Error() string {
return "no disk matching the filter found"
}
type nodiskFoundError struct{}
type talosMachineDisksDataSource struct{}
type talosMachineDisksDataSourceModelV1 struct { //nolint:govet
ID types.String `tfsdk:"id"`
Node types.String `tfsdk:"node"`
Endpoint types.String `tfsdk:"endpoint"`
Selector types.String `tfsdk:"selector"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
Disks []diskspec `tfsdk:"disks"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
var _ datasource.DataSource = &talosMachineDisksDataSource{}
// NewTalosMachineDisksDataSource implements the datasource.DataSource interface.
func NewTalosMachineDisksDataSource() datasource.DataSource {
return &talosMachineDisksDataSource{}
}
func (d *talosMachineDisksDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_machine_disks"
}
func (d *talosMachineDisksDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generate a machine configuration for a node type",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The generated ID of this resource",
Computed: true,
},
"node": schema.StringAttribute{
Required: true,
Description: "controlplane node to retrieve the kubeconfig from",
},
"endpoint": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "endpoint to use for the talosclient. If not set, the node value will be used",
},
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Required: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Required: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Required: true,
Sensitive: true,
Description: "The client key",
},
},
Required: true,
Description: "The client configuration data",
},
"selector": schema.StringAttribute{
Optional: true,
MarkdownDescription: `The CEL expression to filter the disks.
If not set, all disks will be returned.
See [CEL documentation](https://www.talos.dev/latest/talos-guides/configuration/disk-management/#disk-selector).`,
},
"disks": schema.ListNestedAttribute{
Description: "The disks that match the filters",
NestedObject: schema.NestedAttributeObject{
Attributes: diskspecAttributes,
},
Computed: true,
},
"timeouts": timeouts.Attributes(ctx, timeouts.Opts{
Read: true,
}),
},
}
}
func (d *talosMachineDisksDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { //nolint:gocognit,gocyclo,cyclop
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var state talosMachineDisksDataSourceModelV1
diags = obj.As(ctx, &state, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
talosConfig, err := talosClientTFConfigToTalosClientConfig(
"dynamic",
state.ClientConfiguration.CA.ValueString(),
state.ClientConfiguration.Cert.ValueString(),
state.ClientConfiguration.Key.ValueString(),
)
if err != nil {
resp.Diagnostics.AddError("failed to generate talos config", err.Error())
return
}
if state.Endpoint.IsNull() {
state.Endpoint = state.Node
}
readTimeout, diags := state.Timeouts.Read(ctx, 10*time.Minute)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctxDeadline, cancel := context.WithTimeout(ctx, readTimeout)
defer cancel()
selector := state.Selector.ValueString()
if selector == "" {
// if there is no selector, we can return all disks
selector = "true"
}
exp, err := cel.ParseBooleanExpression(selector, celenv.DiskLocator())
if err != nil {
resp.Diagnostics.AddError("failed to parse celenv selector", err.Error())
return
}
if err := retry.RetryContext(ctxDeadline, readTimeout, func() *retry.RetryError {
if err := talosClientOp(ctx, state.Endpoint.ValueString(), state.Node.ValueString(), talosConfig, func(nodeCtx context.Context, c *client.Client) error {
disks, err := blockhelpers.MatchDisks(nodeCtx, c.COSI, &exp)
if err != nil {
return err
}
for _, disk := range disks {
state.Disks = append(state.Disks, diskspecToTFTypes(*disk.TypedSpec()))
}
return nil
}); err != nil {
if s := status.Code(err); s == codes.InvalidArgument {
return retry.NonRetryableError(err)
}
if errors.Is(err, nodiskFoundError{}) {
return retry.NonRetryableError(err)
}
return retry.RetryableError(err)
}
return nil
}); err != nil {
resp.Diagnostics.AddError("failed to get list of disks", err.Error())
return
}
state.ID = basetypes.NewStringValue("machine_disks")
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_machine_disks_data_source_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccTalosMachineDisksDataSource(t *testing.T) {
rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resource.ParallelTest(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{
"libvirt": {
Source: "dmacvicar/libvirt",
VersionConstraint: "= 0.8.3",
},
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test default config
{
Config: testAccTalosMachineDisksDataSourceConfigV0("talos", rName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.talos_machine_disks.this", "id", "machine_disks"),
resource.TestCheckResourceAttrSet("data.talos_machine_disks.this", "node"),
resource.TestCheckResourceAttrSet("data.talos_machine_disks.this", "endpoint"),
resource.TestCheckResourceAttrSet("data.talos_machine_disks.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("data.talos_machine_disks.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("data.talos_machine_disks.this", "client_configuration.client_key"),
resource.TestCheckResourceAttr("data.talos_machine_disks.this", "selector", "disk.size > 6u * GB"),
resource.TestCheckResourceAttr("data.talos_machine_disks.this", "disks.#", "1"),
resource.TestCheckResourceAttr("data.talos_machine_disks.this", "disks.0.dev_path", "/dev/vda"),
),
},
},
})
}
func testAccTalosMachineDisksDataSourceConfigV0(providerName, rName string) string {
config := dynamicConfig{
Provider: providerName,
ResourceName: rName,
WithApplyConfig: false,
WithBootstrap: false,
}
return config.render()
}
================================================
FILE: pkg/talos/talos_machine_disks_data_source_types.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Code generated by "diskspec.go"; DO NOT EDIT.
package talos
import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/talos/pkg/machinery/resources/block"
)
type diskspec struct {
DevPath types.String `tfsdk:"dev_path"`
Size types.Int64 `tfsdk:"size"`
PrettySize types.String `tfsdk:"pretty_size"`
IOSize types.Int64 `tfsdk:"io_size"`
SectorSize types.Int64 `tfsdk:"sector_size"`
Readonly types.Bool `tfsdk:"readonly"`
CDROM types.Bool `tfsdk:"cdrom"`
Model types.String `tfsdk:"model"`
Serial types.String `tfsdk:"serial"`
Modalias types.String `tfsdk:"modalias"`
WWID types.String `tfsdk:"wwid"`
UUID types.String `tfsdk:"uuid"`
BusPath types.String `tfsdk:"bus_path"`
SubSystem types.String `tfsdk:"sub_system"`
Transport types.String `tfsdk:"transport"`
Rotational types.Bool `tfsdk:"rotational"`
SecondaryDisks types.List `tfsdk:"secondary_disks"`
Symlinks types.List `tfsdk:"symlinks"`
}
var diskspecAttributes = map[string]schema.Attribute{
"dev_path": schema.StringAttribute{
Computed: true,
},
"size": schema.Int64Attribute{
Computed: true,
},
"pretty_size": schema.StringAttribute{
Computed: true,
},
"io_size": schema.Int64Attribute{
Computed: true,
},
"sector_size": schema.Int64Attribute{
Computed: true,
},
"readonly": schema.BoolAttribute{
Computed: true,
},
"cdrom": schema.BoolAttribute{
Computed: true,
},
"model": schema.StringAttribute{
Computed: true,
},
"serial": schema.StringAttribute{
Computed: true,
},
"modalias": schema.StringAttribute{
Computed: true,
},
"wwid": schema.StringAttribute{
Computed: true,
},
"uuid": schema.StringAttribute{
Computed: true,
},
"bus_path": schema.StringAttribute{
Computed: true,
},
"sub_system": schema.StringAttribute{
Computed: true,
},
"transport": schema.StringAttribute{
Computed: true,
},
"rotational": schema.BoolAttribute{
Computed: true,
},
"secondary_disks": schema.ListAttribute{
ElementType: types.StringType,
Computed: true,
},
"symlinks": schema.ListAttribute{
ElementType: types.StringType,
Computed: true,
},
}
func diskspecToTFTypes(diskspecSpec block.DiskSpec) diskspec {
return diskspec{
DevPath: types.StringValue(diskspecSpec.DevPath),
Size: types.Int64Value(int64(diskspecSpec.Size)),
PrettySize: types.StringValue(diskspecSpec.PrettySize),
IOSize: types.Int64Value(int64(diskspecSpec.IOSize)),
SectorSize: types.Int64Value(int64(diskspecSpec.SectorSize)),
Readonly: types.BoolValue(diskspecSpec.Readonly),
CDROM: types.BoolValue(diskspecSpec.CDROM),
Model: types.StringValue(diskspecSpec.Model),
Serial: types.StringValue(diskspecSpec.Serial),
Modalias: types.StringValue(diskspecSpec.Modalias),
WWID: types.StringValue(diskspecSpec.WWID),
UUID: types.StringValue(diskspecSpec.UUID),
BusPath: types.StringValue(diskspecSpec.BusPath),
SubSystem: types.StringValue(diskspecSpec.SubSystem),
Transport: types.StringValue(diskspecSpec.Transport),
Rotational: types.BoolValue(diskspecSpec.Rotational),
SecondaryDisks: types.ListValueMust(types.StringType, xslices.Map(diskspecSpec.SecondaryDisks, func(s string) attr.Value {
return types.StringValue(s)
})),
Symlinks: types.ListValueMust(types.StringType, xslices.Map(diskspecSpec.Symlinks, func(s string) attr.Value {
return types.StringValue(s)
})),
}
}
================================================
FILE: pkg/talos/talos_machine_secrets_ephemeral_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"time"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"golang.org/x/mod/semver"
)
var _ ephemeral.EphemeralResource = &talosMachineSecretsEphemeralResource{}
type talosMachineSecretsEphemeralResource struct{}
type talosMachineSecretsEphemeralResourceModel struct {
TalosVersion types.String `tfsdk:"talos_version"`
MachineSecrets machineSecrets `tfsdk:"machine_secrets"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
}
// NewTalosMachineSecretsEphemeralResource implements the ephemeral.EphemeralResource interface.
func NewTalosMachineSecretsEphemeralResource() ephemeral.EphemeralResource {
return &talosMachineSecretsEphemeralResource{}
}
func (r *talosMachineSecretsEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_machine_secrets"
}
func (r *talosMachineSecretsEphemeralResource) Schema(_ context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Generate machine secrets for Talos cluster. This is an ephemeral resource that does not persist secrets in Terraform state.",
Attributes: map[string]schema.Attribute{
"talos_version": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The Talos version contract used to generate the secrets. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`",
Validators: []validator.String{
talosVersionValid(),
},
},
"machine_secrets": machineSecretsOutputSchemaAttribute(),
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Computed: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Computed: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Computed: true,
Sensitive: true,
Description: "The client key",
},
},
Computed: true,
Description: "The generated client configuration data",
},
},
}
}
func (r *talosMachineSecretsEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var obj types.Object
diags := req.Config.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosMachineSecretsEphemeralResourceModel
diags = obj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Apply defaults for talos_version if not set, matching the regular resource's
// plan modifier behavior which uses the compiled-in Talos version
talosVersion := config.TalosVersion.ValueString()
if talosVersion == "" {
talosVersion = semver.MajorMinor(gendata.VersionTag)
}
versionContract, err := validateVersionContract(talosVersion)
if err != nil {
resp.Diagnostics.AddError(
"failed to validate talos version",
err.Error(),
)
return
}
// Generate secrets
secretsBundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), versionContract)
if err != nil {
resp.Diagnostics.AddError(
"failed to generate secrets bundle",
err.Error(),
)
return
}
// Convert to model
temp, err := secretsBundleTomachineSecrets(secretsBundle)
if err != nil {
resp.Diagnostics.AddError("failed to convert secrets bundle to machine secrets", err.Error())
return
}
// Build the ephemeral model (without ID field)
result := talosMachineSecretsEphemeralResourceModel{
TalosVersion: types.StringValue(talosVersion),
MachineSecrets: temp.MachineSecrets,
ClientConfiguration: temp.ClientConfiguration,
}
// Set result - ephemeral resources use Result instead of State
diags = resp.Result.Set(ctx, &result)
resp.Diagnostics.Append(diags...)
}
================================================
FILE: pkg/talos/talos_machine_secrets_ephemeral_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/echoprovider"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
"github.com/siderolabs/terraform-provider-talos/pkg/talos"
)
// testAccProtoV6ProviderFactoriesWithEcho includes both the talos provider and echo provider.
var testAccProtoV6ProviderFactoriesWithEcho = map[string]func() (tfprotov6.ProviderServer, error){
"talos": providerserver.NewProtocol6WithError(talos.New()),
"echo": echoprovider.NewProviderServer(),
}
// TestAccTalosMachineSecretsEphemeralResource tests that:
// 1. Ephemeral resource generates secrets
// 2. Secrets can be passed to other resources via echo provider
//
// Uses the Echo Provider to test values set in ephemeral resources
// see documentation here for more details:
// https://developer.hashicorp.com/terraform/plugin/testing/acceptance-tests/ephemeral-resources#using-echo-provider-in-acceptance-tests
func TestAccTalosMachineSecretsEphemeralResource(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
provider "echo" {
data = ephemeral.talos_machine_secrets.this
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
// Verify that the ephemeral resource provides client_configuration
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("client_configuration").AtMapKey("ca_certificate"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("client_configuration").AtMapKey("client_certificate"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("client_configuration").AtMapKey("client_key"), knownvalue.NotNull()),
// Verify that machine_secrets are populated
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("machine_secrets").AtMapKey("cluster").AtMapKey("id"), knownvalue.NotNull()),
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("machine_secrets").AtMapKey("cluster").AtMapKey("secret"), knownvalue.NotNull()),
// Verify bootstrap token exists
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("machine_secrets").AtMapKey("secrets").AtMapKey("bootstrap_token"), knownvalue.NotNull()),
},
},
},
})
}
// TestAccTalosMachineSecretsEphemeralResourceNotInState verifies that
// ephemeral resources are not persisted to state by the framework.
func TestAccTalosMachineSecretsEphemeralResourceNotInState(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `ephemeral "talos_machine_secrets" "this" {}`,
},
},
})
}
// TestAccTalosMachineSecretsEphemeralResourceWithDefault tests generation
// with default talos_version.
func TestAccTalosMachineSecretsEphemeralResourceWithDefault(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: `
ephemeral "talos_machine_secrets" "this" {}
provider "echo" {
data = {
cluster_id = ephemeral.talos_machine_secrets.this.machine_secrets.cluster.id
}
}
resource "echo" "test" {}
`,
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("cluster_id"), knownvalue.NotNull()),
},
},
},
})
}
================================================
FILE: pkg/talos/talos_machine_secrets_resource.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"crypto/x509"
"encoding/pem"
"os"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"go.yaml.in/yaml/v4"
"golang.org/x/mod/semver"
)
var (
_ resource.Resource = &talosMachineSecretsResource{}
_ resource.ResourceWithUpgradeState = &talosMachineSecretsResource{}
_ resource.ResourceWithImportState = &talosMachineSecretsResource{}
_ resource.ResourceWithModifyPlan = &talosMachineSecretsResource{}
)
// OverridableTimeFunc is a function that returns the current time. It is used to allow tests to override the current time.
//
//nolint:gocritic
var OverridableTimeFunc = func() time.Time {
return time.Now()
}
type talosMachineSecretsResource struct{}
type talosMachineSecretsResourceModelV0 struct {
ID types.String `tfsdk:"id"`
TalosVersion types.String `tfsdk:"talos_version"`
MachineSecrets types.String `tfsdk:"machine_secrets"`
}
type talosMachineSecretsResourceModelV1 struct {
ID types.String `tfsdk:"id"`
TalosVersion types.String `tfsdk:"talos_version"`
MachineSecrets machineSecrets `tfsdk:"machine_secrets"`
ClientConfiguration clientConfiguration `tfsdk:"client_configuration"`
}
type clientConfiguration struct {
CA types.String `tfsdk:"ca_certificate"`
Cert types.String `tfsdk:"client_certificate"`
Key types.String `tfsdk:"client_key"`
}
type machineSecrets struct {
Cluster machineSecretsCluster `tfsdk:"cluster"`
Secrets machineSecretsSecrets `tfsdk:"secrets"`
TrustdInfo machineSecretsTrustdInfo `tfsdk:"trustdinfo"`
Certs machineSecretsCerts `tfsdk:"certs"`
}
type machineSecretsCluster struct {
ID types.String `tfsdk:"id"`
Secret types.String `tfsdk:"secret"`
}
type machineSecretsSecrets struct {
BootstrapToken types.String `tfsdk:"bootstrap_token"`
SecretboxEncryptionSecret types.String `tfsdk:"secretbox_encryption_secret"`
AESCBCEncryptionSecret types.String `tfsdk:"aescbc_encryption_secret"`
}
type machineSecretsTrustdInfo struct {
Token types.String `tfsdk:"token"`
}
type machineSecretsCerts struct {
Etcd machineSecretsCertKeyPair `tfsdk:"etcd"`
K8s machineSecretsCertKeyPair `tfsdk:"k8s"`
K8sAggregator machineSecretsCertKeyPair `tfsdk:"k8s_aggregator"`
K8sServiceAccount machineSecretsCertsK8sServiceAccount `tfsdk:"k8s_serviceaccount"`
OS machineSecretsCertKeyPair `tfsdk:"os"`
}
type machineSecretsCertsK8sServiceAccount struct {
Key types.String `tfsdk:"key"`
}
type machineSecretsCertKeyPair struct {
Cert types.String `tfsdk:"cert"`
Key types.String `tfsdk:"key"`
}
// NewTalosMachineSecretsResource implements the resource.Resource interface.
func NewTalosMachineSecretsResource() resource.Resource {
return &talosMachineSecretsResource{}
}
func (r *talosMachineSecretsResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_machine_secrets"
}
func (r *talosMachineSecretsResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Version: 1,
Description: "Generate machine secrets for Talos cluster.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The computed ID of the Talos cluster",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"talos_version": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The Talos version contract used to generate the secrets. Example values: `v1.12`, `v1.12.1`, `1.12`, `1.12.1`",
Validators: []validator.String{
talosVersionValid(),
},
PlanModifiers: []planmodifier.String{
talosMachineFeaturesVersionDefaults(),
},
},
"machine_secrets": machineSecretsOutputSchemaAttribute(),
"client_configuration": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"ca_certificate": schema.StringAttribute{
Computed: true,
Description: "The client CA certificate",
},
"client_certificate": schema.StringAttribute{
Computed: true,
Description: "The client certificate",
},
"client_key": schema.StringAttribute{
Computed: true,
Sensitive: true,
Description: "The client key",
},
},
Computed: true,
Description: "The generated client configuration data",
},
},
}
}
func machineSecretsOutputSchemaAttribute() schema.SingleNestedAttribute {
return schema.SingleNestedAttribute{
Description: "The secrets for the talos cluster",
Attributes: map[string]schema.Attribute{
"cluster": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The cluster ID",
Computed: true,
},
"secret": schema.StringAttribute{
Description: "The cluster secret",
Computed: true,
Sensitive: true,
},
},
Description: "The cluster secrets",
Computed: true,
},
"secrets": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"bootstrap_token": schema.StringAttribute{
Description: "The bootstrap token",
Computed: true,
Sensitive: true,
},
"secretbox_encryption_secret": schema.StringAttribute{
Description: "The secretbox encryption secret",
Computed: true,
Sensitive: true,
},
"aescbc_encryption_secret": schema.StringAttribute{
Description: "The AES-CBC encryption secret",
Computed: true,
Sensitive: true,
},
},
Description: "kubernetes cluster secrets",
Computed: true,
},
"trustdinfo": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"token": schema.StringAttribute{
Description: "The trustd token",
Computed: true,
Sensitive: true,
},
},
Description: "trustd secrets",
Computed: true,
},
"certs": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"etcd": certSchema(),
"k8s": certSchema(),
"k8s_aggregator": certSchema(),
"k8s_serviceaccount": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"key": schema.StringAttribute{
Description: "The service account key",
Computed: true,
Sensitive: true,
},
},
Description: "The service account secrets",
Computed: true,
},
"os": certSchema(),
},
Computed: true,
},
},
Computed: true,
}
}
func certSchema() schema.SingleNestedAttribute {
return schema.SingleNestedAttribute{
Description: "The certificate and key pair",
Attributes: map[string]schema.Attribute{
"cert": schema.StringAttribute{
Description: "certificate data",
Computed: true,
},
"key": schema.StringAttribute{
Description: "key data",
Computed: true,
Sensitive: true,
},
},
Computed: true,
}
}
func (r *talosMachineSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var obj types.Object
diags := req.Plan.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var plan talosMachineSecretsResourceModelV1
diags = obj.As(ctx, &plan, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
versionContract, err := validateVersionContract(plan.TalosVersion.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"failed to validate talos version",
err.Error(),
)
return
}
secretsBundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), versionContract)
if err != nil {
resp.Diagnostics.AddError(
"failed to generate secrets bundle",
err.Error(),
)
return
}
state, err := secretsBundleTomachineSecrets(secretsBundle)
if err != nil {
resp.Diagnostics.AddError("failed to convert secrets bundle to machine secrets", err.Error())
return
}
state.TalosVersion = plan.TalosVersion
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func (r *talosMachineSecretsResource) Read(_ context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) {
}
func (r *talosMachineSecretsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
// delete is a no-op
if req.Plan.Raw.IsNull() {
return
}
clientConfigurationPath := path.Root("client_configuration")
var obj types.Object
resp.Diagnostics.Append(req.State.GetAttribute(ctx, clientConfigurationPath, &obj)...)
if resp.Diagnostics.HasError() {
return
}
var clientConfigurationData clientConfiguration
diags := obj.As(ctx, &clientConfigurationData, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if clientConfigurationData.CA.IsNull() || clientConfigurationData.CA.IsUnknown() {
return
}
clientCertificate := clientConfigurationData.Cert.ValueString()
clientCertificateBytes, err := base64ToBytes(clientCertificate)
if err != nil {
resp.Diagnostics.AddError("failed to decode client certificate", err.Error())
return
}
block, _ := pem.Decode(clientCertificateBytes)
if block == nil {
resp.Diagnostics.AddError("failed to decode client certificate", "failed to parse PEM block")
return
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
resp.Diagnostics.AddError("failed to parse client certificate", err.Error())
return
}
// check if NotAfter expires in a month
if x509Cert.NotAfter.Before(OverridableTimeFunc().AddDate(0, 1, 0)) {
tflog.Info(ctx, "client certificate expires in a month, needs regeneration")
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("client_configuration").AtName("ca_certificate"), types.StringUnknown())...)
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("client_configuration").AtName("client_certificate"), types.StringUnknown())...)
resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, path.Root("client_configuration").AtName("client_key"), types.StringUnknown())...)
if resp.Diagnostics.HasError() {
return
}
}
}
func (r *talosMachineSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var talosVersion string
diags := req.Plan.GetAttribute(ctx, path.Root("talos_version"), &talosVersion)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Set state to fully populated data
diags = resp.State.SetAttribute(ctx, path.Root("talos_version"), talosVersion)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
clientConfigurationPath := path.Root("client_configuration")
var obj types.Object
resp.Diagnostics.Append(req.State.GetAttribute(ctx, clientConfigurationPath, &obj)...)
if resp.Diagnostics.HasError() {
return
}
var clientConfigurationData clientConfiguration
diags = obj.As(ctx, &clientConfigurationData, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if clientConfigurationData.CA.IsNull() || clientConfigurationData.CA.IsUnknown() {
return
}
clientCertificate := clientConfigurationData.Cert.ValueString()
clientCertificateBytes, err := base64ToBytes(clientCertificate)
if err != nil {
resp.Diagnostics.AddError("failed to decode client certificate", err.Error())
return
}
block, _ := pem.Decode(clientCertificateBytes)
if block == nil {
resp.Diagnostics.AddError("failed to decode client certificate", "failed to parse PEM block")
return
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
resp.Diagnostics.AddError("failed to parse client certificate", err.Error())
return
}
// check if NotAfter expires in a month
if x509Cert.NotAfter.Before(OverridableTimeFunc().AddDate(0, 1, 0)) {
tflog.Info(ctx, "client certificate expires in a month, regenerating")
var obj types.Object
diags := req.State.Get(ctx, &obj)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var config talosMachineSecretsResourceModelV1
diags = obj.As(ctx, &config, basetypes.ObjectAsOptions{
UnhandledNullAsEmpty: true,
UnhandledUnknownAsEmpty: true,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
secretsBundle, err := machineSecretsToSecretsBundle(config)
if err != nil {
resp.Diagnostics.AddError("failed to convert machine secrets to secrets bundle", err.Error())
return
}
if secretsBundle.Clock == nil {
secretsBundle.Clock = secrets.NewFixedClock(time.Now())
}
state, err := secretsBundleTomachineSecrets(secretsBundle)
if err != nil {
resp.Diagnostics.AddError("failed to convert secrets bundle to machine secrets", err.Error())
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("client_configuration").AtName("ca_certificate"), &state.ClientConfiguration.CA)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("client_configuration").AtName("client_certificate"), &state.ClientConfiguration.Cert)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("client_configuration").AtName("client_key"), &state.ClientConfiguration.Key)...)
if resp.Diagnostics.HasError() {
return
}
}
}
func (r *talosMachineSecretsResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
}
func talosMachineFeaturesVersionDefaults() planmodifier.String {
return &talosMachineFeaturesVersionPlanModifier{}
}
type talosMachineFeaturesVersionPlanModifier struct{}
var _ planmodifier.String = (*talosMachineFeaturesVersionPlanModifier)(nil)
func (apm *talosMachineFeaturesVersionPlanModifier) Description(_ context.Context) string {
return "sets default value for talos_version if not set"
}
func (apm *talosMachineFeaturesVersionPlanModifier) MarkdownDescription(ctx context.Context) string {
return apm.Description(ctx)
}
func (apm *talosMachineFeaturesVersionPlanModifier) PlanModifyString(_ context.Context, req planmodifier.StringRequest, res *planmodifier.StringResponse) {
if req.ConfigValue.IsUnknown() {
return
}
// setting default value
if req.PlanValue.IsUnknown() || req.PlanValue.IsNull() {
res.PlanValue = basetypes.NewStringValue(semver.MajorMinor(gendata.VersionTag))
return
}
planValue := "v" + strings.TrimPrefix(req.PlanValue.ValueString(), "v")
stateValue := "v" + strings.TrimPrefix(req.StateValue.ValueString(), "v")
if semver.Compare(planValue, stateValue) < 0 {
res.RequiresReplace = true
}
}
func (r *talosMachineSecretsResource) UpgradeState(_ context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: &schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"talos_version": schema.StringAttribute{
Optional: true,
},
"machine_secrets": schema.StringAttribute{
Computed: true,
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var priorStateData talosMachineSecretsResourceModelV0
diags := req.State.Get(ctx, &priorStateData)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
var secretsBundle *secrets.Bundle
if err := yaml.Unmarshal([]byte(priorStateData.MachineSecrets.ValueString()), &secretsBundle); err != nil {
resp.Diagnostics.AddError("failed to unmarshal machine secrets", err.Error())
return
}
state, err := secretsBundleTomachineSecrets(secretsBundle)
if err != nil {
resp.Diagnostics.AddError("failed to convert secrets bundle to machine secrets", err.Error())
return
}
state.TalosVersion = basetypes.NewStringValue("v1.3")
if secretsBundle.Secrets.AESCBCEncryptionSecret != "" {
state.TalosVersion = basetypes.NewStringValue("v1.2")
}
// Set state to fully populated data
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
},
},
}
}
func (r *talosMachineSecretsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
id := req.ID
if _, err := os.Stat(id); err != nil {
resp.Diagnostics.AddError("failed to import state", err.Error())
return
}
secretBytes, err := os.ReadFile(id)
if err != nil {
resp.Diagnostics.AddError("failed to read machine secrets file", err.Error())
return
}
var secretsBundle *secrets.Bundle
if err = yaml.Unmarshal(secretBytes, &secretsBundle); err != nil {
resp.Diagnostics.AddError("failed to unmarshal machine secrets", err.Error())
return
}
state, err := secretsBundleTomachineSecrets(secretsBundle)
if err != nil {
resp.Diagnostics.AddError("failed to convert secrets bundle to machine secrets", err.Error())
return
}
state.TalosVersion = basetypes.NewStringValue("v1.3")
if secretsBundle.Secrets.AESCBCEncryptionSecret != "" {
state.TalosVersion = basetypes.NewStringValue("v1.2")
}
// Set state to fully populated data
diags := resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
================================================
FILE: pkg/talos/talos_machine_secrets_resource_test.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos_test
import (
"fmt"
"testing"
"time"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"golang.org/x/mod/semver"
"github.com/siderolabs/terraform-provider-talos/pkg/talos"
)
//nolint:maintidx
func TestAccTalosMachineSecretsResource(t *testing.T) {
testTime := time.Now()
resource.Test(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test defaults
{
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", semver.MajorMinor(gendata.VersionTag)),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.secretbox_encryption_secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_key"),
resource.TestCheckNoResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
// test talosconfig regeneration
{
Config: testAccTalosMachineSecretsResourceConfig(""),
PreConfig: func() {
talos.OverridableTimeFunc = func() time.Time {
return testTime.AddDate(0, 12, 5)
}
},
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
},
})
talos.OverridableTimeFunc = func() time.Time {
return testTime
}
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test defaults
{
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", semver.MajorMinor(gendata.VersionTag)),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.secretbox_encryption_secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_key"),
resource.TestCheckNoResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
// test that setting the talos_version to the same value does not cause a diff
{
Config: testAccTalosMachineSecretsResourceConfig(semver.MajorMinor(gendata.VersionTag)),
PreConfig: func() {
talos.OverridableTimeFunc = func() time.Time {
return testTime
}
},
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectEmptyPlan(),
},
},
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", semver.MajorMinor(gendata.VersionTag)),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.secretbox_encryption_secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_key"),
resource.TestCheckNoResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
// test that setting the talos_version to a lower version causes a diff and requires replacement
// also test that the aescbc_encryption_secret is set
{ //nolint:dupl
Config: testAccTalosMachineSecretsResourceConfig("v1.2.0"),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectNonEmptyPlan(),
plancheck.ExpectResourceAction("talos_machine_secrets.this", plancheck.ResourceActionReplace),
},
},
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.2.0"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
// test that setting the talos_version to a higher version does not cause a diff and requires no replacement
// also test that the aescbc_encryption_secret is still set when upgrading
{ //nolint:dupl
Config: testAccTalosMachineSecretsResourceConfig("1.4"),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectNonEmptyPlan(),
plancheck.ExpectResourceAction("talos_machine_secrets.this", plancheck.ResourceActionUpdate),
},
},
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "1.4"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.ca_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
},
})
resource.Test(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test importing from a secrets.yaml file
{
Config: testAccTalosMachineSecretsResourceConfig(""),
ResourceName: "talos_machine_secrets.this",
ImportState: true,
ImportStatePersist: true,
ImportStateId: "testdata/secrets.yaml",
},
// verify there are no changes after import
{
Config: testAccTalosMachineSecretsResourceConfig(""),
PlanOnly: true,
},
// verify state is correct after import
{
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.3"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.cluster.id", "_u8NZvwQ9ObtEN7iTzc-OEpk20K-rnO3FNcjvVEQ84Q="),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.cluster.secret", "UnZE8oq6qPNI8tuw+WF3PGi2Zba0RQuit/aJTflOau8="),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token", "m2wfba.pcyzhp6rf6pubqtk"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.secretbox_encryption_secret", "avDR6jwn4iYS6sTOH689P2UcNUlh3UsuO+FaOKI2hls="),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.trustdinfo.token", "s5lcto.f2ythdlx6avcsny9"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmakNDQVNTZ0F3SUJBZ0lSQUt6KzNQbkZYWHNTdXNQb3RLTnVabG93Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TXpBME1ERXhORE01TURaYUZ3MHpNekF6TWpreE5ETTVNRFphTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTbGFaU0p1UnhMCit4NDdYelY5OWIwaEd1dmdybGZrUnFEdStVUWlrSFJlRCtFL3VZUXprc1ArZTFLMVBUcFJVTG45ZEJYY21jd1AKazY2UXRCN09mQ0pEbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5kVHVVdmduZXcwCkJSa0dZRnJueWNpWFZjUTdNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURJZGVqQ3ppL25xb3h0eUp3QnROTEYKTDE3UWdqQjNzMFoySUV5ZDZPTE51UUlnY0lHcWRWemJKQ3dXSkZXSWxnWWVyUGZvZzdzUjFxMWdKV25ST3p4UApBakE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.etcd.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUoxWVI2ck9pd3N2TmRWZFErVnpiQ1hlRlBEbGJ1cDVUM1ZidnJrVDM2M1NvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFcFdtVWlia2NTL3NlTzE4MWZmVzlJUnJyNEs1WDVFYWc3dmxFSXBCMFhnL2hQN21FTTVMRAovbnRTdFQwNlVWQzUvWFFWM0puTUQ1T3VrTFFlem53aVF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpakNDQVMrZ0F3SUJBZ0lRSE5COFFlVjRtazlUR3k4amtTUGtYVEFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNRFF3TVRFME16a3dObG9YRFRNek1ETXlPVEUwTXprdwpObG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCQVBKQjEwL3RnVFlkdVhrS0h6SVFEZDNLbW54MithbmFXcGN3RUlhWlhMbnNsRHRycGU3ancwaXRVK1oKa2w2eEd5STk4M0FpRkxWc004cHhra3RoZ2tTallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU1K0hGd29PbTdURWhpcm1DTGt2SllyRmRQc293Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQU1WZFNVUEUKcGlNWkpkNHNYV1pvdzdLb0djWWhPb1NtcDJkaUZEdUEzMjcyQWlFQTRuamcwN2ZhVEFGck1SUGF4SmFIMVhsdQpKTHBPMTh0bHZhVlVFMW5XY2xnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUZJQ0crOUR1Wm9YU0xiVjRWK1VoZkdRUWV5OUJ3dWEwZUx6M0hTU1hyMmhvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQThrSFhUKzJCTmgyNWVRb2ZNaEFOM2NxYWZIYjVxZHBhbHpBUWhwbGN1ZXlVTzJ1bDd1UApEU0sxVDVtU1hyRWJJajN6Y0NJVXRXd3p5bkdTUzJHQ1JBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUtDY3hEVUM4UG9GK3pSajJWVlJLWTR3Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU16QTBNREV4TkRNNU1EWmFGdzB6TXpBek1qa3hORE01TURaYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVFPUjZKcTl1ekp3RWNFZnowc1dKdERVejhwNUpseHRJZkJjTTMzRFJ5cDhCSDZGQTFCCjJiUkx5ZTBsU3p6TXFmdVpFblhxOE9qazdobklGSUdsNlVNaW8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGQUpPaUFGNTRhUXRPaUpzdFU2U2Z1QWpGOFJwTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRREdPOUhXVXNaZlozRUdJTEdGRjJwUDIwUGNjSDZMV2hZSjg1eHB2RDBOSGdJaEFOOHFpVElXYVF2dUs3M1cKVENwT2g5TU1PS2o1YmFNcEh6M2FJOGVMZHBiVAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU4rRDdGWGJoUTg4c1d6eEJPSXZtMVJVVVBtT25VeFBCRGlvR21mL1dnMjlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFRGtlaWF2YnN5Y0JIQkg4OUxGaWJRMU0vS2VTWmNiU0h3WEROOXcwY3FmQVIraFFOUWRtMApTOG50SlVzOHpLbjdtUkoxNnZEbzVPNFp5QlNCcGVsRElnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVabmsrN2djSXBLdC8vM2lrQ1dLZm5SNnFjeWY5bHVyK1lGbzQyTlhVVVlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeG9rOTBEbnI2MGRYQ3BhQmxUMnVJUnRjdUJPLytCVjZEajVPaWYvTGVwSS95NktlQ2lRTApwRGZxenNHQW5zQXkrVHI3SWNxSUlROGZGaXRtK0t5emJBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.os.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBM0E0TEZ3cDVTZG5wTXVmYlp3NHVpekFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qTXdOREF4TVRRek9UQTJXaGNOTXpNd016STVNVFF6T1RBMldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUZ2TWUyeHFzSk9WemkvY0xLNXVXVzU2VmZZK29nWlYvQVowCmxlTFdtTWl1bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkxqK3VablllVFFUdEdmUgpOeVVQYWh6dzNkdkhNQVVHQXl0bGNBTkJBQVg4cVhJNm4ydlk3ZGxnZGtxckUvN25ua2kwTzFtVERDL3dBamlwCmpaemY5QmhocEdRUXFYSkxHdlhJTnRDaXN5KzQrcTVtOUVjUUpMMXF4UWdOdndnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.os.key", "LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJSlVlQmxic3hhMW0vR1B0NTZCeVIvZ1Z3YWRzVmdkc3pXZEh4MWZiZ0c4VwotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K"), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "client_configuration.ca_certificate", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBM0E0TEZ3cDVTZG5wTXVmYlp3NHVpekFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qTXdOREF4TVRRek9UQTJXaGNOTXpNd016STVNVFF6T1RBMldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUZ2TWUyeHFzSk9WemkvY0xLNXVXVzU2VmZZK29nWlYvQVowCmxlTFdtTWl1bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkxqK3VablllVFFUdEdmUgpOeVVQYWh6dzNkdkhNQVVHQXl0bGNBTkJBQVg4cVhJNm4ydlk3ZGxnZGtxckUvN25ua2kwTzFtVERDL3dBamlwCmpaemY5QmhocEdRUXFYSkxHdlhJTnRDaXN5KzQrcTVtOUVjUUpMMXF4UWdOdndnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_key"),
resource.TestCheckNoResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
},
})
resource.Test(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// test importing from a v1.2 secrets file
{
Config: testAccTalosMachineSecretsResourceConfig(""),
ResourceName: "talos_machine_secrets.this",
ImportState: true,
ImportStatePersist: true,
ImportStateId: "testdata/secretsv1.2.yaml",
},
// verify that there are no diffs
{
Config: testAccTalosMachineSecretsResourceConfig(""),
PlanOnly: true,
},
// verify state is correct after import
{
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.2"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.cluster.id", "q_I385nl7MWqU1UpW224rQyZW4TWd_WmnxsA2MQLsl8="),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.cluster.secret", "1szT7qMuensSCcSVRtnFsG0pbXMLMSZ8r5wu/41aJBc="),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token", "5co9z6.qnnjtotc5ffntt62"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.secretbox_encryption_secret", ""),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.trustdinfo.token", "o2q4ek.ofdeihu3li44x7lr"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmakNDQVNTZ0F3SUJBZ0lSQU8rYmlXSlFJdHZXZUZ0UUNEVDhwRXd3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TXpBME1ERXhOVFF4TURSYUZ3MHpNekF6TWpreE5UUXhNRFJhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFReHRnaFlZV1AvCngzWGFBM1RPVXd4Y1AySlh2QVYzdllaS2I1SENKK0E2M2dieE50dW5Gcm03NW8rK0ZQQndYdUtZUmMrU09pTXEKdWY5bjdkZmY4cUJKbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkU2VUVneitoOSsvCnZYdnNod1d2bHJvQkh6SlFNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUUQ3YTFzMy91cmRybFlxaXJxTmN6aTIKTm5qNVFCdFVmczFNYkNqNTdkYXR3d0lnZlJYdEIyWVZMUy9OMFVQTDkxekhITCtLM09EaEZ2M1dsc0gwMlpmdgpMNHc9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.etcd.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJDNUpZQmgzWWQ2NnJwWkcvTHZEMWt4SFRvWTA4QnZsQkpMcy9aZXR4NldvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFTWJZSVdHRmovOGQxMmdOMHpsTU1YRDlpVjd3RmQ3MkdTbStSd2lmZ090NEc4VGJicHhhNQp1K2FQdmhUd2NGN2ltRVhQa2pvaktybi9aKzNYMy9LZ1NRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVW9hWGkyZGxUWHk1alNBUVdvSUZOVEFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNRFF3TVRFMU5ERXdORm9YRFRNek1ETXlPVEUxTkRFdwpORm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCRWJieTQreTdIWmZUcVNNaDRtMWx3a3E3THE5WUtWdVhmV3BJLzZ4K1orZC9uUFJFNFA0eGhNUklKL3oKZHl4TDJxTGNSOUcxV3pjVWJBRVYrMjliUWNDallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVUzRG8xb2MzM054dWhYRTJ4M2NvbHM3a2dWWmN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9pb3RHR1MKNnJLdWFoT2twMlQ5NjRzSUhhdmx3bUJqZ0ljVkI1dTFPV2RsQWlBRXZKUUVTRGNBZWlmVm1seG1pdWVDMHl4SQowODBqY2FxUjdUVWNjaHhrSnc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUx5bm9FaURjeG1NL3ZlL1B1R0YwWFNjeEgydG10SGZSdVU0SkVvejBGRmtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUnR2TGo3THNkbDlPcEl5SGliV1hDU3JzdXIxZ3BXNWQ5YWtqL3JINW41MytjOUVUZy9qRwpFeEVnbi9OM0xFdmFvdHhIMGJWYk54UnNBUlg3YjF0QndBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZRENDQVFhZ0F3SUJBZ0lSQUp5THJmLzk5aE00a1ZNWTJraFd4TjR3Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU16QTBNREV4TlRReE1EUmFGdzB6TXpBek1qa3hOVFF4TURSYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVFZTTBWWUVZWTVOaURlNHp6Z21mYlJSdDRNalRmOUlyeUVVcms1SjNPRFQzYkFpanVDCmd6eWpmTkZIbFlXbm9PcWZBeGpjVVVsQkU2L2xuRmdiMzNwUW8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGTHBKUEhhRGw1UjY4NjlDNEVyVXF5WHhBeUpWTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDCklFc0VlOElSNEwvK3phMC9CbzlUNGRKbERpQ3VDK1BSd1JueXE4OEE5dFUvQWlFQTBGUXNJcGk5V1ZiU2krODQKQkVCaGRWTkpmQUVUYTZVQ2UrTWFsNUJRUjlrPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUMxSDViQUY5RkJWQzVIQjZ2dzJwL1FRUFkvWEVjdzhaTUJ0ZDZFTmw3cXFvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFR0RORldCR0dPVFlnM3VNODRKbjIwVWJlREkwMy9TSzhoRks1T1NkemcwOTJ3SW83Z29NOApvM3pSUjVXRnA2RHFud01ZM0ZGSlFST3Y1WnhZRzk5NlVBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key", "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxHRjlhcmp4SEdyMExMbTdMTjg0Ympjbml2RWpGSkxlUFNvVGhZUS9maWdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNjRuQVlld3hqeVkrVkY4MDZ5WU5iU3pnNEl4cFh6TW1hMW93b3FjbDc5elZtMkRsbHcxUApYR3FTZ0hpWUxwcjZ1ZU5OeSswcXdOdklCU3RKSXZLVzNnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.os.cert", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEIzcXY0bDNhTStSaGFsWmdHOGowdDNNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16QTBNREV4TlRReE1EUmFGdzB6TXpBek1qa3hOVFF4TURSYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBWk1TT1d4aHF4UThEbHdUVmszM2xRN09ydDAvOTE5b0JXTVpUCmRSU3Q4SGFqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVYkhDeHcyOTd4RHc3Tjh0SQpDQTJTUDc2K3Q5OHdCUVlESzJWd0EwRUFrcnQ1UEVnZ2JZNFFlYnNIa2lDTmZlMFpZNlE1UmZhVm52TVRxOE1lCnRhSktTQ1NPYTljczh2dXVDMnl2QmNSU0hPWldocG9WaW05bXhEaVc3TDZTQXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.certs.os.key", "LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJRjdtNmJKWHNOd3F4ejFMaXRnVlFJSEx5WDJab1hadW85UTNEZjRGSThWaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K"), //nolint:lll
resource.TestCheckResourceAttr("talos_machine_secrets.this", "client_configuration.ca_certificate", "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEIzcXY0bDNhTStSaGFsWmdHOGowdDNNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16QTBNREV4TlRReE1EUmFGdzB6TXpBek1qa3hOVFF4TURSYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBWk1TT1d4aHF4UThEbHdUVmszM2xRN09ydDAvOTE5b0JXTVpUCmRSU3Q4SGFqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVYkhDeHcyOTd4RHc3Tjh0SQpDQTJTUDc2K3Q5OHdCUVlESzJWd0EwRUFrcnQ1UEVnZ2JZNFFlYnNIa2lDTmZlMFpZNlE1UmZhVm52TVRxOE1lCnRhSktTQ1NPYTljczh2dXVDMnl2QmNSU0hPWldocG9WaW05bXhEaVc3TDZTQXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="), //nolint:lll
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_certificate"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "client_configuration.client_key"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret", "hLrjDIpZ8gSGwejFfnUnjOrn9PQ7Bj3yq/ggAgD9AHA="),
),
},
},
})
}
func TestAccTalosMachineSecretsResourceUpgrade1(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
Steps: []resource.TestStep{
// create talos_machine_secrets resource with talos version 0.1.2
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.1.2",
Source: "siderolabs/talos",
},
},
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "id"),
resource.TestCheckNoResourceAttr("talos_machine_secrets.this", "talos_version"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets"),
),
},
// verify the new state is compatible with the latest version of the provider
{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.3"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.secretbox_encryption_secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckNoResourceAttr("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
},
})
}
func TestAccTalosMachineSecretsResourceUpgrade2(t *testing.T) { //nolint:dupl
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
Steps: []resource.TestStep{
// create talos_machine_secrets resource with talos version 0.1.2
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.1.2",
Source: "siderolabs/talos",
},
},
Config: testAccTalosMachineSecretsResourceConfig("v1.2"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "id"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.2"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets"),
),
},
// verify the new state is compatible with the latest version of the provider
{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.2"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
},
})
}
func TestAccTalosMachineSecretsResourceUpgrade3(t *testing.T) { //nolint:dupl
resource.ParallelTest(t, resource.TestCase{
IsUnitTest: true, // this is a local only resource, so can be unit tested
Steps: []resource.TestStep{
// create talos_machine_secrets resource with talos version 0.1.2
{
ExternalProviders: map[string]resource.ExternalProvider{
"talos": {
VersionConstraint: "=0.1.2",
Source: "siderolabs/talos",
},
},
Config: testAccTalosMachineSecretsResourceConfig("v1.1"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "id"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.1"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets"),
),
},
// verify the new state is compatible with the latest version of the provider
{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Config: testAccTalosMachineSecretsResourceConfig(""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("talos_machine_secrets.this", "id", "machine_secrets"),
resource.TestCheckResourceAttr("talos_machine_secrets.this", "talos_version", "v1.2"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.id"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.cluster.secret"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.bootstrap_token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.trustdinfo.token"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.etcd.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_aggregator.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.k8s_serviceaccount.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.cert"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.certs.os.key"),
resource.TestCheckResourceAttrSet("talos_machine_secrets.this", "machine_secrets.secrets.aescbc_encryption_secret"),
),
},
},
})
}
func testAccTalosMachineSecretsResourceConfig(talosConfigVersion string) string {
if talosConfigVersion != "" {
return fmt.Sprintf(`
resource "talos_machine_secrets" "this" {
talos_version = "%s"
}
`, talosConfigVersion)
}
return `
resource "talos_machine_secrets" "this" {}
`
}
================================================
FILE: pkg/talos/testdata/patch-invalid.yaml
================================================
machine:
sysctl:
foo: bar
================================================
FILE: pkg/talos/testdata/patch-strategic.yaml
================================================
machine:
sysfs:
foo: bar
cluster:
apiServer:
extraArgs:
foo: bar
================================================
FILE: pkg/talos/testdata/secrets.yaml
================================================
cluster:
id: _u8NZvwQ9ObtEN7iTzc-OEpk20K-rnO3FNcjvVEQ84Q=
secret: UnZE8oq6qPNI8tuw+WF3PGi2Zba0RQuit/aJTflOau8=
secrets:
bootstraptoken: m2wfba.pcyzhp6rf6pubqtk
secretboxencryptionsecret: avDR6jwn4iYS6sTOH689P2UcNUlh3UsuO+FaOKI2hls=
trustdinfo:
token: s5lcto.f2ythdlx6avcsny9
certs:
etcd:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmakNDQVNTZ0F3SUJBZ0lSQUt6KzNQbkZYWHNTdXNQb3RLTnVabG93Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TXpBME1ERXhORE01TURaYUZ3MHpNekF6TWpreE5ETTVNRFphTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTbGFaU0p1UnhMCit4NDdYelY5OWIwaEd1dmdybGZrUnFEdStVUWlrSFJlRCtFL3VZUXprc1ArZTFLMVBUcFJVTG45ZEJYY21jd1AKazY2UXRCN09mQ0pEbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5kVHVVdmduZXcwCkJSa0dZRnJueWNpWFZjUTdNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURJZGVqQ3ppL25xb3h0eUp3QnROTEYKTDE3UWdqQjNzMFoySUV5ZDZPTE51UUlnY0lHcWRWemJKQ3dXSkZXSWxnWWVyUGZvZzdzUjFxMWdKV25ST3p4UApBakE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUoxWVI2ck9pd3N2TmRWZFErVnpiQ1hlRlBEbGJ1cDVUM1ZidnJrVDM2M1NvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFcFdtVWlia2NTL3NlTzE4MWZmVzlJUnJyNEs1WDVFYWc3dmxFSXBCMFhnL2hQN21FTTVMRAovbnRTdFQwNlVWQzUvWFFWM0puTUQ1T3VrTFFlem53aVF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
k8s:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpakNDQVMrZ0F3SUJBZ0lRSE5COFFlVjRtazlUR3k4amtTUGtYVEFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNRFF3TVRFME16a3dObG9YRFRNek1ETXlPVEUwTXprdwpObG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCQVBKQjEwL3RnVFlkdVhrS0h6SVFEZDNLbW54MithbmFXcGN3RUlhWlhMbnNsRHRycGU3ancwaXRVK1oKa2w2eEd5STk4M0FpRkxWc004cHhra3RoZ2tTallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU1K0hGd29PbTdURWhpcm1DTGt2SllyRmRQc293Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQU1WZFNVUEUKcGlNWkpkNHNYV1pvdzdLb0djWWhPb1NtcDJkaUZEdUEzMjcyQWlFQTRuamcwN2ZhVEFGck1SUGF4SmFIMVhsdQpKTHBPMTh0bHZhVlVFMW5XY2xnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUZJQ0crOUR1Wm9YU0xiVjRWK1VoZkdRUWV5OUJ3dWEwZUx6M0hTU1hyMmhvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQThrSFhUKzJCTmgyNWVRb2ZNaEFOM2NxYWZIYjVxZHBhbHpBUWhwbGN1ZXlVTzJ1bDd1UApEU0sxVDVtU1hyRWJJajN6Y0NJVXRXd3p5bkdTUzJHQ1JBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
k8saggregator:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUtDY3hEVUM4UG9GK3pSajJWVlJLWTR3Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU16QTBNREV4TkRNNU1EWmFGdzB6TXpBek1qa3hORE01TURaYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVFPUjZKcTl1ekp3RWNFZnowc1dKdERVejhwNUpseHRJZkJjTTMzRFJ5cDhCSDZGQTFCCjJiUkx5ZTBsU3p6TXFmdVpFblhxOE9qazdobklGSUdsNlVNaW8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGQUpPaUFGNTRhUXRPaUpzdFU2U2Z1QWpGOFJwTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRREdPOUhXVXNaZlozRUdJTEdGRjJwUDIwUGNjSDZMV2hZSjg1eHB2RDBOSGdJaEFOOHFpVElXYVF2dUs3M1cKVENwT2g5TU1PS2o1YmFNcEh6M2FJOGVMZHBiVAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU4rRDdGWGJoUTg4c1d6eEJPSXZtMVJVVVBtT25VeFBCRGlvR21mL1dnMjlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFRGtlaWF2YnN5Y0JIQkg4OUxGaWJRMU0vS2VTWmNiU0h3WEROOXcwY3FmQVIraFFOUWRtMApTOG50SlVzOHpLbjdtUkoxNnZEbzVPNFp5QlNCcGVsRElnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
k8sserviceaccount:
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVabmsrN2djSXBLdC8vM2lrQ1dLZm5SNnFjeWY5bHVyK1lGbzQyTlhVVVlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeG9rOTBEbnI2MGRYQ3BhQmxUMnVJUnRjdUJPLytCVjZEajVPaWYvTGVwSS95NktlQ2lRTApwRGZxenNHQW5zQXkrVHI3SWNxSUlROGZGaXRtK0t5emJBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
os:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBM0E0TEZ3cDVTZG5wTXVmYlp3NHVpekFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qTXdOREF4TVRRek9UQTJXaGNOTXpNd016STVNVFF6T1RBMldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUZ2TWUyeHFzSk9WemkvY0xLNXVXVzU2VmZZK29nWlYvQVowCmxlTFdtTWl1bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkxqK3VablllVFFUdEdmUgpOeVVQYWh6dzNkdkhNQVVHQXl0bGNBTkJBQVg4cVhJNm4ydlk3ZGxnZGtxckUvN25ua2kwTzFtVERDL3dBamlwCmpaemY5QmhocEdRUXFYSkxHdlhJTnRDaXN5KzQrcTVtOUVjUUpMMXF4UWdOdndnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJSlVlQmxic3hhMW0vR1B0NTZCeVIvZ1Z3YWRzVmdkc3pXZEh4MWZiZ0c4VwotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K
================================================
FILE: pkg/talos/testdata/secretsv1.2.yaml
================================================
cluster:
id: q_I385nl7MWqU1UpW224rQyZW4TWd_WmnxsA2MQLsl8=
secret: 1szT7qMuensSCcSVRtnFsG0pbXMLMSZ8r5wu/41aJBc=
secrets:
bootstraptoken: 5co9z6.qnnjtotc5ffntt62
aescbcencryptionsecret: hLrjDIpZ8gSGwejFfnUnjOrn9PQ7Bj3yq/ggAgD9AHA=
trustdinfo:
token: o2q4ek.ofdeihu3li44x7lr
certs:
etcd:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmakNDQVNTZ0F3SUJBZ0lSQU8rYmlXSlFJdHZXZUZ0UUNEVDhwRXd3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TXpBME1ERXhOVFF4TURSYUZ3MHpNekF6TWpreE5UUXhNRFJhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFReHRnaFlZV1AvCngzWGFBM1RPVXd4Y1AySlh2QVYzdllaS2I1SENKK0E2M2dieE50dW5Gcm03NW8rK0ZQQndYdUtZUmMrU09pTXEKdWY5bjdkZmY4cUJKbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkU2VUVneitoOSsvCnZYdnNod1d2bHJvQkh6SlFNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUUQ3YTFzMy91cmRybFlxaXJxTmN6aTIKTm5qNVFCdFVmczFNYkNqNTdkYXR3d0lnZlJYdEIyWVZMUy9OMFVQTDkxekhITCtLM09EaEZ2M1dsc0gwMlpmdgpMNHc9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJDNUpZQmgzWWQ2NnJwWkcvTHZEMWt4SFRvWTA4QnZsQkpMcy9aZXR4NldvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFTWJZSVdHRmovOGQxMmdOMHpsTU1YRDlpVjd3RmQ3MkdTbStSd2lmZ090NEc4VGJicHhhNQp1K2FQdmhUd2NGN2ltRVhQa2pvaktybi9aKzNYMy9LZ1NRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
k8s:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVW9hWGkyZGxUWHk1alNBUVdvSUZOVEFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNRFF3TVRFMU5ERXdORm9YRFRNek1ETXlPVEUxTkRFdwpORm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCRWJieTQreTdIWmZUcVNNaDRtMWx3a3E3THE5WUtWdVhmV3BJLzZ4K1orZC9uUFJFNFA0eGhNUklKL3oKZHl4TDJxTGNSOUcxV3pjVWJBRVYrMjliUWNDallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVUzRG8xb2MzM054dWhYRTJ4M2NvbHM3a2dWWmN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9pb3RHR1MKNnJLdWFoT2twMlQ5NjRzSUhhdmx3bUJqZ0ljVkI1dTFPV2RsQWlBRXZKUUVTRGNBZWlmVm1seG1pdWVDMHl4SQowODBqY2FxUjdUVWNjaHhrSnc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUx5bm9FaURjeG1NL3ZlL1B1R0YwWFNjeEgydG10SGZSdVU0SkVvejBGRmtvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUnR2TGo3THNkbDlPcEl5SGliV1hDU3JzdXIxZ3BXNWQ5YWtqL3JINW41MytjOUVUZy9qRwpFeEVnbi9OM0xFdmFvdHhIMGJWYk54UnNBUlg3YjF0QndBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
k8saggregator:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZRENDQVFhZ0F3SUJBZ0lSQUp5THJmLzk5aE00a1ZNWTJraFd4TjR3Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU16QTBNREV4TlRReE1EUmFGdzB6TXpBek1qa3hOVFF4TURSYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVFZTTBWWUVZWTVOaURlNHp6Z21mYlJSdDRNalRmOUlyeUVVcms1SjNPRFQzYkFpanVDCmd6eWpmTkZIbFlXbm9PcWZBeGpjVVVsQkU2L2xuRmdiMzNwUW8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGTHBKUEhhRGw1UjY4NjlDNEVyVXF5WHhBeUpWTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDCklFc0VlOElSNEwvK3phMC9CbzlUNGRKbERpQ3VDK1BSd1JueXE4OEE5dFUvQWlFQTBGUXNJcGk5V1ZiU2krODQKQkVCaGRWTkpmQUVUYTZVQ2UrTWFsNUJRUjlrPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUMxSDViQUY5RkJWQzVIQjZ2dzJwL1FRUFkvWEVjdzhaTUJ0ZDZFTmw3cXFvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFR0RORldCR0dPVFlnM3VNODRKbjIwVWJlREkwMy9TSzhoRks1T1NkemcwOTJ3SW83Z29NOApvM3pSUjVXRnA2RHFud01ZM0ZGSlFST3Y1WnhZRzk5NlVBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
k8sserviceaccount:
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxHRjlhcmp4SEdyMExMbTdMTjg0Ympjbml2RWpGSkxlUFNvVGhZUS9maWdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNjRuQVlld3hqeVkrVkY4MDZ5WU5iU3pnNEl4cFh6TW1hMW93b3FjbDc5elZtMkRsbHcxUApYR3FTZ0hpWUxwcjZ1ZU5OeSswcXdOdklCU3RKSXZLVzNnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
os:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEIzcXY0bDNhTStSaGFsWmdHOGowdDNNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16QTBNREV4TlRReE1EUmFGdzB6TXpBek1qa3hOVFF4TURSYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBWk1TT1d4aHF4UThEbHdUVmszM2xRN09ydDAvOTE5b0JXTVpUCmRSU3Q4SGFqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVYkhDeHcyOTd4RHc3Tjh0SQpDQTJTUDc2K3Q5OHdCUVlESzJWd0EwRUFrcnQ1UEVnZ2JZNFFlYnNIa2lDTmZlMFpZNlE1UmZhVm52TVRxOE1lCnRhSktTQ1NPYTljczh2dXVDMnl2QmNSU0hPWldocG9WaW05bXhEaVc3TDZTQXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJRjdtNmJKWHNOd3F4ejFMaXRnVlFJSEx5WDJab1hadW85UTNEZjRGSThWaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K
================================================
FILE: pkg/talos/util.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package talos
import (
"context"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/sha256"
"crypto/tls"
stdlibx509 "crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"fmt"
"math/big"
"net/url"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/siderolabs/crypto/x509"
sideronet "github.com/siderolabs/net"
"github.com/siderolabs/talos/pkg/machinery/client"
clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config"
"github.com/siderolabs/talos/pkg/machinery/config"
"github.com/siderolabs/talos/pkg/machinery/config/bundle"
"github.com/siderolabs/talos/pkg/machinery/config/configpatcher"
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
"github.com/siderolabs/talos/pkg/machinery/config/generate"
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/gendata"
"github.com/siderolabs/talos/pkg/machinery/role"
"golang.org/x/crypto/hkdf"
)
type machineConfigGenerateOptions struct { //nolint:govet
machineType machine.Type
clusterName string
clusterEndpoint string
machineSecrets *secrets.Bundle
kubernetesVersion string
talosVersion string
docsEnabled bool
examplesEnabled bool
configPatches []string
}
func (m *machineConfigGenerateOptions) generate() (string, error) {
genOptions := make([]generate.Option, 0)
// default gen options
genOptions = append(genOptions,
generate.WithClusterDiscovery(true),
generate.WithDNSDomain(constants.DefaultDNSDomain),
generate.WithInstallDisk("/dev/sda"),
generate.WithInstallImage(GenerateInstallerImage()),
generate.WithSecretsBundle(m.machineSecrets),
)
versionContract, err := validateVersionContract(m.talosVersion)
if err != nil {
return "", err
}
genOptions = append(genOptions, generate.WithVersionContract(versionContract))
commentsFlags := encoder.CommentsDisabled
if m.docsEnabled {
commentsFlags |= encoder.CommentsDocs
}
if m.examplesEnabled {
commentsFlags |= encoder.CommentsExamples
}
configBundleOpts := []bundle.Option{
bundle.WithInputOptions(
&bundle.InputOptions{
ClusterName: m.clusterName,
Endpoint: m.clusterEndpoint,
KubeVersion: strings.TrimPrefix(m.kubernetesVersion, "v"),
GenOptions: genOptions,
},
),
bundle.WithVerbose(false),
}
addConfigPatch := func(configPatches []string, configOpt func([]configpatcher.Patch) bundle.Option) error {
var patches []configpatcher.Patch
patches, err = configpatcher.LoadPatches(configPatches)
if err != nil {
return fmt.Errorf("error parsing config patch: %w", err)
}
configBundleOpts = append(configBundleOpts, configOpt(patches))
return nil
}
switch m.machineType { //nolint:exhaustive
case machine.TypeControlPlane:
if err = addConfigPatch(m.configPatches, bundle.WithPatchControlPlane); err != nil {
return "", err
}
case machine.TypeWorker:
if err = addConfigPatch(m.configPatches, bundle.WithPatchWorker); err != nil {
return "", err
}
}
configBundle, err := bundle.NewBundle(configBundleOpts...)
if err != nil {
return "", err
}
machineConfigBytes, err := configBundle.Serialize(commentsFlags, m.machineType)
if err != nil {
return "", err
}
return string(machineConfigBytes), nil
}
// GenerateInstallerImage generates the installer image name.
func GenerateInstallerImage() string {
return fmt.Sprintf("%s/%s/installer:%s", gendata.ImagesRegistry, gendata.ImagesUsername, gendata.VersionTag)
}
func secretsBundleTomachineSecrets(secretsBundle *secrets.Bundle) (talosMachineSecretsResourceModelV1, error) {
model := talosMachineSecretsResourceModelV1{
ID: types.StringValue("machine_secrets"),
MachineSecrets: machineSecrets{
Cluster: machineSecretsCluster{
ID: types.StringValue(secretsBundle.Cluster.ID),
Secret: types.StringValue(secretsBundle.Cluster.Secret),
},
Secrets: machineSecretsSecrets{
BootstrapToken: types.StringValue(secretsBundle.Secrets.BootstrapToken),
SecretboxEncryptionSecret: types.StringValue(secretsBundle.Secrets.SecretboxEncryptionSecret),
},
TrustdInfo: machineSecretsTrustdInfo{
Token: types.StringValue(secretsBundle.TrustdInfo.Token),
},
Certs: machineSecretsCerts{
Etcd: machineSecretsCertKeyPair{
Cert: types.StringValue(bytesToBase64(secretsBundle.Certs.Etcd.Crt)),
Key: types.StringValue(bytesToBase64(secretsBundle.Certs.Etcd.Key)),
},
K8s: machineSecretsCertKeyPair{
Cert: types.StringValue(bytesToBase64(secretsBundle.Certs.K8s.Crt)),
Key: types.StringValue(bytesToBase64(secretsBundle.Certs.K8s.Key)),
},
K8sServiceAccount: machineSecretsCertsK8sServiceAccount{
Key: types.StringValue(bytesToBase64(secretsBundle.Certs.K8sServiceAccount.Key)),
},
OS: machineSecretsCertKeyPair{
Cert: types.StringValue(bytesToBase64(secretsBundle.Certs.OS.Crt)),
Key: types.StringValue(bytesToBase64(secretsBundle.Certs.OS.Key)),
},
},
},
}
if secretsBundle.Certs.K8sAggregator.Crt != nil {
model.MachineSecrets.Certs.K8sAggregator.Cert = types.StringValue(bytesToBase64(secretsBundle.Certs.K8sAggregator.Crt))
model.MachineSecrets.Certs.K8sAggregator.Key = types.StringValue(bytesToBase64(secretsBundle.Certs.K8sAggregator.Key))
}
// support for talos < 1.3
if secretsBundle.Secrets.AESCBCEncryptionSecret != "" {
model.MachineSecrets.Secrets.AESCBCEncryptionSecret = types.StringValue(secretsBundle.Secrets.AESCBCEncryptionSecret)
}
cc, err := generateClientConfigurationWithTTL(secretsBundle, constants.TalosAPIDefaultCertificateValidityDuration)
if err != nil {
return model, err
}
model.ClientConfiguration = cc
return model, nil
}
// generateClientConfigurationWithTTL generates a clientConfiguration with a TTL-based cert.
// Used by the managed talos_machine_secrets resource which stores and renews certs in state.
func generateClientConfigurationWithTTL(secretsBundle *secrets.Bundle, ttl time.Duration) (clientConfiguration, error) {
if secretsBundle.Clock == nil {
secretsBundle.Clock = secrets.NewFixedClock(time.Now())
}
clientcert, err := secretsBundle.GenerateTalosAPIClientCertificateWithTTL(role.MakeSet(role.Admin), ttl)
if err != nil {
return clientConfiguration{}, err
}
return clientConfiguration{
CA: types.StringValue(bytesToBase64(secretsBundle.Certs.OS.Crt)),
Cert: types.StringValue(bytesToBase64(clientcert.Crt)),
Key: types.StringValue(bytesToBase64(clientcert.Key)),
}, nil
}
// generateClientConfiguration generates a clientConfiguration from a secrets bundle.
//
// The admin client certificate and key are derived deterministically using HKDF
// (RFC 5869) seeded from the OS CA private key and all cert-relevant inputs.
// Same inputs always produce byte-identical output — no crypto/rand is used.
//
// Supports Ed25519 and ECDSA P-256 OS CA keys (Talos uses Ed25519 since v1.x).
// Ed25519 signing is deterministic by design (RFC 8032); ECDSA uses RFC 6979.
func generateClientConfiguration(secretsBundle *secrets.Bundle, clusterName string, notBefore, notAfter time.Time) (clientConfiguration, error) {
caCertBlock, _ := pem.Decode(secretsBundle.Certs.OS.Crt)
if caCertBlock == nil {
return clientConfiguration{}, fmt.Errorf("error decoding OS CA certificate PEM")
}
caCert, err := stdlibx509.ParseCertificate(caCertBlock.Bytes)
if err != nil {
return clientConfiguration{}, fmt.Errorf("error parsing OS CA certificate: %w", err)
}
caKeyBlock, _ := pem.Decode(secretsBundle.Certs.OS.Key)
if caKeyBlock == nil {
return clientConfiguration{}, fmt.Errorf("error decoding OS CA private key PEM")
}
info := fmt.Sprintf("talos-clientconfig:v1:%s:%d:%d", clusterName, notBefore.Unix(), notAfter.Unix())
deterministicReader := hkdf.New(sha256.New, secretsBundle.Certs.OS.Key, []byte("talos-clientconfig-v1"), []byte(info))
serialBytes := make([]byte, 16)
if _, err = deterministicReader.Read(serialBytes); err != nil {
return clientConfiguration{}, fmt.Errorf("error deriving serial number: %w", err)
}
template := &stdlibx509.Certificate{
SerialNumber: new(big.Int).SetBytes(serialBytes),
Subject: pkix.Name{
Organization: []string{"os:admin"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: stdlibx509.KeyUsageDigitalSignature,
ExtKeyUsage: []stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageClientAuth},
}
var (
certDER []byte
clientKeyPEM []byte
)
caKeyParsed, parseErr := parseCAPrivateKey(caKeyBlock)
if parseErr != nil {
return clientConfiguration{}, parseErr
}
switch caKey := caKeyParsed.(type) {
case ed25519.PrivateKey:
adminKeyBytes := make([]byte, ed25519.SeedSize)
if _, err = deterministicReader.Read(adminKeyBytes); err != nil {
return clientConfiguration{}, fmt.Errorf("error deriving admin key bytes: %w", err)
}
adminKey := ed25519.NewKeyFromSeed(adminKeyBytes)
// Ed25519 signing is deterministic per RFC 8032 — rand reader is unused.
certDER, err = stdlibx509.CreateCertificate(nil, template, caCert, adminKey.Public(), caKey)
if err != nil {
return clientConfiguration{}, fmt.Errorf("error signing admin certificate: %w", err)
}
adminKeyDER, marshalErr := stdlibx509.MarshalPKCS8PrivateKey(adminKey)
if marshalErr != nil {
return clientConfiguration{}, fmt.Errorf("error marshaling admin private key: %w", marshalErr)
}
clientKeyPEM = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: adminKeyDER})
case *ecdsa.PrivateKey:
adminKeyBytes := make([]byte, 32)
if _, err = deterministicReader.Read(adminKeyBytes); err != nil {
return clientConfiguration{}, fmt.Errorf("error deriving admin key bytes: %w", err)
}
adminKey, ecParseErr := ecdsa.ParseRawPrivateKey(elliptic.P256(), adminKeyBytes)
if ecParseErr != nil {
return clientConfiguration{}, fmt.Errorf("error parsing derived admin key: %w", ecParseErr)
}
caSigner := &deterministicECDSASigner{key: caKey}
certDER, err = stdlibx509.CreateCertificate(nil, template, caCert, &adminKey.PublicKey, caSigner)
if err != nil {
return clientConfiguration{}, fmt.Errorf("error signing admin certificate: %w", err)
}
adminKeyDER, marshalErr := stdlibx509.MarshalECPrivateKey(adminKey)
if marshalErr != nil {
return clientConfiguration{}, fmt.Errorf("error marshaling admin private key: %w", marshalErr)
}
clientKeyPEM = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: adminKeyDER})
default:
return clientConfiguration{}, fmt.Errorf("unsupported OS CA private key type: %T", caKeyParsed)
}
clientCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
return clientConfiguration{
CA: types.StringValue(bytesToBase64(secretsBundle.Certs.OS.Crt)),
Cert: types.StringValue(bytesToBase64(clientCertPEM)),
Key: types.StringValue(bytesToBase64(clientKeyPEM)),
}, nil
}
// parseCAPrivateKey parses a PEM block into either an ed25519.PrivateKey or *ecdsa.PrivateKey.
func parseCAPrivateKey(block *pem.Block) (any, error) {
switch block.Type {
case "EC PRIVATE KEY":
key, err := stdlibx509.ParseECPrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("error parsing OS CA private key: %w", err)
}
return key, nil
case "ED25519 PRIVATE KEY", "PRIVATE KEY":
parsed, err := stdlibx509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("error parsing OS CA private key: %w", err)
}
switch k := parsed.(type) {
case ed25519.PrivateKey:
return k, nil
case *ecdsa.PrivateKey:
return k, nil
default:
return nil, fmt.Errorf("unsupported OS CA private key type in PKCS#8: %T", parsed)
}
default:
return nil, fmt.Errorf("unsupported OS CA private key PEM type: %s", block.Type)
}
}
type clientConfigTimestampError struct {
summary string
detail string
}
func (e *clientConfigTimestampError) Error() string { return e.detail }
// resolveClientConfigTimestamps resolves notBefore/notAfter for the admin client certificate.
// When notBeforeStr is non-empty it parses the RFC3339 timestamp and adds crtTTLStr (default 87600h).
// When notBeforeStr is empty it reads the timestamps from the OS CA PEM.
func resolveClientConfigTimestamps(notBeforeStr, crtTTLStr string, osCACert []byte) (notBefore, notAfter time.Time, tsErr *clientConfigTimestampError) {
if notBeforeStr != "" {
var err error
notBefore, err = time.Parse(time.RFC3339, notBeforeStr)
if err != nil {
return time.Time{}, time.Time{}, &clientConfigTimestampError{
summary: "invalid not_before",
detail: fmt.Sprintf("unable to parse not_before %q as RFC3339: %s", notBeforeStr, err.Error()),
}
}
crtTTL := 87600 * time.Hour
if crtTTLStr != "" {
crtTTL, err = time.ParseDuration(crtTTLStr)
if err != nil {
return time.Time{}, time.Time{}, &clientConfigTimestampError{
summary: "invalid crt_ttl",
detail: fmt.Sprintf("unable to parse crt_ttl %q: %s", crtTTLStr, err.Error()),
}
}
}
return notBefore, notBefore.Add(crtTTL), nil
}
block, _ := pem.Decode(osCACert)
if block == nil {
return time.Time{}, time.Time{}, &clientConfigTimestampError{
summary: "failed to parse Talos OS CA certificate",
detail: "PEM block is nil",
}
}
caCert, err := stdlibx509.ParseCertificate(block.Bytes)
if err != nil {
return time.Time{}, time.Time{}, &clientConfigTimestampError{
summary: "failed to parse Talos OS CA certificate",
detail: err.Error(),
}
}
return caCert.NotBefore, caCert.NotAfter, nil
}
func machineSecretsToSecretsBundle(model talosMachineSecretsResourceModelV1) (*secrets.Bundle, error) {
secretsBundle := &secrets.Bundle{
Cluster: &secrets.Cluster{
ID: model.MachineSecrets.Cluster.ID.ValueString(),
Secret: model.MachineSecrets.Cluster.Secret.ValueString(),
},
Secrets: &secrets.Secrets{
BootstrapToken: model.MachineSecrets.Secrets.BootstrapToken.ValueString(),
SecretboxEncryptionSecret: model.MachineSecrets.Secrets.SecretboxEncryptionSecret.ValueString(),
},
TrustdInfo: &secrets.TrustdInfo{
Token: model.MachineSecrets.TrustdInfo.Token.ValueString(),
},
}
if model.MachineSecrets.Secrets.AESCBCEncryptionSecret.ValueString() != "" {
secretsBundle.Secrets.AESCBCEncryptionSecret = model.MachineSecrets.Secrets.AESCBCEncryptionSecret.ValueString()
}
etcdCert, err := base64ToBytes(model.MachineSecrets.Certs.Etcd.Cert.ValueString())
if err != nil {
return nil, err
}
etcdKey, err := base64ToBytes(model.MachineSecrets.Certs.Etcd.Key.ValueString())
if err != nil {
return nil, err
}
k8sCert, err := base64ToBytes(model.MachineSecrets.Certs.K8s.Cert.ValueString())
if err != nil {
return nil, err
}
k8sKey, err := base64ToBytes(model.MachineSecrets.Certs.K8s.Key.ValueString())
if err != nil {
return nil, err
}
k8sAggregatorCert, err := base64ToBytes(model.MachineSecrets.Certs.K8sAggregator.Cert.ValueString())
if err != nil {
return nil, err
}
k8sAggregatorKey, err := base64ToBytes(model.MachineSecrets.Certs.K8sAggregator.Key.ValueString())
if err != nil {
return nil, err
}
k8sServiceAccountKey, err := base64ToBytes(model.MachineSecrets.Certs.K8sServiceAccount.Key.ValueString())
if err != nil {
return nil, err
}
osCert, err := base64ToBytes(model.MachineSecrets.Certs.OS.Cert.ValueString())
if err != nil {
return nil, err
}
osKey, err := base64ToBytes(model.MachineSecrets.Certs.OS.Key.ValueString())
if err != nil {
return nil, err
}
secretsBundle.Certs = &secrets.Certs{
Etcd: &x509.PEMEncodedCertificateAndKey{
Crt: etcdCert,
Key: etcdKey,
},
K8s: &x509.PEMEncodedCertificateAndKey{
Crt: k8sCert,
Key: k8sKey,
},
K8sAggregator: &x509.PEMEncodedCertificateAndKey{
Crt: k8sAggregatorCert,
Key: k8sAggregatorKey,
},
K8sServiceAccount: &x509.PEMEncodedKey{
Key: k8sServiceAccountKey,
},
OS: &x509.PEMEncodedCertificateAndKey{
Crt: osCert,
Key: osKey,
},
}
return secretsBundle, nil
}
func validateVersionContract(version string) (*config.VersionContract, error) {
versionContract, err := config.ParseContractFromVersion(version)
if err != nil {
return nil, err
}
return versionContract, nil
}
func talosClientOp(ctx context.Context, endpoint, node string, tc *clientconfig.Config, opFunc func(ctx context.Context, c *client.Client) error) error {
nodeCtx := client.WithNode(ctx, node)
c, err := client.New(ctx, client.WithTLSConfig(&tls.Config{
InsecureSkipVerify: true,
}), client.WithEndpoints(endpoint))
if err != nil {
return err
}
_, err = c.Disks(nodeCtx)
if err != nil {
c.Close() //nolint:errcheck
c, err = client.New(ctx, client.WithConfig(tc), client.WithEndpoints(endpoint))
if err != nil {
return err
}
}
defer c.Close() //nolint:errcheck
return opFunc(nodeCtx, c)
}
type talosVersionValidator struct{}
func talosVersionValid() talosVersionValidator {
return talosVersionValidator{}
}
func (v talosVersionValidator) ValidateString(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) {
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
return
}
version := req.ConfigValue.ValueString()
_, err := validateVersionContract(version)
if err != nil {
resp.Diagnostics.AddError("Invalid version", err.Error())
}
}
func (v talosVersionValidator) Description(_ context.Context) string {
return "Validates that the talos version is valid"
}
func (v talosVersionValidator) MarkdownDescription(ctx context.Context) string {
return v.Description(ctx)
}
type goDurationValidator struct{}
func goDurationValid() goDurationValidator {
return goDurationValidator{}
}
func (v goDurationValidator) ValidateString(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) {
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
return
}
if _, err := time.ParseDuration(req.ConfigValue.ValueString()); err != nil {
resp.Diagnostics.AddAttributeError(
req.Path,
"invalid crt_ttl",
fmt.Sprintf("unable to parse duration %q: %s", req.ConfigValue.ValueString(), err.Error()),
)
}
}
func (v goDurationValidator) Description(_ context.Context) string {
return "Validates that the value is a valid Go duration string (e.g. \"8760h\", \"87600h\")"
}
func (v goDurationValidator) MarkdownDescription(ctx context.Context) string {
return v.Description(ctx)
}
type rfc3339Validator struct{}
func rfc3339Valid() rfc3339Validator {
return rfc3339Validator{}
}
func (v rfc3339Validator) ValidateString(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) {
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
return
}
if _, err := time.Parse(time.RFC3339, req.ConfigValue.ValueString()); err != nil {
resp.Diagnostics.AddAttributeError(
req.Path,
"invalid not_before",
fmt.Sprintf("unable to parse not_before %q as RFC3339: %s", req.ConfigValue.ValueString(), err.Error()),
)
}
}
func (v rfc3339Validator) Description(_ context.Context) string {
return "must be a valid RFC3339 timestamp (e.g. \"2024-01-01T00:00:00Z\")"
}
func (v rfc3339Validator) MarkdownDescription(ctx context.Context) string {
return v.Description(ctx)
}
func validateClusterEndpoint(endpoint string) error {
// Validate url input to ensure it has https:// scheme before we attempt to gen
u, err := url.Parse(endpoint)
if err != nil {
if !strings.Contains(endpoint, "/") {
// not a URL, could be just host:port
u = &url.URL{
Host: endpoint,
}
} else {
return fmt.Errorf("failed to parse the cluster endpoint URL: %w", err)
}
}
if u.Scheme == "" {
if u.Port() == "" {
return fmt.Errorf("no scheme and port specified for the cluster endpoint URL\ntry: %q", fixControlPlaneEndpoint(u))
}
return fmt.Errorf("no scheme specified for the cluster endpoint URL\ntry: %q", fixControlPlaneEndpoint(u))
}
if u.Scheme != "https" {
return fmt.Errorf("the control plane endpoint URL should have scheme https://\ntry: %q", fixControlPlaneEndpoint(u))
}
if err = sideronet.ValidateEndpointURI(endpoint); err != nil {
return fmt.Errorf("error validating the cluster endpoint URL: %w", err)
}
return nil
}
func fixControlPlaneEndpoint(u *url.URL) *url.URL {
// handle the case when the hostname/IP is given without the port, it parses as URL Path
if u.Scheme == "" && u.Host == "" && u.Path != "" {
u.Host = u.Path
u.Path = ""
}
u.Scheme = "https"
if u.Port() == "" {
u.Host = fmt.Sprintf("%s:%d", u.Host, constants.DefaultControlPlanePort)
}
return u
}
func bytesToBase64(b []byte) string {
return base64.StdEncoding.EncodeToString(b)
}
func base64ToBytes(in string) ([]byte, error) {
return base64.StdEncoding.DecodeString(in)
}
func talosClientTFConfigToTalosClientConfig(clusterName, ca, cert, key string) (*clientconfig.Config, error) {
caCert, err := base64ToBytes(ca)
if err != nil {
return nil, err
}
clientCert, err := base64ToBytes(cert)
if err != nil {
return nil, err
}
clientKey, err := base64ToBytes(key)
if err != nil {
return nil, err
}
talosConfig := clientconfig.NewConfig(
clusterName,
nil,
caCert,
&x509.PEMEncodedCertificateAndKey{
Crt: clientCert,
Key: clientKey,
},
)
return talosConfig, nil
}
================================================
FILE: templates/data-sources/machine_configuration.md.tmpl
================================================
---
page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}"
subcategory: ""
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# {{.Name}} ({{.Type}})
{{ .Description | trimspace }}
-> **Note:** It is recommended to set the optional `talos_version` attribute. Otherwise when using a new version of the provider with a new major version of the Talos SDK, new machineconfig features will be enabled by default which could cause unexpected behavior.
{{ if .HasExample -}}
## Example Usage
{{ tffile (printf .ExampleFile) }}
{{- end }}
{{ .SchemaMarkdown | trimspace }}
================================================
FILE: templates/data-sources/machine_disks.md.tmpl
================================================
---
page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}"
subcategory: ""
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# {{.Name}} ({{.Type}})
{{ .Description | trimspace }}
-> **Note:** Since Talos natively supports `.machine.install.diskSelector`, the `talos_machine_disks` data source maybe just used to query disk information that could be used elsewhere. It's recommended to use `machine.install.diskSelector` in Talos machine configuration.
{{ if .HasExample -}}
## Example Usage
{{ tffile (printf .ExampleFile) }}
{{- end }}
{{ .SchemaMarkdown | trimspace }}
================================================
FILE: templates/ephemeral-resources/machine_configuration.md.tmpl
================================================
---
page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}"
subcategory: ""
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# {{.Name}} ({{.Type}})
{{ .Description | trimspace }}
-> **Note:** It is recommended to set the optional `talos_version` attribute. Otherwise when using a new version of the provider with a new major version of the Talos SDK, new machineconfig features will be enabled by default which could cause unexpected behavior.
{{ if .HasExample -}}
## Example Usage
{{ tffile (printf .ExampleFile) }}
{{- end }}
{{ .SchemaMarkdown | trimspace }}
================================================
FILE: templates/guides/using_ephemeral_resources.md
================================================
---
page_title: "Using Ephemeral Resources - talos Provider"
subcategory: ""
description: |-
Learn how to use ephemeral resources in the Talos provider to prevent secrets from being stored in Terraform state
---
# Using Ephemeral Resources in the Talos Provider
Ephemeral resources are Terraform resources that are essentially temporary. They allow you to access and use data in your configurations without that data being stored in Terraform state. This is particularly important for sensitive data like machine secrets, certificates, and kubeconfig files.
Ephemeral resources are available in Terraform v1.10 and later. For more information, see the [official HashiCorp documentation for Ephemeral Resources](https://developer.hashicorp.com/terraform/language/resources/ephemeral).
## Available Ephemeral Resources
The Talos provider includes five ephemeral resources:
- [`talos_machine_secrets`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/machine_secrets) - Generate machine secrets without storing them in state
- [`talos_machine_configuration`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/machine_configuration) - Generate machine configuration without storing secrets in state
- [`talos_client_configuration`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/client_configuration) - Generate client configuration (talosconfig) without storing credentials in state
- [`talos_cluster_kubeconfig`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/cluster_kubeconfig) - Retrieve kubeconfig without storing credentials in state
- [`talos_cluster_health`](https://registry.terraform.io/providers/siderolabs/talos/latest/docs/ephemeral-resources/cluster_health) - Check cluster health without storing credentials in state
These complement the existing data sources and resources, allowing you to avoid storing credentials and secret values in your Terraform state.
## Why Use Ephemeral Resources?
**Security Benefits:**
- Secrets never written to Terraform state files
- Reduces risk of credential exposure through state files
- Complies with security policies requiring secret-free state
**When to Use:**
- Generating Talos machine secrets
- Creating machine configurations with sensitive data
- Retrieving kubeconfig files
- Any workflow where secrets shouldn't persist in state
## Critical: Machine Secrets Persistence
**IMPORTANT**: Do not use `ephemeral "talos_machine_secrets"` without also storing them in a secret manager. Generating ephemeral machine secrets without persistence would create **new secrets on every Terraform run**, causing:
- Unpredictable changes to dependent resources
- Need to reconfigure all cluster nodes
- Loss of access to the cluster with previous credentials
- Non-deterministic infrastructure state
### Correct Pattern: Secret Manager Integration
Machine secrets should be:
1. Generated once and stored in a secret manager (Vault, AWS Secrets Manager, etc.)
2. Retrieved ephemerally from the secret manager when needed
3. Used to generate machine configurations deterministically
This ensures:
- Secrets remain stable across Terraform runs
- No secrets stored in Terraform state
- Deterministic, reproducible infrastructure
- Compliance with security policies
## Using Ephemeral Resources with Write-Only Attributes
Ephemeral resources are a source of ephemeral data, and they can be referenced in your configuration just like the attributes of resources and data sources. However, a field that references an ephemeral resource must be capable of handling ephemeral data.
The Talos provider includes write-only attributes that accept ephemeral values:
- `machine_configuration_input_wo` - Write-only alternative to `machine_configuration_input` on `talos_machine_configuration_apply` resource (requires Terraform 1.11+)
## Example: Using Vault for Secret Persistence
This example demonstrates the correct pattern for managing Talos machine secrets with Vault. Both secret generation and retrieval can coexist in the same configuration:
```terraform
terraform {
required_version = ">= 1.11"
required_providers {
talos = {
source = "siderolabs/talos"
version = "~> 0.11"
}
vault = {
source = "hashicorp/vault"
version = "~> 5.0"
}
}
}
# Step 1: Generate and store secrets in Vault
# The ephemeral resource generates secrets only when needed (first run)
# After initial creation, this won't be evaluated because data_json_wo_version is hardcoded
ephemeral "talos_machine_secrets" "this" {}
resource "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-${var.cluster_name}"
# Write-only attributes prevent secrets from being stored in Terraform state
data_json_wo = jsonencode({
machine_secrets = ephemeral.talos_machine_secrets.this.machine_secrets
client_configuration = ephemeral.talos_machine_secrets.this.client_configuration
})
# Hardcoded version prevents unnecessary refreshes after initial creation
data_json_wo_version = 1
}
# Step 2: Retrieve secrets ephemerally from Vault
# This runs on every terraform operation but values are never stored in state
# Referencing the resource attributes creates implicit dependency on the secret
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = vault_kv_secret_v2.talos_secrets.mount
name = vault_kv_secret_v2.talos_secrets.name
}
locals {
# Decode the secret data
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
# Step 3: Generate machine configuration using retrieved secrets
ephemeral "talos_machine_configuration" "controlplane" {
cluster_name = var.cluster_name
cluster_endpoint = var.cluster_endpoint
machine_type = "controlplane"
machine_secrets = local.talos_data.machine_secrets
}
# Step 4: Apply configuration using write-only input
resource "talos_machine_configuration_apply" "controlplane" {
client_configuration_wo = local.talos_data.client_configuration
machine_configuration_input_wo = ephemeral.talos_machine_configuration.controlplane.machine_configuration
node = var.controlplane_node
# Note: machine_configuration computed attribute will be null in state
# This is expected behavior for secret-free operation
}
```
**Secret-Free Operation:**
When using write-only attributes (`_wo` variants), the provider ensures zero secrets leak into state:
- **Write-only inputs** (`client_configuration_wo`, `machine_configuration_input_wo`): Never stored in state
- **Computed outputs** (`machine_configuration`): Automatically set to null in state when using write-only inputs
The provider computes the machine configuration internally during apply operations without persisting it. This provides complete secret-free operation while maintaining full functionality.
**How This Works:**
1. **First Run**:
- Secrets are generated ephemerally
- Stored in Vault with version 1
- Retrieved from Vault for immediate use
- All in a single `terraform apply` (Terraform handles the ordering automatically)
2. **Subsequent Runs**:
- The `vault_kv_secret_v2` resource doesn't need updates (version is hardcoded)
- The `ephemeral "talos_machine_secrets"` isn't evaluated (no dependent resources need it)
- Secrets are retrieved ephemerally from Vault for use in configuration
**Key Benefits:**
- Works in a single run on first apply
- Both blocks coexist permanently in your configuration
- Terraform handles all dependencies automatically
- No secrets stored in Terraform state
### Alternative Pattern: External Secret Generation
If you prefer to manage secret generation outside Terraform:
```bash
# Generate secrets manually using talosctl
talosctl gen secrets -o secrets.yaml
# Store in Vault using vault CLI
vault kv put secret/talos-cluster-prod \
machine_secrets="$(yq -o=json '.machine_secrets' secrets.yaml)" \
client_configuration="$(yq -o=json '.client_configuration' secrets.yaml)"
```
Then your Terraform configuration only needs the retrieval part:
```terraform
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-prod"
}
locals {
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
ephemeral "talos_machine_configuration" "controlplane" {
cluster_name = "prod-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
machine_secrets = local.talos_data.machine_secrets
}
```
## Example: Generating Kubeconfig Ephemerally from Machine Secrets
This example shows how to generate a kubeconfig ephemerally from machine secrets stored in Vault.
The kubeconfig is generated locally from the Kubernetes CA key — no live cluster required.
### Simple usage (CA-pinned timestamps)
When `not_before` is omitted, the admin certificate validity window is taken from the K8s CA's
own timestamps (set once when the cluster was created). The output is byte-identical on every
plan as long as `machine_secrets` does not change.
```terraform
# Retrieve stored secrets from Vault
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-prod"
}
locals {
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
# Generate kubeconfig without storing in state
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "prod-cluster"
machine_secrets = local.talos_data.machine_secrets
endpoint = "https://10.5.0.2:6443"
}
# Output the kubeconfig (marked as ephemeral)
output "kubeconfig" {
value = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw
sensitive = true
ephemeral = true
}
```
### Recommended pattern for Vault-backed workflows (explicit `not_before`)
When storing `kubeconfig_raw` in a Vault KV secret (or any resource that detects byte changes),
use a `terraform_data` resource to persist a stable `not_before` timestamp in Terraform state.
This pins the admin certificate validity window so `kubeconfig_raw` is byte-identical across
all plan invocations — no `ignore_changes` or `data_json_wo_version` bumps required until you
explicitly rotate the certificate.
```terraform
# Persist the admin cert NotBefore timestamp in regular Terraform state.
# Use ignore_changes so it is set once and never updated automatically.
# To rotate the cert: taint this resource and re-apply.
resource "terraform_data" "kubeconfig_nbf" {
input = plantimestamp()
lifecycle {
ignore_changes = [input]
}
}
# Generate kubeconfig with pinned timestamps
ephemeral "talos_cluster_kubeconfig" "this" {
cluster_name = "prod-cluster"
machine_secrets = local.talos_data.machine_secrets
endpoint = "https://10.5.0.2:6443"
not_before = terraform_data.kubeconfig_nbf.output
crt_ttl = "87600h"
}
# Store kubeconfig in Vault — kubeconfig_raw is stable so this resource
# only updates when machine_secrets or not_before change.
resource "vault_kv_secret_v2" "kubeconfig" {
mount = "secret"
name = "kubeconfig-prod-cluster"
data_json_wo = jsonencode({ kubeconfig = ephemeral.talos_cluster_kubeconfig.this.kubeconfig_raw })
data_json_wo_version = 1
}
```
**Certificate rotation**: taint `terraform_data.kubeconfig_nbf` to force a new `not_before`,
which produces a new cert and triggers a `data_json_wo_version` bump on the Vault secret.
**Note**: The kubeconfig is generated locally from the machine secrets and does not require
a running cluster.
## Alternative Secret Managers
While the examples above use HashiCorp Vault, you can use any secret manager that supports:
- Storing secrets via Terraform resources
- Retrieving secrets via ephemeral resources
### AWS Secrets Manager Example
```terraform
# Store secrets in AWS Secrets Manager
resource "aws_secretsmanager_secret" "talos_secrets" {
name = "talos-cluster-${var.cluster_name}"
}
resource "aws_secretsmanager_secret_version" "talos_secrets" {
secret_id = aws_secretsmanager_secret.talos_secrets.id
secret_string = jsonencode({
machine_secrets = talos_machine_secrets.this.machine_secrets
client_configuration = talos_machine_secrets.this.client_configuration
})
}
# Note: AWS provider doesn't yet have ephemeral resources for Secrets Manager
# You would use a data source, which still stores in state
# Watch for AWS provider updates adding ephemeral support
```
### Azure Key Vault Example
```terraform
# Store secrets in Azure Key Vault
resource "azurerm_key_vault_secret" "talos_secrets" {
name = "talos-cluster-${var.cluster_name}"
value = jsonencode({
machine_secrets = talos_machine_secrets.this.machine_secrets
client_configuration = talos_machine_secrets.this.client_configuration
})
key_vault_id = azurerm_key_vault.main.id
}
# Note: Azure provider doesn't yet have ephemeral resources for Key Vault
# You would use a data source for now
```
## Important Considerations
### Terraform Version Requirements
- **Terraform 1.10+**: Supports ephemeral resources only (no write-only attributes)
- **Terraform 1.11+**: Supports both ephemeral resources and write-only attributes
- **OpenTofu 1.11+**: Supports both ephemeral resources and write-only attributes
- Note: OpenTofu 1.10 does NOT support ephemeral resources (they were introduced in 1.11)
**For the examples in this guide**: Terraform 1.11+ or OpenTofu 1.11+ required (uses write-only attributes)
### Compatibility with Existing Resources
Ephemeral resources complement existing data sources and resources:
- **Data sources** (e.g., `data.talos_machine_configuration`) - Still work, but store output in state
- **Ephemeral resources** (e.g., `ephemeral.talos_machine_configuration`) - Same functionality, no state storage
You can migrate existing configurations to ephemeral resources by:
1. Changing `data "talos_machine_configuration"` to `ephemeral "talos_machine_configuration"`
2. Updating references from `data.talos_machine_configuration.this` to `ephemeral.talos_machine_configuration.this`
3. Using write-only attributes (e.g., `machine_configuration_input_wo`) where applicable
## Migration Guide
### From Data Source to Ephemeral Resource with Vault
**Before (using data source):**
```terraform
resource "talos_machine_secrets" "this" {}
data "talos_machine_configuration" "this" {
cluster_name = "my-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
}
```
**After (using ephemeral resources with Vault):**
Step 1 - Store existing secrets in Vault (one-time migration):
```terraform
# Assuming you have existing talos_machine_secrets resource
resource "vault_kv_secret_v2" "talos_secrets" {
mount = "secret"
name = "talos-cluster-my-cluster"
data_json_wo = jsonencode({
machine_secrets = talos_machine_secrets.this.machine_secrets
client_configuration = talos_machine_secrets.this.client_configuration
})
}
```
Step 2 - Use ephemeral resources to retrieve from Vault:
```terraform
# Retrieve secrets from Vault ephemerally
# Reference the resource to create implicit dependency
ephemeral "vault_kv_secret_v2" "talos_secrets" {
mount = vault_kv_secret_v2.talos_secrets.mount
name = vault_kv_secret_v2.talos_secrets.name
}
locals {
talos_data = jsondecode(ephemeral.vault_kv_secret_v2.talos_secrets.data_json)
}
# Generate configuration ephemerally
ephemeral "talos_machine_configuration" "this" {
cluster_name = "my-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
machine_secrets = local.talos_data.machine_secrets
}
# Apply configuration using write-only attribute
resource "talos_machine_configuration_apply" "this" {
client_configuration_wo = local.talos_data.client_configuration
machine_configuration_input_wo = ephemeral.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
}
```
Step 3 - After verifying the migration works, remove the `talos_machine_secrets` resource from your state:
```bash
terraform state rm talos_machine_secrets.this
```
**Benefits:**
- Machine secrets stored securely in Vault, not in Terraform state
- Secrets remain stable across Terraform runs
- Machine configuration never stored in state
- Deterministic infrastructure state
- Improved security and compliance
================================================
FILE: templates/guides/version-0.2-upgrade.html.md
================================================
---
page_title: "Terraform Talos Provider Version 0.2 Upgrade Guide"
description: |-
Terraform Talos Provider Version 0.2 Upgrade Guide
---
# Terraform Talos Provider Version 0.2 Upgrade Guide
Version 0.2 of the Talos Terraform provider is a major release and include some breaking chages. This guide will walk you through the changes and how to upgrade your Terraform configuration.
~> **NOTE:** Version 0.2 of the Talos Terraform provider drops support for the following resources:
> * `talos_client_configuration`
> * `talos_cluster_kubeconfig`
> * `talos_machine_configuration_controlplane`
> * `talos_machine_configuration_worker`
The following table lists the resources that have been removed and the new resources that replace them.
| Removed Resource | Type | New Resource | Type |
| ------------------------------------------ | -------- | ----------------------------- | ------------- |
| `talos_client_configuration` | Resource | `talos_client_configuration` | Data Source |
| `talos_cluster_kubeconfig` | Resource | `talos_cluster_kubeconfig` | Data Source |
| `talos_machine_configuration_controlplane` | Resource | `talos_machine_configuration` | Data Resource |
| `talos_machine_configuration_worker` | Resource | `talos_machine_configuration` | Data Resource |
## Upgrade topics:
- [Upgrading `talos_client_configuration` resource](#upgrading-talos_client_configuration-resource)
- [Upgrading `talos_cluster_kubeconfig` resource](#upgrading-talos_cluster_kubeconfig-resource)
- [Upgrading `talos_machine_configuration_controlplane` resource](#upgrading-talos_machine_configuration_controlplane-resource)
- [Upgrading `talos_machine_configuration_worker` resource](#upgrading-talos_machine_configuration_worker-resource)
### Upgrading `talos_client_configuration` resource
The `talos_client_configuration` resource has been removed. The `talos_client_configuration` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "talosconfig" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
```
`talos_client_configuration` resource should be first removed from the state:
```bash
terraform state rm talos_client_configuration.talosconfig
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_client_configuration" "this" {
cluster_name = "example-cluster"
client_configuration = talos_machine_secrets.this.client_configuration
}
```
### Upgrading `talos_cluster_kubeconfig` resource
The `talos_cluster_kubeconfig` resource has been removed. The `talos_cluster_kubeconfig` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_cluster_kubeconfig" "kubeconfig" {
talos_config = talos_client_configuration.this.talos_config
endpoint = "10.5.0.2"
node = "10.5.0.2"
}
```
`talos_cluster_kubeconfig` resource should be first removed from the state:
```bash
terraform state rm talos_cluster_kubeconfig.kubeconfig
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_cluster_kubeconfig" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
node = "10.5.0.2"
}
```
### Upgrading `talos_machine_configuration_controlplane` resource
The `talos_machine_configuration_controlplane` resource has been removed. The `talos_machine_configuration` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_controlplane" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
talos_config = talos_client_configuration.this.talos_config
machine_configuration = talos_machine_configuration_controlplane.this.machine_config
node = "10.5.0.2"
endpoint = "10.5.0.2"
}
```
`talos_machine_configuration_controlplane` resource should be first removed from the state:
```bash
terraform state rm talos_machine_configuration_controlplane.cp
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "controlplane"
talos_version = talos_machine_secrets.this.talos_version
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.2"
}
```
### Upgrading `talos_machine_configuration_worker` resource
The `talos_machine_configuration_worker` resource has been removed. The `talos_machine_configuration` data source should be used instead.
For example if the following resource was used:
```hcl
resource "talos_machine_secrets" "this" {}
resource "talos_client_configuration" "this" {
cluster_name = "example-cluster"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_worker" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
talos_config = talos_client_configuration.this.talos_config
machine_configuration = talos_machine_configuration_worker.this.machine_config
node = "10.5.0.3"
endpoint = "10.5.0.3"
}
```
`talos_machine_configuration_worker` resource should be first removed from the state:
```bash
terraform state rm talos_machine_configuration_worker.worker
```
and the code should be updated to:
```hcl
resource "talos_machine_secrets" "machine_secrets" {}
data "talos_machine_configuration" "this" {
cluster_name = "example-cluster"
cluster_endpoint = "https://10.5.0.2:6443"
machine_type = "worker"
talos_version = talos_machine_secrets.this.talos_version
machine_secrets = talos_machine_secrets.this.machine_secrets
}
resource "talos_machine_configuration_apply" "this" {
client_configuration = talos_machine_secrets.this.client_configuration
machine_configuration_input = data.talos_machine_configuration.this.machine_configuration
node = "10.5.0.3"
}
```
================================================
FILE: templates/index.md.tmpl
================================================
---
page_title: "Provider: Talos"
description: |-
The Talos provider is used to manage a Talos cluster config generation and initial setup.
---
# Talos Provider
Talos provider allows to generate configs for a Talos cluster and apply them to the nodes, bootstrap nodes, check cluster health, and retrieve `kubeconfig` and `talosconfig`.
Complete usages for this provider across a variety of environments can be found [here](https://github.com/siderolabs/contrib/tree/main/examples/terraform).
================================================
FILE: templates/resources.md.tmpl
================================================
---
page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}"
subcategory: ""
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# {{.Name}} ({{.Type}})
{{ .Description | trimspace }}
{{ if .HasExample -}}
## Example Usage
{{ tffile (printf .ExampleFile) }}
{{- end }}
{{ .SchemaMarkdown | trimspace }}
{{ if .HasImport -}}
## Import
Import is supported using the following syntax:
{{ tffile (printf .ImportFile) }}
{{- end }}
================================================
FILE: terraform-registry-manifest.json
================================================
{
"version": 1,
"metadata": {
"protocol_versions": [
"5.0"
]
}
}
================================================
FILE: tools/go.mod
================================================
module github.com/siderolabs/terraform-provider-talos/tools
go 1.26.1
tool (
github.com/anchore/syft/cmd/syft
golang.org/x/vuln/cmd/govulncheck
)
require github.com/hashicorp/terraform-plugin-docs v0.24.0
require (
cel.dev/expr v0.25.1 // indirect
cloud.google.com/go v0.123.0 // indirect
cloud.google.com/go/auth v0.18.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
cloud.google.com/go/iam v1.5.3 // indirect
cloud.google.com/go/monitoring v1.24.3 // indirect
cloud.google.com/go/storage v1.60.0 // indirect
cyphar.com/go-pathrs v0.2.1 // indirect
dario.cat/mergo v1.0.2 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/CycloneDX/cyclonedx-go v0.10.0 // indirect
github.com/DataDog/zstd v1.5.7 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.14.0-rc.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v1.4.0 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/acobaugh/osrelease v0.1.0 // indirect
github.com/adrg/xdg v0.5.3 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf // indirect
github.com/anchore/clio v0.0.0-20250715152405-a0fa658e5084 // indirect
github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232 // indirect
github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c // indirect
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d // indirect
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 // indirect
github.com/anchore/go-lzo v0.1.0 // indirect
github.com/anchore/go-macholibre v0.0.0-20250320151634-807da7ad2331 // indirect
github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec // indirect
github.com/anchore/go-struct-converter v0.1.0 // indirect
github.com/anchore/go-sync v0.0.0-20250714163430-add63db73ad1 // indirect
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 // indirect
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 // indirect
github.com/anchore/stereoscope v0.1.22 // indirect
github.com/anchore/syft v1.42.3 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aquasecurity/go-pep440-version v0.0.1 // indirect
github.com/aquasecurity/go-version v0.0.1 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.2 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.10 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.10 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect
github.com/aws/smithy-go v1.24.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/bitnami/go-version v0.0.0-20250505154626-452e8c5ee607 // indirect
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/bubbles v1.0.0 // indirect
github.com/charmbracelet/bubbletea v1.3.10 // indirect
github.com/charmbracelet/colorprofile v0.4.1 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.10.0 // indirect
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/containerd/cgroups/v3 v3.1.2 // indirect
github.com/containerd/containerd/api v1.10.0 // indirect
github.com/containerd/containerd/v2 v2.2.1 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.2 // indirect
github.com/containerd/plugin v1.0.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb // indirect
github.com/diskfs/go-diskfs v1.7.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v29.3.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.5 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elliotchance/phpserialize v1.4.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/facebookincubator/nvdtools v0.1.5 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/github/go-spdx/v2 v2.4.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.8.0 // indirect
github.com/go-git/go-git/v5 v5.17.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gohugoio/hashstructure v0.6.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.21.2 // indirect
github.com/google/licensecheck v0.3.1 // indirect
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
github.com/gookit/color v1.6.0 // indirect
github.com/gpustack/gguf-parser-go v0.24.0 // indirect
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.70 // indirect
github.com/hashicorp/cli v1.1.7 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.8.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.8.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hc-install v0.9.2 // indirect
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
github.com/hashicorp/terraform-exec v0.24.0 // indirect
github.com/hashicorp/terraform-json v0.27.2 // indirect
github.com/henvic/httpretty v0.1.4 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jedib0t/go-pretty/v6 v6.7.8 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mholt/archives v0.1.5 // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/moby/api v1.54.0 // indirect
github.com/moby/moby/client v0.3.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 // indirect
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.2.0 // indirect
github.com/olekukonko/ll v0.1.6 // indirect
github.com/olekukonko/tablewriter v1.1.4 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.3.0 // indirect
github.com/opencontainers/selinux v1.13.1 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pjbgf/sha1cd v0.4.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pkg/xattr v0.4.12 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
github.com/sassoftware/go-rpmutils v0.4.0 // indirect
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d // indirect
github.com/sorairolake/lzip-go v0.3.8 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb // indirect
github.com/spdx/tools-golang v0.5.7 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.24.0 // indirect
github.com/sylabs/squashfs v1.0.6 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/vbatts/go-mtree v0.7.0 // indirect
github.com/vbatts/tar-split v0.12.2 // indirect
github.com/vifraa/gopom v1.0.0 // indirect
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 // indirect
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yuin/goldmark v1.7.7 // indirect
github.com/yuin/goldmark-meta v1.1.0 // indirect
github.com/zclconf/go-cty v1.17.0 // indirect
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 // indirect
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
golang.org/x/mod v0.34.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c // indirect
golang.org/x/term v0.41.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.43.0 // indirect
golang.org/x/vuln v1.1.4 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/api v0.267.0 // indirect
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.67.6 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.46.1 // indirect
)
================================================
FILE: tools/go.sum
================================================
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY=
cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw=
cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=
cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=
cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=
cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.60.0 h1:oBfZrSOCimggVNz9Y/bXY35uUcts7OViubeddTTVzQ8=
cloud.google.com/go/storage v1.60.0/go.mod h1:q+5196hXfejkctrnx+VYU8RKQr/L3c0cBIlrjmiAKE0=
cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=
cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.10.0 h1:7xyklU7YD+CUyGzSFIARG18NYLsKVn4QFg04qSsu+7Y=
github.com/CycloneDX/cyclonedx-go v0.10.0/go.mod h1:vUvbCXQsEm48OI6oOlanxstwNByXjCZ2wuleUlwGEO8=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0=
github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ=
github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ=
github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf h1:UY7SQkfVVaeGUpPZrJxqmTc8M0ZSWc5ChiKF6I6aL3I=
github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf/go.mod h1:w8Br1ZKk1Nk82YRSh10pcD7LO7avPyFmNnaY1TRPgs0=
github.com/anchore/clio v0.0.0-20250715152405-a0fa658e5084 h1:7DUAXEdAxoANPlDgxYiaSRKnWnTygvdrrWhnmvEjNLg=
github.com/anchore/clio v0.0.0-20250715152405-a0fa658e5084/go.mod h1:42dWox8z4//b898OIELsQnSdYq9q1aCXkwp5fKF+BEU=
github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232 h1:aVC6r9h5wGNh8BYTW3CXxOdPoZzY/bBRWne1NvSTlO8=
github.com/anchore/fangs v0.0.0-20250716230140-94c22408c232/go.mod h1:Zees1AEKNpXIRgdVAMYWITncarLFiPOtEQ7rl45V/h0=
github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c h1:eoJXyC0n7DZ4YvySG/ETdYkTar2Due7eH+UmLK6FbrA=
github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc=
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d/go.mod h1:PhSnuFYknwPZkOWKB1jXBNToChBA+l0FjwOxtViIc50=
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 h1:2SqmFgE7h+Ql4VyBzhjLkRF/3gDrcpUBj8LjvvO6OOM=
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722/go.mod h1:oFuE8YuTCM+spgMXhePGzk3asS94yO9biUfDzVTFqNw=
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
github.com/anchore/go-macholibre v0.0.0-20250320151634-807da7ad2331 h1:fWPHXkH3FQGVCyPkFMqNvMjQvdNMfkylBTsDqZC4lE4=
github.com/anchore/go-macholibre v0.0.0-20250320151634-807da7ad2331/go.mod h1:DYvTRnWrlJ//6YOR83SiewmJiNFDEMRaOTnrzgco9FA=
github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec h1:SjjPMOXTzpuU1ZME4XeoHyek+dry3/C7I8gzaCo02eg=
github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec/go.mod h1:eQVa6QFGzKy0qMcnW2pez0XBczvgwSjw9vA23qifEyU=
github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=
github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/anchore/go-sync v0.0.0-20250714163430-add63db73ad1 h1:UK1SWZf2xD5jq8QVeDdpt6wW31cO3RckBvPmGlDrTkg=
github.com/anchore/go-sync v0.0.0-20250714163430-add63db73ad1/go.mod h1:hd0Ol9qFM8tRDdF50a+DpZEoB0HFNaEnCp/BSVyBRlg=
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE+o2gozGEBoUMpX27lsku+xrMwlmBZJtbg=
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.1.22 h1:L807G/kk0WZzOCGuRGF7knxMKzwW2PGdbPVRystryd8=
github.com/anchore/stereoscope v0.1.22/go.mod h1:FikPtAb/WnbqwgLHAvQA9O+fWez0K4pbjxzghz++iy4=
github.com/anchore/syft v1.42.3 h1:eIeeGyqfXm/C8wpBWU50xFbOjdL37VbLatMj9nEJ6n4=
github.com/anchore/syft v1.42.3/go.mod h1:i2PZ+276IdPcnd/n32aeIv849iO/QqdjRknbIc39yL0=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/aquasecurity/go-pep440-version v0.0.1 h1:8VKKQtH2aV61+0hovZS3T//rUF+6GDn18paFTVS0h0M=
github.com/aquasecurity/go-pep440-version v0.0.1/go.mod h1:3naPe+Bp6wi3n4l5iBFCZgS0JG8vY6FT0H4NGhFJ+i4=
github.com/aquasecurity/go-version v0.0.1 h1:4cNl516agK0TCn5F7mmYN+xVs1E3S45LkgZk3cbaW2E=
github.com/aquasecurity/go-version v0.0.1/go.mod h1:s1UU6/v2hctXcOa3OLwfj5d9yoXHa3ahf+ipSwEvGT0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls=
github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI=
github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw=
github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8=
github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R50aTBHkA7vu0lK+k=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g=
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA=
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs=
github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0=
github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitnami/go-version v0.0.0-20250505154626-452e8c5ee607 h1:lBg3tHGquFySSblLi9zNi2iGNmVLRHBzVal2fqphCM8=
github.com/bitnami/go-version v0.0.0-20250505154626-452e8c5ee607/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8=
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=
github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=
github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=
github.com/containerd/containerd/v2 v2.2.1 h1:TpyxcY4AL5A+07dxETevunVS5zxqzuq7ZqJXknM11yk=
github.com/containerd/containerd/v2 v2.2.1/go.mod h1:NR70yW1iDxe84F2iFWbR9xfAN0N2F0NcjTi1OVth4nU=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=
github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb h1:4W/2rQ3wzEimF5s+J6OY3ODiQtJZ5W1sForSgogVXkY=
github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk=
github.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY=
github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk=
github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ=
github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.4.0 h1:+4IwVwJJbm3rzvrQ6P1nI9BDMcy3la4RchRy5uehV/M=
github.com/github/go-spdx/v2 v2.4.0/go.mod h1:/5rwgS0txhGtRdUZwc02bTglzg6HK3FfuEbECKlK2Sg=
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-snaps v0.5.20 h1:FGKonEeQPJ12t7RQj6cTPa881fl5c8HYarMLv5vP7sg=
github.com/gkampitakis/go-snaps v0.5.20/go.mod h1:gC3YqxQTPyIXvQrw/Vpt3a8VqR1MO8sVpZFWN4DGwNs=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM=
github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg=
github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.21.2 h1:vYaMU4nU55JJGFC9JR/s8NZcTjbE9DBBbvusTW9NeS0=
github.com/google/go-containerregistry v0.21.2/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/gpustack/gguf-parser-go v0.24.0 h1:tdJceXYp9e5RhE9RwVYIuUpir72Jz2D68NEtDXkKCKc=
github.com/gpustack/gguf-parser-go v0.24.0/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.70 h1:0HADrxxqaQkGycO1JoUUA+B4FnIkuo8d2bz/hSaTFFQ=
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.70/go.mod h1:fm2FdDCzJdtbXF7WKAMvBb5NEPouXPHFbGNYs9ShFns=
github.com/hashicorp/cli v1.1.7 h1:/fZJ+hNdwfTSfsxMBa9WWMlfjUZbX8/LnUxgAd7lCVU=
github.com/hashicorp/cli v1.1.7/go.mod h1:e6Mfpga9OCT1vqzFuoGZiiF/KaG9CbUfO5s3ghU3YgU=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.8.5 h1:DMPV5CSw5JrNg/IK7kDZt3+l2REKXOi3oAw7uYLh2NM=
github.com/hashicorp/go-getter v1.8.5/go.mod h1:WIffejwAyDSJhoVptc3UEshEMkR9O63rw34V7k43O3Q=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hc-install v0.9.2 h1:v80EtNX4fCVHqzL9Lg/2xkp62bbvQMnvPQ0G+OmtO24=
github.com/hashicorp/hc-install v0.9.2/go.mod h1:XUqBQNnuT4RsxoxiM9ZaUk0NX8hi2h+Lb6/c0OZnC/I=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hashicorp/terraform-exec v0.24.0 h1:mL0xlk9H5g2bn0pPF6JQZk5YlByqSqrO5VoaNtAf8OE=
github.com/hashicorp/terraform-exec v0.24.0/go.mod h1:lluc/rDYfAhYdslLJQg3J0oDqo88oGQAdHR+wDqFvo4=
github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU=
github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE=
github.com/hashicorp/terraform-plugin-docs v0.24.0 h1:YNZYd+8cpYclQyXbl1EEngbld8w7/LPOm99GD5nikIU=
github.com/hashicorp/terraform-plugin-docs v0.24.0/go.mod h1:YLg+7LEwVmRuJc0EuCw0SPLxuQXw5mW8iJ5ml/kvi+o=
github.com/henvic/httpretty v0.1.4 h1:Jo7uwIRWVFxkqOnErcoYfH90o3ddQyVrSANeS4cxYmU=
github.com/henvic/httpretty v0.1.4/go.mod h1:Dn60sQTZfbt2dYsdUSNsCljyF4AfdqnuJFDLJA1I4AM=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.7.8 h1:BVYrDy5DPBA3Qn9ICT+PokP9cvCv1KaHv2i+Hc8sr5o=
github.com/jedib0t/go-pretty/v6 v6.7.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 h1:WdAeg/imY2JFPc/9CST4bZ80nNJbiBFCAdSZCSgrS5Y=
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953/go.mod h1:6o+UrvuZWc4UTyBhQf0LGjW9Ld7qJxLz/OqvSOWWlEc=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=
github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=
github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+EDG4s40hMwE//d5SBgDjUOrITReV2u4aA=
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0=
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrpz5K6Vw=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.4.0 h1:NXzbL1RvjTUi6kgYZCX3fPwwl27Q1LJndxtUDVfJGRY=
github.com/pjbgf/sha1cd v0.4.0/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c h1:8gOLsYwaY2JwlTMT4brS5/9XJdrdIbmk2obvQ748CC0=
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c/go.mod h1:kwM/7r/rVluTE8qJbHAffduuqmSv4knVQT2IajGvSiA=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc=
github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc=
github.com/sebdah/goldie/v2 v2.8.0/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d h1:3VwvTjiRPA7cqtgOWddEL+JrcijMlXUmj99c/6YyZoY=
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0=
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb h1:7G2Czq97VORM5xNRrD8tSQdhoXPRs8s+Otlc7st9TS0=
github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/sylabs/sif/v2 v2.24.0 h1:1wB5uMDUQYjk8AckTySaDcP9YnpMb1LyDRr1Jt9A10w=
github.com/sylabs/sif/v2 v2.24.0/go.mod h1:DbXWqWZ1hdLSU+K9ipdds5AmZeHWsyxCOj/oQakBa88=
github.com/sylabs/squashfs v1.0.6 h1:PvJcDzxr+vIm2kH56mEMbaOzvGu79gK7P7IX+R7BDZI=
github.com/sylabs/squashfs v1.0.6/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE=
github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo=
github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E=
github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY=
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0 h1:EHsPe0Q0ANoLOZff1dBLAyeWLTA4sbPTpGI+2zb0FnM=
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0/go.mod h1:g/D9uEUFp5YLyciwCpVsSOZOm56hfv4rzGJod6MlqIM=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
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/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 h1:V+UsotZpAVvfj3X/LMoEytoLzSiP6Lg0F7wdVyu9gGg=
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis=
go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw=
go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0 h1:5gn2urDL/FBnK8OkCfD1j3/ER79rUuTYmCvlXBKeYL8=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0/go.mod h1:0fBG6ZJxhqByfFZDwSwpZGzJU671HkwpWaNe2t4VUPI=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc=
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=
golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
google.golang.org/api v0.267.0 h1:w+vfWPMPYeRs8qH1aYYsFX68jMls5acWl/jocfLomwE=
google.golang.org/api v0.267.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0=
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
================================================
FILE: tools.go
================================================
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build tools
package tools
import (
// document generation
_ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
)