├── .github
└── workflows
│ └── main.yaml
├── .gitignore
├── .goreleaser.yml
├── .vscode
└── launch.json
├── LICENSE
├── Makefile
├── README.md
├── docs
├── data-sources
│ └── config.md
├── index.md
└── resources
│ ├── config.md
│ ├── config_block.md
│ ├── config_block_tree.md
│ └── static_host_mapping.md
├── examples
├── provider
│ └── provider.tf
└── resources
│ ├── vyos_config
│ ├── import.sh
│ └── resource.tf
│ ├── vyos_config_block
│ ├── import.sh
│ └── resource.tf
│ ├── vyos_config_block_tree
│ ├── import.sh
│ └── resource.tf
│ └── vyos_static_host_mapping
│ └── resource.tf
├── go.mod
├── go.sum
├── main.go
├── terraform-registry-manifest.json
├── tools
└── tools.go
└── vyos
├── data_source_config.go
├── provider.go
├── resource_config.go
├── resource_config_block.go
├── resource_config_block_tree.go
└── resource_static_host_mapping.go
/.github/workflows/main.yaml:
--------------------------------------------------------------------------------
1 | name: release
2 | on:
3 | push:
4 | tags:
5 | - 'v*'
6 | jobs:
7 | goreleaser:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
11 | with:
12 | # Allow goreleaser to access older tag information.
13 | fetch-depth: 0
14 |
15 | - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
16 | with:
17 | go-version-file: 'go.mod'
18 | cache: true
19 |
20 | - name: Import GPG key
21 | id: import_gpg
22 | uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
23 | with:
24 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
25 | passphrase: ${{ secrets.GPG_PASSPHRASE }}
26 |
27 | - name: Run GoReleaser
28 | uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
29 | with:
30 | args: release --clean
31 | env:
32 | # GitHub sets the GITHUB_TOKEN secret automatically.
33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 |
3 | **/.terraform/*
4 | *.tfstate
5 | *.tfstate.*
6 | .terraform.lock.hcl
7 | terraform-provider-vyos
8 | __debug_bin
9 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | # Visit https://goreleaser.com for documentation on how to customize this
2 | # behavior.
3 | version: 2
4 | before:
5 | hooks:
6 | # this is just an example and not a requirement for provider building/publishing
7 | - go mod tidy
8 | builds:
9 | - env:
10 | # goreleaser does not work with CGO, it could also complicate
11 | # usage by users in CI/CD systems like HCP Terraform where
12 | # they are unable to install libraries.
13 | - CGO_ENABLED=0
14 | mod_timestamp: '{{ .CommitTimestamp }}'
15 | flags:
16 | - -trimpath
17 | ldflags:
18 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}'
19 | goos:
20 | - freebsd
21 | - windows
22 | - linux
23 | - darwin
24 | goarch:
25 | - amd64
26 | - '386'
27 | - arm
28 | - arm64
29 | ignore:
30 | - goos: darwin
31 | goarch: '386'
32 | binary: '{{ .ProjectName }}_v{{ .Version }}'
33 | archives:
34 | - formats: [zip]
35 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
36 | checksum:
37 | extra_files:
38 | - glob: 'terraform-registry-manifest.json'
39 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
40 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
41 | algorithm: sha256
42 | signs:
43 | - artifacts: checksum
44 | args:
45 | # if you are using this in a GitHub action or some other automated pipeline, you
46 | # need to pass the batch flag to indicate its not interactive.
47 | - "--batch"
48 | - "--local-user"
49 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key
50 | - "--output"
51 | - "${signature}"
52 | - "--detach-sign"
53 | - "${artifact}"
54 | release:
55 | extra_files:
56 | - glob: 'terraform-registry-manifest.json'
57 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
58 | # If you want to manually examine the release before its live, uncomment this line:
59 | # draft: true
60 | changelog:
61 | disable: true
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 |
5 | {
6 | "name": "Debug Terraform Provider",
7 | "type": "go",
8 | "request": "launch",
9 | "mode": "debug",
10 | // this assumes your workspace is the root of the repo
11 | "program": "${workspaceFolder}",
12 | "env": {},
13 | "args": [
14 | "-debug",
15 | ]
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022 Jack Foltz
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | HOSTNAME=github.com
2 | NAMESPACE=foltik
3 | NAME=vyos
4 | BINARY=terraform-provider-${NAME}
5 | VERSION=0.3.1
6 | OS_ARCH=linux_amd64
7 |
8 | default: install
9 |
10 | doc:
11 | go generate
12 |
13 | build:
14 | go build -o ${BINARY}
15 |
16 | release:
17 | GOOS=linux GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_linux_amd64
18 | GOOS=linux GOARCH=arm64 go build -o ./bin/${BINARY}_${VERSION}_linux_arm64
19 | GOOS=linux GOARCH=arm go build -o ./bin/${BINARY}_${VERSION}_linux_arm
20 | GOOS=darwin GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_darwin_amd64
21 | GOOS=darwin GOARCH=arm64 go build -o ./bin/${BINARY}_${VERSION}_darwin_arm64
22 | GOOS=windows GOARCH=amd64 go build -o ./bin/${BINARY}_${VERSION}_windows_amd64
23 |
24 | install: build
25 | mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
26 | cp -a ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH}
27 |
28 | test:
29 | go test ./...
30 |
31 | debug: build
32 | ./${BINARY} -debug
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terraform Provider for VyOS
2 |
--------------------------------------------------------------------------------
/docs/data-sources/config.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "vyos_config Data Source - terraform-provider-vyos"
4 | subcategory: ""
5 | description: |-
6 |
7 | ---
8 |
9 | # vyos_config (Data Source)
10 |
11 |
12 |
13 |
14 |
15 |
16 | ## Schema
17 |
18 | ### Required
19 |
20 | - **key** (String)
21 |
22 | ### Optional
23 |
24 | - **id** (String) The ID of this resource.
25 | - **timeouts** (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
26 |
27 | ### Read-Only
28 |
29 | - **value** (String)
30 |
31 |
32 | ### Nested Schema for `timeouts`
33 |
34 | Optional:
35 |
36 | - **default** (String)
37 | - **read** (String)
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "vyos Provider"
4 | subcategory: ""
5 | description: |-
6 |
7 | ---
8 |
9 | # vyos Provider
10 |
11 |
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | terraform {
17 | required_providers {
18 | vyos = {
19 | version = "0.x.x"
20 | source = "foltik/vyos"
21 | }
22 | }
23 | }
24 |
25 | provider "vyos" {
26 | url = "https://vyos.local"
27 | key = "xxxxxxxxx"
28 | }
29 | ```
30 |
31 |
32 | ## Schema
33 |
34 | ### Required
35 |
36 | - **key** (String, Sensitive)
37 | - **url** (String)
38 |
39 | ### Optional
40 |
41 | - **cert** (String)
42 | - **save** (Boolean) Save after making changes in Vyos
43 | - **save_file** (String) File to save configuration. Uses config.boot by default.
44 |
--------------------------------------------------------------------------------
/docs/resources/config.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "vyos_config Resource - terraform-provider-vyos"
4 | subcategory: ""
5 | description: |-
6 | This resource manages a single configuration value. This as well as vyosconfigblock can act as a fallback when a dedicated resource does not exist.
7 | ---
8 |
9 | # vyos_config (Resource)
10 |
11 | This resource manages a single configuration value. This as well as vyos_config_block can act as a fallback when a dedicated resource does not exist.
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | # Performs "set system host-name vyos"
17 | resource "vyos_config" "hostname" {
18 | key = "system host-name"
19 | value = "vyos"
20 | }
21 | ```
22 |
23 |
24 | ## Schema
25 |
26 | ### Required
27 |
28 | - **key** (String) Config path separated by spaces.
29 | - **value** (String) Config value.
30 |
31 | ### Optional
32 |
33 | - **timeouts** (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
34 |
35 | ### Read-Only
36 |
37 | - **id** (String) The resource ID, same as the `key`
38 |
39 |
40 | ### Nested Schema for `timeouts`
41 |
42 | Optional:
43 |
44 | - **create** (String)
45 | - **default** (String)
46 | - **delete** (String)
47 | - **read** (String)
48 | - **update** (String)
49 |
50 | ## Import
51 |
52 | Import is supported using the following syntax:
53 |
54 | ```shell
55 | terraform import vyos_config.hostname "system host-name"
56 | ```
57 |
--------------------------------------------------------------------------------
/docs/resources/config_block.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "vyos_config_block Resource - terraform-provider-vyos"
4 | subcategory: ""
5 | description: |-
6 | This resource is useful when a single command is not enough for a valid config commit. This as well as vyos_config can act as a fallback when a dedicated resource does not exist.
7 | ---
8 |
9 | # vyos_config_block (Resource)
10 |
11 | This resource is useful when a single command is not enough for a valid config commit. This as well as vyos_config can act as a fallback when a dedicated resource does not exist.
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | resource "vyos_config_block" "allow_sith_supremacy" {
17 | path = "firewall name Empire-Senate rule 66"
18 |
19 | configs = {
20 | "action" = "accept"
21 | "description" = "For a safe and secure society"
22 | "log" = "disable"
23 | }
24 | }
25 |
26 | resource "vyos_config_block" "allow_sith_supremacy_jedi" {
27 | path = "firewall name Empire-Senate rule 66 destination group"
28 |
29 | configs = {
30 | "port-group" = "Jedi"
31 | }
32 |
33 | depends_on = [vyos_config_block.allow_sith_supremacy]
34 | }
35 | ```
36 |
37 |
38 | ## Schema
39 |
40 | ### Required
41 |
42 | - **configs** (Map of String) Key/Value map of config parameters.
43 | - **path** (String) Config path seperated by spaces.
44 |
45 | ### Optional
46 |
47 | - **timeouts** (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
48 |
49 | ### Read-Only
50 |
51 | - **id** (String) The resource ID, same as the `path`
52 |
53 |
54 | ### Nested Schema for `timeouts`
55 |
56 | Optional:
57 |
58 | - **create** (String)
59 | - **default** (String)
60 | - **delete** (String)
61 | - **read** (String)
62 | - **update** (String)
63 |
64 | ## Import
65 |
66 | Import is supported using the following syntax:
67 |
68 | ```shell
69 | terraform import vyos_config_block.allow_sith_supremacy "firewall name Empire-Senate rule 66"
70 | ```
71 |
--------------------------------------------------------------------------------
/docs/resources/config_block_tree.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "vyos_config_block_tree Resource - terraform-provider-vyos"
4 | subcategory: ""
5 | description: |-
6 | This resource is useful when a single command is not enough for a valid config commit and children paths are needed.
7 | ---
8 |
9 | # vyos_config_block_tree (Resource)
10 |
11 | This resource is useful when a single command is not enough for a valid config commit and children paths are needed.
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | resource "vyos_config_block" "allow_sith_supremacy" {
17 | path = "firewall name Empire-Senate rule 66"
18 |
19 | configs = {
20 | "action" = "accept"
21 | "description" = "For a safe and secure society"
22 | "log" = "disable"
23 | "destination group port-group": "Jedi"
24 | }
25 | }
26 |
27 | resource "vyos_config_block_tree" "ssh" {
28 | path = "service ssh"
29 |
30 | configs = {
31 | "port" = "22",
32 | "disable-password-authentication" = "", #Keep simple passwords for login via terminal but require key for ssh
33 | "listen-address" = jsonencode(["192.168.2.1", "192.168.63.1"]) # Listen in LAN and management interface
34 | }
35 | }
36 | ```
37 |
38 |
39 | ## Schema
40 |
41 | ### Required
42 |
43 | - **configs** (Map of String) Key/Value map of config parameters. Value can be a jsonencode list
44 | - **path** (String) Config path seperated by spaces.
45 |
46 | ### Optional
47 |
48 | - **timeouts** (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
49 |
50 | ### Read-Only
51 |
52 | - **id** (String) The resource ID, same as the `path`
53 |
54 |
55 | ### Nested Schema for `timeouts`
56 |
57 | Optional:
58 |
59 | - **create** (String)
60 | - **default** (String)
61 | - **delete** (String)
62 | - **read** (String)
63 | - **update** (String)
64 |
65 | ## Import
66 |
67 | Import is supported using the following syntax:
68 |
69 | ```shell
70 | terraform import vyos_config_block_tree.allow_sith_supremacy "firewall name Empire-Senate rule 66"
71 | ```
72 |
--------------------------------------------------------------------------------
/docs/resources/static_host_mapping.md:
--------------------------------------------------------------------------------
1 | ---
2 | # generated by https://github.com/hashicorp/terraform-plugin-docs
3 | page_title: "vyos_static_host_mapping Resource - terraform-provider-vyos"
4 | subcategory: ""
5 | description: |-
6 | This resource manages a static host mapping with the given hostname and ipv4 address.
7 | ---
8 |
9 | # vyos_static_host_mapping (Resource)
10 |
11 | This resource manages a static host mapping with the given hostname and ipv4 address.
12 |
13 | ## Example Usage
14 |
15 | ```terraform
16 | # Performs "set system static-host-mapping host-name test.local inet 10.0.0.1"
17 | resource "vyos_static_host_mapping" "mapping" {
18 | host = "test.local"
19 | ip = "10.0.0.1"
20 | }
21 | ```
22 |
23 |
24 | ## Schema
25 |
26 | ### Required
27 |
28 | - **host** (String) Hostname.
29 | - **ip** (String) IPv4 address.
30 |
31 | ### Optional
32 |
33 | - **id** (String) The ID of this resource.
34 | - **timeouts** (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
35 |
36 |
37 | ### Nested Schema for `timeouts`
38 |
39 | Optional:
40 |
41 | - **create** (String)
42 | - **default** (String)
43 | - **delete** (String)
44 | - **read** (String)
45 | - **update** (String)
46 |
47 |
48 |
--------------------------------------------------------------------------------
/examples/provider/provider.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | vyos = {
4 | version = "0.x.x"
5 | source = "foltik/vyos"
6 | }
7 | }
8 | }
9 |
10 | provider "vyos" {
11 | url = "https://vyos.local"
12 | key = "xxxxxxxxx"
13 | }
14 |
--------------------------------------------------------------------------------
/examples/resources/vyos_config/import.sh:
--------------------------------------------------------------------------------
1 | terraform import vyos_config.hostname "system host-name"
2 |
--------------------------------------------------------------------------------
/examples/resources/vyos_config/resource.tf:
--------------------------------------------------------------------------------
1 | # Performs "set system host-name vyos"
2 | resource "vyos_config" "hostname" {
3 | key = "system host-name"
4 | value = "vyos"
5 | }
6 |
--------------------------------------------------------------------------------
/examples/resources/vyos_config_block/import.sh:
--------------------------------------------------------------------------------
1 | terraform import vyos_config_block.allow_sith_supremacy "firewall name Empire-Senate rule 66"
2 |
--------------------------------------------------------------------------------
/examples/resources/vyos_config_block/resource.tf:
--------------------------------------------------------------------------------
1 | resource "vyos_config_block" "allow_sith_supremacy" {
2 | path = "firewall name Empire-Senate rule 66"
3 |
4 | configs = {
5 | "action" = "accept"
6 | "description" = "For a safe and secure society"
7 | "log" = "disable"
8 | }
9 | }
10 |
11 | resource "vyos_config_block" "allow_sith_supremacy_jedi" {
12 | path = "firewall name Empire-Senate rule 66 destination group"
13 |
14 | configs = {
15 | "port-group" = "Jedi"
16 | }
17 |
18 | depends_on = [vyos_config_block.allow_sith_supremacy]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/resources/vyos_config_block_tree/import.sh:
--------------------------------------------------------------------------------
1 | terraform import vyos_config_block_tree.allow_sith_supremacy "firewall name Empire-Senate rule 66"
2 |
--------------------------------------------------------------------------------
/examples/resources/vyos_config_block_tree/resource.tf:
--------------------------------------------------------------------------------
1 | resource "vyos_config_block" "allow_sith_supremacy" {
2 | path = "firewall name Empire-Senate rule 66"
3 |
4 | configs = {
5 | "action" = "accept"
6 | "description" = "For a safe and secure society"
7 | "log" = "disable"
8 | "destination group port-group": "Jedi"
9 | }
10 | }
11 |
12 | resource "vyos_config_block_tree" "ssh" {
13 | path = "service ssh"
14 |
15 | configs = {
16 | "port" = "22",
17 | "disable-password-authentication" = "", #Keep simple passwords for login via terminal but require key for ssh
18 | "listen-address" = jsonencode(["192.168.2.1", "192.168.63.1"]) # Listen in LAN and management interface
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/resources/vyos_static_host_mapping/resource.tf:
--------------------------------------------------------------------------------
1 | # Performs "set system static-host-mapping host-name test.local inet 10.0.0.1"
2 | resource "vyos_static_host_mapping" "mapping" {
3 | host = "test.local"
4 | ip = "10.0.0.1"
5 | }
6 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/foltik/terraform-provider-vyos
2 |
3 | go 1.23.7
4 |
5 | require (
6 | github.com/foltik/vyos-client-go v0.4.3-0.20230628033509-5944c2819b30
7 | github.com/hashicorp/terraform-plugin-docs v0.21.0
8 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1
9 | )
10 |
11 | require (
12 | github.com/BurntSushi/toml v1.2.1 // indirect
13 | github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
14 | github.com/Masterminds/goutils v1.1.1 // indirect
15 | github.com/Masterminds/semver/v3 v3.2.0 // indirect
16 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect
17 | github.com/ProtonMail/go-crypto v1.1.3 // indirect
18 | github.com/agext/levenshtein v1.2.3 // indirect
19 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
20 | github.com/armon/go-radix v1.0.0 // indirect
21 | github.com/bgentry/speakeasy v0.1.0 // indirect
22 | github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
23 | github.com/cloudflare/circl v1.3.7 // indirect
24 | github.com/fatih/color v1.18.0 // indirect
25 | github.com/go-resty/resty/v2 v2.16.5 // indirect
26 | github.com/golang/protobuf v1.5.4 // indirect
27 | github.com/google/go-cmp v0.7.0 // indirect
28 | github.com/google/uuid v1.6.0 // indirect
29 | github.com/hashicorp/cli v1.1.7 // indirect
30 | github.com/hashicorp/errwrap v1.1.0 // indirect
31 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect
32 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
33 | github.com/hashicorp/go-cty v1.5.0 // indirect
34 | github.com/hashicorp/go-hclog v1.6.3 // indirect
35 | github.com/hashicorp/go-multierror v1.1.1 // indirect
36 | github.com/hashicorp/go-plugin v1.6.3 // indirect
37 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
38 | github.com/hashicorp/go-uuid v1.0.3 // indirect
39 | github.com/hashicorp/go-version v1.7.0 // indirect
40 | github.com/hashicorp/hc-install v0.9.1 // indirect
41 | github.com/hashicorp/hcl/v2 v2.23.0 // indirect
42 | github.com/hashicorp/logutils v1.0.0 // indirect
43 | github.com/hashicorp/terraform-exec v0.22.0 // indirect
44 | github.com/hashicorp/terraform-json v0.24.0 // indirect
45 | github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect
46 | github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
47 | github.com/hashicorp/terraform-registry-address v0.2.5 // indirect
48 | github.com/hashicorp/terraform-svchost v0.1.1 // indirect
49 | github.com/hashicorp/yamux v0.1.2 // indirect
50 | github.com/huandu/xstrings v1.3.3 // indirect
51 | github.com/imdario/mergo v0.3.15 // indirect
52 | github.com/mattn/go-colorable v0.1.14 // indirect
53 | github.com/mattn/go-isatty v0.0.20 // indirect
54 | github.com/mattn/go-runewidth v0.0.9 // indirect
55 | github.com/mitchellh/copystructure v1.2.0 // indirect
56 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect
57 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect
58 | github.com/mitchellh/mapstructure v1.5.0 // indirect
59 | github.com/mitchellh/reflectwalk v1.0.2 // indirect
60 | github.com/oklog/run v1.1.0 // indirect
61 | github.com/posener/complete v1.2.3 // indirect
62 | github.com/shopspring/decimal v1.3.1 // indirect
63 | github.com/spf13/cast v1.5.0 // indirect
64 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
65 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
66 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
67 | github.com/yuin/goldmark v1.7.7 // indirect
68 | github.com/yuin/goldmark-meta v1.1.0 // indirect
69 | github.com/zclconf/go-cty v1.16.2 // indirect
70 | go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
71 | golang.org/x/crypto v0.37.0 // indirect
72 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
73 | golang.org/x/mod v0.24.0 // indirect
74 | golang.org/x/net v0.39.0 // indirect
75 | golang.org/x/sync v0.13.0 // indirect
76 | golang.org/x/sys v0.32.0 // indirect
77 | golang.org/x/text v0.24.0 // indirect
78 | golang.org/x/tools v0.32.0 // indirect
79 | google.golang.org/appengine v1.6.8 // indirect
80 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
81 | google.golang.org/grpc v1.71.1 // indirect
82 | google.golang.org/protobuf v1.36.6 // indirect
83 | gopkg.in/yaml.v2 v2.3.0 // indirect
84 | gopkg.in/yaml.v3 v3.0.1 // indirect
85 | )
86 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
2 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
3 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
4 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
5 | github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0=
6 | github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc=
7 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
8 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
9 | github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
10 | github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
11 | github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
12 | github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
13 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
14 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
15 | github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
16 | github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
17 | github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
18 | github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
19 | github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
20 | github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
21 | github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
22 | github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
23 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
24 | github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
25 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
26 | github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
27 | github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
28 | github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
29 | github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
30 | github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
31 | github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
32 | github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
33 | github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
34 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
35 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
36 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
37 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
38 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
39 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
40 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
41 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
42 | github.com/foltik/vyos-client-go v0.4.3-0.20230628033509-5944c2819b30 h1:FLH0+dQgIBD3Lg6aEfpa1oLMd9drHxiZ3OVxOkRKvRk=
43 | github.com/foltik/vyos-client-go v0.4.3-0.20230628033509-5944c2819b30/go.mod h1:oFBMYnKWwFqhIXHX89oEU0IU3U5fkD9uyTfZkdn4AfM=
44 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
45 | github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
46 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
47 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
48 | github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
49 | github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
50 | github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E=
51 | github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw=
52 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
53 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
54 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
55 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
56 | github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
57 | github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
58 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
59 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
60 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
61 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
62 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
63 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
64 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
65 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
66 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
67 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
68 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
69 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
70 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
71 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
72 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
73 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
74 | github.com/hashicorp/cli v1.1.7 h1:/fZJ+hNdwfTSfsxMBa9WWMlfjUZbX8/LnUxgAd7lCVU=
75 | github.com/hashicorp/cli v1.1.7/go.mod h1:e6Mfpga9OCT1vqzFuoGZiiF/KaG9CbUfO5s3ghU3YgU=
76 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
77 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
78 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
79 | github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
80 | github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
81 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
82 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
83 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
84 | github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0=
85 | github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM=
86 | github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
87 | github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
88 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
89 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
90 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
91 | github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
92 | github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
93 | github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
94 | github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
95 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
96 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
97 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
98 | github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
99 | github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
100 | github.com/hashicorp/hc-install v0.9.1 h1:gkqTfE3vVbafGQo6VZXcy2v5yoz2bE0+nhZXruCuODQ=
101 | github.com/hashicorp/hc-install v0.9.1/go.mod h1:pWWvN/IrfeBK4XPeXXYkL6EjMufHkCK5DvwxeLKuBf0=
102 | github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
103 | github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
104 | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
105 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
106 | github.com/hashicorp/terraform-exec v0.22.0 h1:G5+4Sz6jYZfRYUCg6eQgDsqTzkNXV+fP8l+uRmZHj64=
107 | github.com/hashicorp/terraform-exec v0.22.0/go.mod h1:bjVbsncaeh8jVdhttWYZuBGj21FcYw6Ia/XfHcNO7lQ=
108 | github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4NbeATsYz8Q=
109 | github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow=
110 | github.com/hashicorp/terraform-plugin-docs v0.21.0 h1:yoyA/Y719z9WdFJAhpUkI1jRbKP/nteVNBaI3hW7iQ8=
111 | github.com/hashicorp/terraform-plugin-docs v0.21.0/go.mod h1:J4Wott1J2XBKZPp/NkQv7LMShJYOcrqhQ2myXBcu64s=
112 | github.com/hashicorp/terraform-plugin-go v0.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiypAfFQmw8aE65O2M=
113 | github.com/hashicorp/terraform-plugin-go v0.26.0/go.mod h1:+CXjuLDiFgqR+GcrM5a2E2Kal5t5q2jb0E3D57tTdNY=
114 | github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
115 | github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
116 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 h1:WNMsTLkZf/3ydlgsuXePa3jvZFwAJhruxTxP/c1Viuw=
117 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1/go.mod h1:P6o64QS97plG44iFzSM6rAn6VJIC/Sy9a9IkEtl79K4=
118 | github.com/hashicorp/terraform-registry-address v0.2.5 h1:2GTftHqmUhVOeuu9CW3kwDkRe4pcBDq0uuK5VJngU1M=
119 | github.com/hashicorp/terraform-registry-address v0.2.5/go.mod h1:PpzXWINwB5kuVS5CA7m1+eO2f1jKb5ZDIxrOPfpnGkg=
120 | github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
121 | github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
122 | github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
123 | github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
124 | github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
125 | github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
126 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
127 | github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
128 | github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
129 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
130 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
131 | github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
132 | github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
133 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
134 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
135 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
136 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
137 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
138 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
139 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
140 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
141 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
142 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
143 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
144 | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
145 | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
146 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
147 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
148 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
149 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
150 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
151 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
152 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
153 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
154 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
155 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
156 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
157 | github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
158 | github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
159 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
160 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
161 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
162 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
163 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
164 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
165 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
166 | github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
167 | github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
168 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
169 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
170 | github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
171 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
172 | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
173 | github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
174 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
175 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
176 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
177 | github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
178 | github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
179 | github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
180 | github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
181 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
182 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
183 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
184 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
185 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
186 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
187 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
188 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
189 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
190 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
191 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
192 | github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
193 | github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
194 | github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
195 | github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
196 | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
197 | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
198 | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
199 | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
200 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
201 | github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
202 | github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
203 | github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
204 | github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
205 | github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
206 | github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
207 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
208 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
209 | go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw=
210 | go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
211 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
212 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
213 | go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
214 | go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
215 | go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
216 | go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
217 | go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
218 | go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
219 | go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
220 | go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
221 | go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
222 | go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
223 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
224 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
225 | golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
226 | golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
227 | golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
228 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
229 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
230 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
231 | golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
232 | golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
233 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
234 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
235 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
236 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
237 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
238 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
239 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
240 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
241 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
242 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
243 | golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
244 | golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
245 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
246 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
247 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
248 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
249 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
250 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
251 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
252 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
253 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
254 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
255 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
256 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
257 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
258 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
259 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
260 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
261 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
262 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
263 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
264 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
265 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
266 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
267 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
268 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
269 | golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
270 | golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
271 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
272 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
273 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
274 | golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
275 | golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
276 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
277 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
278 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
279 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
280 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
281 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
282 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
283 | google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
284 | google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
285 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
286 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
287 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
288 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
289 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
290 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
291 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
292 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
293 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
294 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
295 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
296 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
297 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
298 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
299 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
5 | "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
6 |
7 | "github.com/foltik/terraform-provider-vyos/vyos"
8 |
9 | "flag"
10 | )
11 |
12 | // Generate docs
13 | //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
14 |
15 | func main() {
16 | var debug bool
17 |
18 | flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
19 | flag.Parse()
20 |
21 | opts := &plugin.ServeOpts{
22 | Debug: debug,
23 | ProviderAddr: "registry.terraform.io/foltik/vyos",
24 | ProviderFunc: func() *schema.Provider {
25 | return vyos.Provider()
26 | },
27 | }
28 |
29 | plugin.Serve(opts)
30 | }
31 |
--------------------------------------------------------------------------------
/terraform-registry-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "metadata": {
4 | "protocol_versions": ["6.0"]
5 | }
6 | }
--------------------------------------------------------------------------------
/tools/tools.go:
--------------------------------------------------------------------------------
1 | //go:build tools
2 | // +build tools
3 |
4 | // Ref: https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md
5 |
6 | package tools
7 |
8 | import (
9 | // document generation
10 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
11 | )
12 |
--------------------------------------------------------------------------------
/vyos/data_source_config.go:
--------------------------------------------------------------------------------
1 | package vyos
2 |
3 | import (
4 | "context"
5 | "strconv"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | )
11 |
12 | func dataSourceConfig() *schema.Resource {
13 | return &schema.Resource{
14 | ReadContext: dataSourceConfigRead,
15 | Schema: map[string]*schema.Schema{
16 | "key": {
17 | Type: schema.TypeString,
18 | Required: true,
19 | },
20 | "value": {
21 | Type: schema.TypeString,
22 | Computed: true,
23 | },
24 | },
25 | Timeouts: &schema.ResourceTimeout{
26 | Read: schema.DefaultTimeout(10 * time.Minute),
27 | Default: schema.DefaultTimeout(10 * time.Minute),
28 | },
29 | }
30 | }
31 |
32 | func dataSourceConfigRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
33 | p := m.(*ProviderClass)
34 | key := d.Get("key").(string)
35 |
36 | value, err := p.ShowCached(ctx, key)
37 | if err != nil {
38 | return diag.FromErr(err)
39 | }
40 |
41 | switch value := value.(type) {
42 | case string:
43 | if err := d.Set("value", value); err != nil {
44 | return diag.FromErr(err)
45 | }
46 | default:
47 | return diag.Errorf("Configuration at '%s' is not a string: %s.", key, value)
48 | }
49 |
50 | d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
51 | return diag.Diagnostics{}
52 | }
53 |
--------------------------------------------------------------------------------
/vyos/provider.go:
--------------------------------------------------------------------------------
1 | package vyos
2 |
3 | import (
4 | "context"
5 | "crypto/tls"
6 | "errors"
7 | "net/http"
8 | "strings"
9 | "sync"
10 | "time"
11 |
12 | "github.com/foltik/vyos-client-go/client"
13 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
14 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
15 | )
16 |
17 | func Provider() *schema.Provider {
18 | return &schema.Provider{
19 | Schema: map[string]*schema.Schema{
20 | "url": {
21 | Type: schema.TypeString,
22 | Required: true,
23 | },
24 | "key": {
25 | Type: schema.TypeString,
26 | Required: true,
27 | Sensitive: true,
28 | DefaultFunc: schema.EnvDefaultFunc("VYOS_KEY", nil),
29 | },
30 | "cert": {
31 | Type: schema.TypeString,
32 | Optional: true,
33 | },
34 | "save": {
35 | Type: schema.TypeBool,
36 | Optional: true,
37 | Default: true,
38 | Description: "Save after making changes in Vyos",
39 | },
40 | "save_file": {
41 | Type: schema.TypeString,
42 | Optional: true,
43 | Description: "File to save configuration. Uses config.boot by default.",
44 | },
45 | "cache": {
46 | Type: schema.TypeBool,
47 | Optional: true,
48 | Default: true,
49 | Description: "Use cache for read operations",
50 | },
51 | },
52 | ResourcesMap: map[string]*schema.Resource{
53 | "vyos_config": resourceConfig(),
54 | "vyos_config_block": resourceConfigBlock(),
55 | "vyos_config_block_tree": resourceConfigBlockTree(),
56 | "vyos_static_host_mapping": resourceStaticHostMapping(),
57 | },
58 | DataSourcesMap: map[string]*schema.Resource{
59 | "vyos_config": dataSourceConfig(),
60 | },
61 | ConfigureContextFunc: providerConfigure,
62 | }
63 | }
64 |
65 | type ProviderClass struct {
66 | schema *schema.ResourceData
67 | client *client.Client
68 |
69 | _showCacheMutex *sync.Mutex
70 | _showCache *map[string]any
71 | }
72 |
73 | func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
74 | url := d.Get("url").(string)
75 | key := d.Get("key").(string)
76 |
77 | cert := d.Get("cert").(string)
78 | c := &client.Client{}
79 |
80 | if cert != "" {
81 | return nil, diag.Errorf("TODO: Use trusted self signed certificate")
82 | } else {
83 | // Just allow self signed certificates if a trusted cert isn't specified
84 | tr := http.DefaultTransport.(*http.Transport).Clone()
85 | tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
86 | cc := &http.Client{Transport: tr, Timeout: 10 * time.Minute}
87 | c = client.NewWithClient(cc, url, key)
88 | }
89 |
90 | return &ProviderClass{d, c, &sync.Mutex{}, nil}, diag.Diagnostics{}
91 | }
92 |
93 | func (p *ProviderClass) conditionalSave(ctx context.Context) {
94 | save := p.schema.Get("save").(bool)
95 | save_file := p.schema.Get("save_file").(string)
96 |
97 | if save {
98 | if save_file == "" {
99 | p.client.Config.Save(ctx)
100 | } else {
101 | p.client.Config.SaveFile(ctx, save_file)
102 | }
103 | }
104 | }
105 |
106 | func (p *ProviderClass) ShowCached(ctx context.Context, path string) (any, error) {
107 | cache := p.schema.Get("cache").(bool)
108 |
109 | if !cache {
110 | return p.client.Config.Show(ctx, path)
111 | }
112 |
113 | p._showCacheMutex.Lock()
114 | if p._showCache == nil {
115 | c := *p.client
116 | showCache, err := c.Config.Show(ctx, "")
117 | if err != nil {
118 | return showCache, err
119 | }
120 | switch value := showCache.(type) {
121 | case map[string]any:
122 | p._showCache = &value
123 | default:
124 | return nil, errors.New("Configuration is not a map")
125 | }
126 | }
127 | p._showCacheMutex.Unlock()
128 |
129 | var val any = *p._showCache
130 | for _, component := range strings.Split(path, " ") {
131 | obj, ok := val.(map[string]any)[component]
132 | if !ok {
133 | return nil, nil
134 | }
135 | val = obj
136 | }
137 | return val, nil
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/vyos/resource_config.go:
--------------------------------------------------------------------------------
1 | package vyos
2 |
3 | import (
4 | "context"
5 | "strconv"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | )
11 |
12 | func resourceConfig() *schema.Resource {
13 | return &schema.Resource{
14 | Description: "This resource manages a single configuration value. This as well as vyos_config_block can act as a fallback when a dedicated resource does not exist.",
15 | CreateContext: resourceConfigCreate,
16 | ReadContext: resourceConfigRead,
17 | UpdateContext: resourceConfigUpdate,
18 | DeleteContext: resourceConfigDelete,
19 | Importer: &schema.ResourceImporter{
20 | StateContext: schema.ImportStatePassthroughContext,
21 | },
22 | Schema: map[string]*schema.Schema{
23 | "id": {
24 | Description: "The resource ID, same as the `key`",
25 | Type: schema.TypeString,
26 | Computed: true,
27 | },
28 | "key": {
29 | Description: "Config path separated by spaces.",
30 | Type: schema.TypeString,
31 | Required: true,
32 | },
33 | "value": {
34 | Description: "Config value.",
35 | Type: schema.TypeString,
36 | Required: true,
37 | },
38 | },
39 | Timeouts: &schema.ResourceTimeout{
40 | Create: schema.DefaultTimeout(10 * time.Minute),
41 | Read: schema.DefaultTimeout(10 * time.Minute),
42 | Update: schema.DefaultTimeout(10 * time.Minute),
43 | Delete: schema.DefaultTimeout(10 * time.Minute),
44 | Default: schema.DefaultTimeout(10 * time.Minute),
45 | },
46 | }
47 | }
48 |
49 | func resourceConfigCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
50 | p := m.(*ProviderClass)
51 | c := *p.client
52 | key, value := d.Get("key").(string), d.Get("value").(string)
53 |
54 | var diags diag.Diagnostics
55 |
56 | // Check if config already exists
57 | val, err := p.ShowCached(ctx, key)
58 | if err != nil {
59 | return diag.FromErr(err)
60 | }
61 | // Dont care about sub config blocks
62 | if val != nil {
63 | return diag.Errorf("Configuration '%s' already exists with value '%s' set, try a resource import instead.", key, val)
64 | }
65 |
66 | err = c.Config.Set(ctx, key, value)
67 | if err != nil {
68 | return diag.FromErr(err)
69 | }
70 |
71 | d.SetId(key)
72 | p.conditionalSave(ctx)
73 | return diags
74 | }
75 |
76 | func resourceConfigRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
77 | p := m.(*ProviderClass)
78 | key := d.Id()
79 |
80 | // Convert old unix timestamp style ID to key path for existing resources to support importing
81 | if _, err := strconv.Atoi(key); err == nil {
82 | key = d.Get("key").(string)
83 | d.SetId(key)
84 | }
85 |
86 | // Easiest way to allow ImportStatePassthroughContext to work is to set the path
87 | if d.Get("key") == "" {
88 | if err := d.Set("key", key); err != nil {
89 | return diag.FromErr(err)
90 | }
91 | }
92 |
93 | value, err := p.ShowCached(ctx, key)
94 | if err != nil {
95 | return diag.FromErr(err)
96 | }
97 |
98 | if err := d.Set("value", value); err != nil {
99 | return diag.FromErr(err)
100 | }
101 |
102 | return diag.Diagnostics{}
103 | }
104 |
105 | func resourceConfigUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
106 | p := m.(*ProviderClass)
107 | c := *p.client
108 | key, value := d.Get("key").(string), d.Get("value").(string)
109 |
110 | err := c.Config.Set(ctx, key, value)
111 | if err != nil {
112 | return diag.FromErr(err)
113 | }
114 |
115 | p.conditionalSave(ctx)
116 | return diag.Diagnostics{}
117 | }
118 |
119 | func resourceConfigDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
120 | p := m.(*ProviderClass)
121 | c := *p.client
122 | key := d.Get("key").(string)
123 |
124 | err := c.Config.Delete(ctx, key)
125 | if err != nil {
126 | return diag.FromErr(err)
127 | }
128 |
129 | p.conditionalSave(ctx)
130 | return diag.Diagnostics{}
131 | }
132 |
--------------------------------------------------------------------------------
/vyos/resource_config_block.go:
--------------------------------------------------------------------------------
1 | package vyos
2 |
3 | import (
4 | "context"
5 | "regexp"
6 | "time"
7 |
8 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
11 | )
12 |
13 | func resourceConfigBlock() *schema.Resource {
14 | return &schema.Resource{
15 | Description: "This resource is useful when a single command is not enough for a valid config commit. This as well as vyos_config can act as a fallback when a dedicated resource does not exist.",
16 | CreateContext: resourceConfigBlockCreate,
17 | ReadContext: resourceConfigBlockRead,
18 | UpdateContext: resourceConfigBlockUpdate,
19 | DeleteContext: resourceConfigBlockDelete,
20 | Importer: &schema.ResourceImporter{
21 | StateContext: schema.ImportStatePassthroughContext,
22 | },
23 | Schema: map[string]*schema.Schema{
24 | "id": {
25 | Description: "The resource ID, same as the `path`",
26 | Type: schema.TypeString,
27 | Computed: true,
28 | },
29 | "path": {
30 | Description: "Config path seperated by spaces.",
31 | Type: schema.TypeString,
32 | Required: true,
33 | ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace),
34 | ForceNew: true,
35 | },
36 | "configs": {
37 | Description: "Key/Value map of config parameters.",
38 | Type: schema.TypeMap,
39 | Elem: &schema.Schema{
40 | Type: schema.TypeString,
41 | },
42 | Required: true,
43 | ValidateDiagFunc: validation.MapKeyMatch(regexp.MustCompile("^[^ ]+$"), "Config keys can not contain whitespace"),
44 | },
45 | },
46 | Timeouts: &schema.ResourceTimeout{
47 | Create: schema.DefaultTimeout(10 * time.Minute),
48 | Read: schema.DefaultTimeout(10 * time.Minute),
49 | Update: schema.DefaultTimeout(10 * time.Minute),
50 | Delete: schema.DefaultTimeout(10 * time.Minute),
51 | Default: schema.DefaultTimeout(10 * time.Minute),
52 | },
53 | }
54 | }
55 |
56 | func resourceConfigBlockCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
57 | var diags diag.Diagnostics
58 |
59 | p := m.(*ProviderClass)
60 | client := *p.client
61 | path := d.Get("path").(string)
62 |
63 | // Check if config already exists
64 | configs, err := p.ShowCached(ctx, path)
65 | if err != nil {
66 | return diag.FromErr(err)
67 | }
68 |
69 | // Dont care about sub config blocks
70 | if configs != nil {
71 | return diag.Errorf("Configuration '%s' already exists with value '%s' set, try a resource import instead.", path, configs)
72 | }
73 |
74 | configs = d.Get("configs").(map[string]interface{})
75 |
76 | err = client.Config.Set(ctx, path, configs)
77 | if err != nil {
78 | return diag.FromErr(err)
79 | }
80 |
81 | d.SetId(path)
82 | p.conditionalSave(ctx)
83 | return diags
84 | }
85 |
86 | func resourceConfigBlockRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
87 | var diags diag.Diagnostics
88 |
89 | p := m.(*ProviderClass)
90 | path := d.Id()
91 |
92 | configs, err := p.ShowCached(ctx, path)
93 | if err != nil {
94 | return diag.FromErr(err)
95 | }
96 |
97 | switch value := configs.(type) {
98 | case map[string]any:
99 | if err := d.Set("configs", value); err != nil {
100 | return diag.FromErr(err)
101 | }
102 | return diags
103 | default:
104 | return diag.Errorf("Configuration at '%s' is not a string: %s.", path, value)
105 | }
106 |
107 | // // Remove child blocks of config
108 | // for attr, val := range configs {
109 | // switch val.(type) {
110 | // default:
111 | // delete(configs, attr)
112 | // case string:
113 | // continue
114 | // case int:
115 | // continue
116 | // }
117 | // }
118 |
119 | }
120 |
121 | func resourceConfigBlockUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
122 | var diags diag.Diagnostics
123 |
124 | p := m.(*ProviderClass)
125 | c := *p.client
126 |
127 | path := d.Get("path").(string)
128 | o, n := d.GetChange("configs")
129 | old_configs := o.(map[string]interface{})
130 | new_configs := n.(map[string]interface{})
131 |
132 | deleted_attrs := []string{}
133 |
134 | for old_attr := range old_configs {
135 | value, ok := new_configs[old_attr]
136 | _ = value
137 | if !ok {
138 | deleted_attrs = append(deleted_attrs, old_attr)
139 | }
140 | }
141 |
142 | errDel := c.Config.Delete(ctx, path, deleted_attrs)
143 | if errDel != nil {
144 | return diag.FromErr(errDel)
145 | }
146 |
147 | errSet := c.Config.Set(ctx, path, new_configs)
148 | if errSet != nil {
149 | return diag.FromErr(errSet)
150 | }
151 |
152 | p.conditionalSave(ctx)
153 | return diags
154 | }
155 |
156 | func resourceConfigBlockDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
157 | var diags diag.Diagnostics
158 |
159 | p := m.(*ProviderClass)
160 | c := *p.client
161 | path := d.Get("path").(string)
162 |
163 | err := c.Config.Delete(ctx, path)
164 | if err != nil {
165 | return diag.FromErr(err)
166 | }
167 |
168 | p.conditionalSave(ctx)
169 | return diags
170 | }
171 |
--------------------------------------------------------------------------------
/vyos/resource_config_block_tree.go:
--------------------------------------------------------------------------------
1 | package vyos
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "reflect"
7 | "sort"
8 | "strings"
9 | "time"
10 |
11 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
14 |
15 | "github.com/foltik/vyos-client-go/client"
16 | )
17 |
18 | func resourceConfigBlockTree() *schema.Resource {
19 | return &schema.Resource{
20 | Description: "This resource is useful when a single command is not enough for a valid config commit and children paths are needed.",
21 | CreateContext: resourceConfigBlockTreeCreate,
22 | ReadContext: resourceConfigBlockTreeRead,
23 | UpdateContext: resourceConfigBlockTreeUpdate,
24 | DeleteContext: resourceConfigBlockTreeDelete,
25 | Importer: &schema.ResourceImporter{
26 | StateContext: schema.ImportStatePassthroughContext,
27 | },
28 | Schema: map[string]*schema.Schema{
29 | "id": {
30 | Description: "The resource ID, same as the `path`",
31 | Type: schema.TypeString,
32 | Computed: true,
33 | },
34 | "path": {
35 | Description: "Config path seperated by spaces.",
36 | Type: schema.TypeString,
37 | Required: true,
38 | ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace),
39 | ForceNew: true,
40 | },
41 | "configs": {
42 | Description: "Key/Value map of config parameters. Value can be a jsonencode list",
43 | Type: schema.TypeMap,
44 | Elem: &schema.Schema{
45 | Type: schema.TypeString,
46 | },
47 | Required: true,
48 | DiffSuppressFunc: configDiffSuppressFunc,
49 | },
50 | },
51 | Timeouts: &schema.ResourceTimeout{
52 | Create: schema.DefaultTimeout(10 * time.Minute),
53 | Read: schema.DefaultTimeout(10 * time.Minute),
54 | Update: schema.DefaultTimeout(10 * time.Minute),
55 | Delete: schema.DefaultTimeout(10 * time.Minute),
56 | Default: schema.DefaultTimeout(10 * time.Minute),
57 | },
58 | }
59 | }
60 |
61 | func configDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool {
62 |
63 | multivalueOld := []string{}
64 | err := json.Unmarshal([]byte(old), &multivalueOld)
65 | if err != nil {
66 | return false
67 | }
68 | sort.Strings(multivalueOld)
69 |
70 | multivalueNew := []string{}
71 | err = json.Unmarshal([]byte(new), &multivalueNew)
72 | if err != nil {
73 | return false
74 | }
75 | sort.Strings(multivalueNew)
76 |
77 | return reflect.DeepEqual(multivalueOld, multivalueNew)
78 | }
79 |
80 | // Covert configs to a set of vyos client commands.
81 | // If expand_slice is set, then list values (json encoded) are expanded in multiple vyos client commands
82 | // If expand_slice is not set then the values in the map might contain slices
83 | func getCommandsForConfig(config interface{}, expand_slice bool) (commands map[string]any) {
84 |
85 | commands = map[string]interface{}{}
86 | for key, value := range config.(map[string]interface{}) {
87 |
88 | // Try to decode the string as json list
89 | value := value.(string)
90 | multivalue := []string{}
91 | err := json.Unmarshal([]byte(value), &multivalue)
92 | if err == nil {
93 | if expand_slice {
94 | for _, subvalue := range multivalue {
95 | commands[key+" "+subvalue] = ""
96 | }
97 | } else {
98 | commands[key] = multivalue
99 | }
100 | } else {
101 | // Could not decode json string - assume single value string
102 | if expand_slice {
103 | commands[key] = value
104 | } else {
105 | commands[key] = []string{value}
106 | }
107 | }
108 | }
109 | return
110 | }
111 |
112 | func resourceConfigBlockTreeCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
113 | var diags diag.Diagnostics
114 |
115 | p := m.(*ProviderClass)
116 | client := *p.client
117 | path := d.Get("path").(string)
118 |
119 | // Get commands needed to create resource in Vyos
120 | commands := getCommandsForConfig(d.Get("configs"), true)
121 |
122 | err := client.Config.Set(ctx, path, commands)
123 | if err != nil {
124 | return diag.FromErr(err)
125 | }
126 |
127 | d.SetId(path)
128 | p.conditionalSave(ctx)
129 | return diags
130 | }
131 |
132 | func resourceConfigBlockTreeRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
133 | var diags diag.Diagnostics
134 |
135 | p := m.(*ProviderClass)
136 | //c := *p.client
137 | path := d.Id()
138 |
139 | configsTree, err := p.ShowCached(ctx, path)
140 | if err != nil {
141 | return diag.FromErr(err)
142 | }
143 |
144 | flat, err := client.Flatten(configsTree)
145 | if err != nil {
146 | return diag.FromErr(err)
147 | }
148 |
149 | // Convert Vyos commands to Terraform schema
150 | configs := map[string]interface{}{}
151 | for _, config := range flat {
152 | key := config[0]
153 | value := config[1]
154 | existing_value, ok := configs[key]
155 | if ok {
156 | // This is command with multiple values
157 | switch existing_value := existing_value.(type) {
158 | case string:
159 | // Second value for command found - convert to slice
160 | configs[key] = []string{existing_value, value}
161 | case []string:
162 | // N value for command found - append to slice
163 | configs[key] = append(existing_value, value)
164 | }
165 | } else {
166 | configs[key] = value
167 | }
168 | }
169 |
170 | // If there are slices then covert them to json strings
171 | for key, value := range configs {
172 | switch value := value.(type) {
173 | case []string:
174 | jsonBytes, _ := json.Marshal(value)
175 | configs[key] = string(jsonBytes)
176 | }
177 | }
178 |
179 | // Easiest way to allow ImportStatePassthroughContext to work is to set the path
180 | if d.Get("path") == "" {
181 | if err := d.Set("path", path); err != nil {
182 | return diag.FromErr(err)
183 | }
184 | }
185 |
186 | if err := d.Set("configs", configs); err != nil {
187 | return diag.FromErr(err)
188 | }
189 |
190 | return diags
191 | }
192 |
193 | func resourceConfigBlockTreeUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
194 | var diags diag.Diagnostics
195 |
196 | p := m.(*ProviderClass)
197 | c := *p.client
198 |
199 | path := d.Get("path").(string)
200 | o, n := d.GetChange("configs")
201 | old_configs := o.(map[string]interface{})
202 | new_configs := n.(map[string]interface{})
203 |
204 | // Get commands needed to create old a new config
205 | old_comands := getCommandsForConfig(old_configs, false)
206 | new_comands := getCommandsForConfig(new_configs, false)
207 |
208 | // NOTE: it is important to apply new settings before deleting to
209 | // avoid errors. This is because delete and set are 2
210 | // different API calls and this might result in invalid
211 | // intermediary configs if we delete first.
212 |
213 | // Calculate new commands (new config minus old config)
214 | set_commands := map[string]interface{}{}
215 | for command, new_value := range new_comands {
216 | old_value, ok := old_comands[command]
217 | if !ok || !reflect.DeepEqual(new_value, old_value) {
218 | switch new_value := new_value.(type) {
219 | case []string:
220 | // List value - multiple subcommands
221 | for _, value := range new_value {
222 | set_commands[command+" "+value] = ""
223 | }
224 | case string:
225 | set_commands[command] = new_value
226 | }
227 | }
228 | }
229 | if len(new_comands) > 0 {
230 | errSet := c.Config.Set(ctx, path, new_comands)
231 | if errSet != nil {
232 | return diag.FromErr(errSet)
233 | }
234 | }
235 |
236 | // Calculate delete commands (old config minus new config)
237 | delete_commands := map[string]interface{}{}
238 | for command, old_value := range old_comands {
239 | new_value, ok := new_comands[command]
240 | if !ok {
241 | // Not found in new config - delete commpletly
242 | if len(command) > len(path) {
243 | // Do not delete path
244 | delete_commands[command] = ""
245 | }
246 | } else {
247 | // Compare old and new values for this command
248 | for _, old_value_part := range old_value.([]string) {
249 | found := false
250 | for _, new_value_part := range new_value.([]string) {
251 | if old_value_part == new_value_part {
252 | found = true
253 | break
254 | }
255 | }
256 | // Only delete if old value not in new config AND this
257 | // command is bellow the resource path
258 | if !found && len(command) > len(path) {
259 | delete_commands[command] = old_value_part
260 | }
261 | }
262 | }
263 | }
264 | // Remove orphan nodes as well
265 | // An orphan is a node that does not have any entries in the new config
266 | //
267 | // Example: "system static-host-mapping host-name foo inet" => 1.2.3.4"
268 | // This will fail if we delete only "system static-host-mapping host-name foo inet"
269 | // and do not delete "system static-host-mapping host-name foo" as well
270 | for key, _ := range delete_commands {
271 | key_parts := strings.Split(key, " ")
272 | parent := ""
273 | out:
274 | for _, key_part := range key_parts {
275 | if len(parent) > 0 {
276 | parent += " "
277 | }
278 | parent += key_part
279 | found := false
280 | for key, _ := range new_comands {
281 | if strings.Contains(key, parent) {
282 | // parent still exists in new config - keep parent
283 | found = true
284 | break
285 | }
286 | }
287 | if !found && len(parent) > len(path) {
288 | // Delete parent if not done already
289 | if _, ok := delete_commands[parent]; !ok {
290 | delete_commands[parent] = ""
291 | }
292 | break out
293 | }
294 | }
295 | }
296 | if len(delete_commands) > 0 {
297 | errDel := c.Config.Delete(ctx, path, delete_commands)
298 | if errDel != nil {
299 | return diag.FromErr(errDel)
300 | }
301 | }
302 |
303 | p.conditionalSave(ctx)
304 | return diags
305 | }
306 |
307 | func resourceConfigBlockTreeDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
308 | var diags diag.Diagnostics
309 |
310 | p := m.(*ProviderClass)
311 | c := *p.client
312 | path := d.Get("path").(string)
313 |
314 | err := c.Config.Delete(ctx, path)
315 | if err != nil {
316 | return diag.FromErr(err)
317 | }
318 |
319 | p.conditionalSave(ctx)
320 | return diags
321 | }
322 |
--------------------------------------------------------------------------------
/vyos/resource_static_host_mapping.go:
--------------------------------------------------------------------------------
1 | package vyos
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "strconv"
7 | "time"
8 |
9 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11 | )
12 |
13 | func resourceStaticHostMapping() *schema.Resource {
14 | return &schema.Resource{
15 | Description: "This resource manages a static host mapping with the given hostname and ipv4 address.",
16 | CreateContext: resourceStaticHostMappingCreate,
17 | ReadContext: resourceStaticHostMappingRead,
18 | UpdateContext: resourceStaticHostMappingUpdate,
19 | DeleteContext: resourceStaticHostMappingDelete,
20 | Schema: map[string]*schema.Schema{
21 | "host": {
22 | Description: "Hostname.",
23 | Type: schema.TypeString,
24 | Required: true,
25 | },
26 | "ip": {
27 | Description: "IPv4 address.",
28 | Type: schema.TypeString,
29 | Required: true,
30 | },
31 | },
32 | Timeouts: &schema.ResourceTimeout{
33 | Create: schema.DefaultTimeout(10 * time.Minute),
34 | Read: schema.DefaultTimeout(10 * time.Minute),
35 | Update: schema.DefaultTimeout(10 * time.Minute),
36 | Delete: schema.DefaultTimeout(10 * time.Minute),
37 | Default: schema.DefaultTimeout(10 * time.Minute),
38 | },
39 | }
40 | }
41 |
42 | func resourceStaticHostMappingCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
43 | p := m.(*ProviderClass)
44 | c := *p.client
45 | host, ip := d.Get("host").(string), d.Get("ip").(string)
46 |
47 | path := fmt.Sprintf("system static-host-mapping host-name %s inet", host)
48 | err := c.Config.Set(ctx, path, ip)
49 | if err != nil {
50 | return diag.FromErr(err)
51 | }
52 |
53 | d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
54 | p.conditionalSave(ctx)
55 | return diag.Diagnostics{}
56 | }
57 |
58 | func resourceStaticHostMappingRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
59 | p := m.(*ProviderClass)
60 | host := d.Get("host").(string)
61 |
62 | path := fmt.Sprintf("system static-host-mapping host-name %s inet", host)
63 | ip, err := p.ShowCached(ctx, path)
64 | if err != nil {
65 | return diag.FromErr(err)
66 | }
67 |
68 | if err := d.Set("ip", ip); err != nil {
69 | return diag.FromErr(err)
70 | }
71 |
72 | return diag.Diagnostics{}
73 | }
74 |
75 | func resourceStaticHostMappingUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
76 | p := m.(*ProviderClass)
77 | c := *p.client
78 | host, ip := d.Get("host").(string), d.Get("ip").(string)
79 |
80 | // If the hostname changes, so does the configuration path,
81 | // so we need to delete the old mapping.
82 | if d.HasChange("host") {
83 | old, _ := d.GetChange("host")
84 | path := fmt.Sprintf("system static-host-mapping host-name %s", old)
85 | err := c.Config.Delete(ctx, path)
86 | if err != nil {
87 | return diag.FromErr(err)
88 | }
89 | }
90 |
91 | path := fmt.Sprintf("system static-host-mapping host-name %s inet", host)
92 | err := c.Config.Set(ctx, path, ip)
93 | if err != nil {
94 | return diag.FromErr(err)
95 | }
96 |
97 | p.conditionalSave(ctx)
98 | return diag.Diagnostics{}
99 | }
100 |
101 | func resourceStaticHostMappingDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
102 | p := m.(*ProviderClass)
103 | c := *p.client
104 | host := d.Get("host").(string)
105 |
106 | path := fmt.Sprintf("system static-host-mapping host-name %s", host)
107 | err := c.Config.Delete(ctx, path)
108 | if err != nil {
109 | return diag.FromErr(err)
110 | }
111 |
112 | p.conditionalSave(ctx)
113 | return diag.Diagnostics{}
114 | }
115 |
--------------------------------------------------------------------------------