diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index af50debc..adcc79f5 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -21,7 +21,5 @@ jobs: with: go-version-file: 'go.mod' - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - version: v1.52 - only-new-issues: true + run: | + go run -modfile=tools/go.mod github.com/golangci/golangci-lint/cmd/golangci-lint run -v --timeout 5m diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 23d6d570..02aaf2a7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,11 +65,15 @@ jobs: - name: Get dependencies run: go mod download - - name: TF acceptance tests +# - name: TF acceptance tests +# timeout-minutes: 10 +# env: +# TF_ACC: "1" +# TF_ACC_TERRAFORM_VERSION: ${{ matrix.terraform }} +# run: go test -v -cover ./... + + - name: Unit tests timeout-minutes: 10 - env: - TF_ACC: "1" - TF_ACC_TERRAFORM_VERSION: ${{ matrix.terraform }} run: go test -v -cover ./... all-tests-passed: diff --git a/.golangci.yml b/.golangci.yml index add5ff0a..19d834be 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -73,6 +73,7 @@ linters: - funlen - gocognit # others + - depguard - exhaustivestruct - exhaustruct - gci diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60a7d2b0..2dfd22f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,20 @@ We expect that all commit messages follow the Please use the `scope` field to indicate the area of the codebase that is being changed. For example, `vm` for changes in the Virtual Machine resource, or -`lcx` for changes in the Container resource: +`lcx` for changes in the Container resource. + +Common scopes are: + +- `vm` - Virtual Machine resources +- `lcx` - Container resources +- `provider` - Provider configuration and resources +- `core` - Core libraries and utilities +- `docs` - Documentation +- `ci` - Continuous Integration / Actions / GitHub Workflows + +Please use lowercase for the description and do not end it with a period. + +For example: ``` feat(vm): add support for the `clone` operation @@ -56,7 +69,7 @@ well. We use automated release management orchestrated by https://github.com/googleapis/release-please GitHub Action. The action creates a new release PR with the changelog and bumps the version based on the -commit messages. The release PR is merged by the maintainers. +commit messages. The release PR is merged by the maintainers. The release will be published to the GitHub Releases page and the Terraform Registry. diff --git a/docs/resources/virtual_environment_network_linux_bridge.md b/docs/resources/virtual_environment_network_linux_bridge.md new file mode 100644 index 00000000..866d6814 --- /dev/null +++ b/docs/resources/virtual_environment_network_linux_bridge.md @@ -0,0 +1,46 @@ +--- +layout: page +title: proxmox_virtual_environment_network_linux_bridge +permalink: /resources/virtual_environment_network_linux_bridge +nav_order: 13 +parent: Resources +subcategory: Virtual Environment +--- + +# Resource: proxmox_virtual_environment_network_linux_bridge + +Manages a Linux Bridge network interface in a Proxmox VE node. + +## Example Usage + +```terraform +resource "proxmox_virtual_environment_network_linux_bridge" "bridge99" { + node_name = "pve" + iface = "vmbr99" + address = "3.3.3.3/24" + comment = "created by terraform" + mtu = 1499 +} +``` + +## Argument Reference + +- `node_name` - (Required) The name of the node to manage the interface on. +- `name` - (Required) The interface name. Must be "vmbrN", where N is a number + between 0 and 9999. + +- `address` - (Optional) The interface IPv4/CIDR address. +- `address6` - (Optional) The interface IPv6/CIDR address. +- `autostart` - (Optional) Automatically start interface on boot (defaults + to `true`). +- `ports` - (Optional) Specify the list of the interface bridge ports. +- `vlan_aware` - (Optional) Whether the interface bridge is VLAN aware (defaults + to `true`). +- `comment` - (Optional) Comment for the interface. +- `gateway` - (Optional) Default gateway address. +- `gateway6` - (Optional) Default IPv6 gateway address. +- `mtu` - (Optional) The interface MTU. + +### Read-Only + +- `id` (String) A unique identifier with format ':' diff --git a/docs/resources/virtual_environment_network_linux_vlan.md b/docs/resources/virtual_environment_network_linux_vlan.md new file mode 100644 index 00000000..4f1d34e5 --- /dev/null +++ b/docs/resources/virtual_environment_network_linux_vlan.md @@ -0,0 +1,43 @@ +--- +layout: page +title: proxmox_virtual_environment_network_linux_vlan +permalink: /resources/virtual_environment_network_linux_vlan +nav_order: 13 +parent: Resources +subcategory: Virtual Environment +--- + +# Resource: proxmox_virtual_environment_network_linux_vlan + +Manages a Linux VLAN network interface in a Proxmox VE node. + +## Example Usage + +```terraform +resource "proxmox_virtual_environment_network_linux_vlan" "vlan21" { + node_name = "pve" + iface = "ens18.21" + comment = "created by terraform" +} +``` + +## Argument Reference + +- `node_name` - (Required) The name of the node to manage the interface on. +- `name` - (Required) The interface name. Add the VLAN tag number to an + existing interface name, e.g. "ens18.21". + +- `address` - (Optional) The interface IPv4/CIDR address. +- `address6` - (Optional) The interface IPv6/CIDR address. +- `autostart` - (Optional) Automatically start interface on boot (defaults + to `true`). +- `comment` - (Optional) Comment for the interface. +- `gateway` - (Optional) Default gateway address. +- `gateway6` - (Optional) Default IPv6 gateway address. +- `mtu` - (Optional) The interface MTU. + +### Read-Only + +- `id` (String) A unique identifier with format ':' +- `interface` (String) The VLAN raw device. +- `vlan` (Number) The VLAN tag diff --git a/example/data_source_virtual_environment_vm.tf b/example/data_source_virtual_environment_vm.tf index d908770d..f8c3e9e1 100644 --- a/example/data_source_virtual_environment_vm.tf +++ b/example/data_source_virtual_environment_vm.tf @@ -1,7 +1,7 @@ data "proxmox_virtual_environment_vm" "example" { depends_on = [proxmox_virtual_environment_vm.example] - vm_id = proxmox_virtual_environment_vm.example.vm_id - node_name = data.proxmox_virtual_environment_nodes.example.names[0] + vm_id = proxmox_virtual_environment_vm.example.vm_id + node_name = data.proxmox_virtual_environment_nodes.example.names[0] } output "proxmox_virtual_environment_vm_example" { diff --git a/example/data_source_virtual_environment_vms.tf b/example/data_source_virtual_environment_vms.tf index e137745b..ceff8199 100644 --- a/example/data_source_virtual_environment_vms.tf +++ b/example/data_source_virtual_environment_vms.tf @@ -1,6 +1,6 @@ data "proxmox_virtual_environment_vms" "example" { depends_on = [proxmox_virtual_environment_vm.example] - tags = ["ubuntu"] + tags = ["ubuntu"] lifecycle { postcondition { diff --git a/example/resource_virtual_environment_firewall_alias.tf b/example/resource_virtual_environment_firewall_alias.tf index 17b42dc0..3ccea4d2 100644 --- a/example/resource_virtual_environment_firewall_alias.tf +++ b/example/resource_virtual_environment_firewall_alias.tf @@ -18,8 +18,8 @@ resource "proxmox_virtual_environment_firewall_alias" "vm_alias" { resource "proxmox_virtual_environment_firewall_alias" "container_alias" { depends_on = [proxmox_virtual_environment_container.example] - node_name = proxmox_virtual_environment_container.example.node_name - container_id = proxmox_virtual_environment_container.example.vm_id + node_name = proxmox_virtual_environment_container.example.node_name + container_id = proxmox_virtual_environment_container.example.vm_id name = "container-alias" cidr = "192.168.2.0/23" diff --git a/example/resource_virtual_environment_firewall_ipset.tf b/example/resource_virtual_environment_firewall_ipset.tf index 22327418..9cf5e287 100644 --- a/example/resource_virtual_environment_firewall_ipset.tf +++ b/example/resource_virtual_environment_firewall_ipset.tf @@ -38,8 +38,8 @@ resource "proxmox_virtual_environment_firewall_ipset" "vm_ipset" { resource "proxmox_virtual_environment_firewall_ipset" "container_ipset" { depends_on = [proxmox_virtual_environment_container.example] - node_name = proxmox_virtual_environment_container.example.node_name - container_id = proxmox_virtual_environment_container.example.vm_id + node_name = proxmox_virtual_environment_container.example.node_name + container_id = proxmox_virtual_environment_container.example.vm_id name = "container-ipset" comment = "Managed by Terraform" diff --git a/example/resource_virtual_environment_firewall_options.tf b/example/resource_virtual_environment_firewall_options.tf index 921874b7..447e8f43 100644 --- a/example/resource_virtual_environment_firewall_options.tf +++ b/example/resource_virtual_environment_firewall_options.tf @@ -34,8 +34,8 @@ resource "proxmox_virtual_environment_firewall_options" "vm_options" { resource "proxmox_virtual_environment_firewall_options" "container_options" { depends_on = [proxmox_virtual_environment_container.example] - node_name = proxmox_virtual_environment_container.example.node_name - container_id = proxmox_virtual_environment_container.example.vm_id + node_name = proxmox_virtual_environment_container.example.node_name + container_id = proxmox_virtual_environment_container.example.vm_id dhcp = false enabled = false diff --git a/example/resource_virtual_environment_firewall_rules.tf b/example/resource_virtual_environment_firewall_rules.tf index 5c055dd7..6bce260f 100644 --- a/example/resource_virtual_environment_firewall_rules.tf +++ b/example/resource_virtual_environment_firewall_rules.tf @@ -58,8 +58,8 @@ resource "proxmox_virtual_environment_firewall_rules" "vm_rules" { resource "proxmox_virtual_environment_firewall_rules" "container_rules" { depends_on = [proxmox_virtual_environment_container.example] - node_name = proxmox_virtual_environment_container.example.node_name - container_id = proxmox_virtual_environment_container.example.vm_id + node_name = proxmox_virtual_environment_container.example.node_name + container_id = proxmox_virtual_environment_container.example.vm_id rule { type = "in" diff --git a/example/resource_virtual_environment_network.tf b/example/resource_virtual_environment_network.tf new file mode 100644 index 00000000..36dbbc32 --- /dev/null +++ b/example/resource_virtual_environment_network.tf @@ -0,0 +1,23 @@ +resource "proxmox_virtual_environment_network_linux_vlan" "vlan99" { + node_name = "pve" + name = "ens18.99" + + comment = "VLAN 99" +} + +resource "proxmox_virtual_environment_network_linux_bridge" "vmbr99" { + depends_on = [ + proxmox_virtual_environment_network_linux_vlan.vlan99 + ] + + node_name = "pve" + name = "vmbr99" + + address = "99.99.99.99/16" + + comment = "vmbr99 comment" + + ports = [ + "ens18.99" + ] +} diff --git a/example/resource_virtual_environment_vm.tf b/example/resource_virtual_environment_vm.tf index 1b47720a..9db5695e 100644 --- a/example/resource_virtual_environment_vm.tf +++ b/example/resource_virtual_environment_vm.tf @@ -9,12 +9,12 @@ resource "proxmox_virtual_environment_vm" "example_template" { description = "Managed by Terraform" -# disk { -# datastore_id = local.datastore_id -# file_id = proxmox_virtual_environment_file.ubuntu_cloud_image.id -# interface = "virtio0" -# iothread = true -# } + # disk { + # datastore_id = local.datastore_id + # file_id = proxmox_virtual_environment_file.ubuntu_cloud_image.id + # interface = "virtio0" + # iothread = true + # } disk { datastore_id = local.datastore_id @@ -24,12 +24,12 @@ resource "proxmox_virtual_environment_vm" "example_template" { ssd = true } -# disk { -# datastore_id = "nfs" -# interface = "scsi1" -# discard = "ignore" -# file_format = "raw" -# } + # disk { + # datastore_id = "nfs" + # interface = "scsi1" + # discard = "ignore" + # file_format = "raw" + # } initialization { datastore_id = local.datastore_id @@ -47,14 +47,14 @@ resource "proxmox_virtual_environment_vm" "example_template" { #} } - user_data_file_id = proxmox_virtual_environment_file.user_config.id + user_data_file_id = proxmox_virtual_environment_file.user_config.id vendor_data_file_id = proxmox_virtual_environment_file.vendor_config.id } name = "terraform-provider-proxmox-example-template" network_device { - mtu = 1450 + mtu = 1450 } network_device { @@ -81,7 +81,7 @@ resource "proxmox_virtual_environment_vm" "example" { node_name = data.proxmox_virtual_environment_nodes.example.names[0] pool_id = proxmox_virtual_environment_pool.example.id vm_id = 2041 - tags = ["terraform", "ubuntu"] + tags = ["terraform", "ubuntu"] clone { vm_id = proxmox_virtual_environment_vm.example_template.id @@ -105,13 +105,13 @@ resource "proxmox_virtual_environment_vm" "example" { ] } -# While overwriting the initialization block when cloning a template is possible, it is not recommended. -# This will cause the coned VM to be reinitialized each time on re-apply. -# initialization { -# dns { -# server = "8.8.8.8" -# } -# } + # While overwriting the initialization block when cloning a template is possible, it is not recommended. + # This will cause the coned VM to be reinitialized each time on re-apply. + # initialization { + # dns { + # server = "8.8.8.8" + # } + # } } diff --git a/example/ssh.tf b/example/ssh.tf index b390b788..717e8545 100644 --- a/example/ssh.tf +++ b/example/ssh.tf @@ -1,11 +1,11 @@ resource "local_sensitive_file" "example_ssh_private_key" { - filename = "${path.module}/autogenerated/id_rsa" - content = tls_private_key.example.private_key_pem + filename = "${path.module}/autogenerated/id_rsa" + content = tls_private_key.example.private_key_pem } resource "local_sensitive_file" "example_ssh_public_key" { - filename = "${path.module}/autogenerated/id_rsa.pub" - content = tls_private_key.example.public_key_openssh + filename = "${path.module}/autogenerated/id_rsa.pub" + content = tls_private_key.example.public_key_openssh } resource "tls_private_key" "example" { diff --git a/example/versions.tf b/example/versions.tf index d7642c09..60bd3318 100644 --- a/example/versions.tf +++ b/example/versions.tf @@ -9,7 +9,7 @@ terraform { version = "3.1.0" } proxmox = { - source = "bpg/proxmox" + source = "bpg/proxmox" } } } diff --git a/go.mod b/go.mod index 958cd58a..2e5beb87 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,11 @@ require ( github.com/google/uuid v1.3.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-multierror v1.1.1 + github.com/hashicorp/terraform-plugin-framework v1.3.0 + github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 + github.com/hashicorp/terraform-plugin-go v0.15.0 github.com/hashicorp/terraform-plugin-log v0.9.0 + github.com/hashicorp/terraform-plugin-mux v0.10.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 github.com/pkg/sftp v1.13.5 github.com/skeema/knownhosts v1.1.1 @@ -17,26 +21,31 @@ require ( ) require ( + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.14.1 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.9 // 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-hclog v1.5.0 // indirect - github.com/hashicorp/go-plugin v1.4.8 // indirect + github.com/hashicorp/go-plugin v1.4.9 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/hc-install v0.5.2 // indirect github.com/hashicorp/hcl/v2 v2.16.2 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.14.3 // indirect - github.com/hashicorp/terraform-registry-address v0.1.0 // indirect + github.com/hashicorp/terraform-exec v0.18.1 // indirect + github.com/hashicorp/terraform-json v0.16.0 // indirect + github.com/hashicorp/terraform-registry-address v0.2.0 // indirect github.com/hashicorp/terraform-svchost v0.1.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/kr/fs v0.1.0 // indirect - github.com/kr/pretty v0.2.1 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/kr/pretty v0.3.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -47,16 +56,16 @@ require ( github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.2 // indirect - github.com/zclconf/go-cty v1.13.1 // indirect + github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zclconf/go-cty v1.13.2 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44 // indirect - google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + google.golang.org/grpc v1.54.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7279ca6a..858dd4c4 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,30 @@ -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -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.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -32,48 +40,65 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +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.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 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.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= -github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.4.9 h1:ESiK220/qE0aGxWdzKIvRH69iLiuN/PjoLTm69RoWtU= +github.com/hashicorp/go-plugin v1.4.9/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +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.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hc-install v0.5.2 h1:SfwMFnEXVVirpwkDuSF5kymUOhrUxrTq3udEseZdOD0= +github.com/hashicorp/hc-install v0.5.2/go.mod h1:9QISwe6newMWIfEiXpzuu1k9HAGtQYgnSH8H9T8wmoI= github.com/hashicorp/hcl/v2 v2.16.2 h1:mpkHZh/Tv+xet3sy3F9Ld4FyI2tUpWe9x3XtPx9f1a0= github.com/hashicorp/hcl/v2 v2.16.2/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= 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-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= -github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= +github.com/hashicorp/terraform-exec v0.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= +github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= +github.com/hashicorp/terraform-json v0.16.0 h1:UKkeWRWb23do5LNAFlh/K3N0ymn1qTOO8c+85Albo3s= +github.com/hashicorp/terraform-json v0.16.0/go.mod h1:v0Ufk9jJnk6tcIZvScHvetlKfiNTC+WS21mnXIlc0B0= +github.com/hashicorp/terraform-plugin-framework v1.3.0 h1:WtP1CIaWAfbzME17xoUXvJcyh5Ewu9attdhbfWNnYLs= +github.com/hashicorp/terraform-plugin-framework v1.3.0/go.mod h1:A1WD3Ry7FhrThViUTbkx4ZDsMq9oaAv4U9oTI8bBzCU= +github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 h1:4L0tmy/8esP6OcvocVymw52lY0HyQ5OxB7VNl7k4bS0= +github.com/hashicorp/terraform-plugin-framework-validators v0.10.0/go.mod h1:qdQJCdimB9JeX2YwOpItEu+IrfoJjWQ5PhLpAOMDQAE= +github.com/hashicorp/terraform-plugin-go v0.15.0 h1:1BJNSUFs09DS8h/XNyJNJaeusQuWc/T9V99ylU9Zwp0= +github.com/hashicorp/terraform-plugin-go v0.15.0/go.mod h1:tk9E3/Zx4RlF/9FdGAhwxHExqIHHldqiQGt20G6g+nQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= +github.com/hashicorp/terraform-plugin-mux v0.10.0 h1:VejY1BffxGy2iYOaa8DDHavY4k9jbvAE8F3lhruspKY= +github.com/hashicorp/terraform-plugin-mux v0.10.0/go.mod h1:9sdnpmY20xIsl4ItsfODZYE+MgpSy/osXpSf+RwaZCY= github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 h1:G9WAfb8LHeCxu7Ae8nc1agZlQOSCUWsb610iAogBhCs= github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1/go.mod h1:xcOSYlRVdPLmDUoqPhO9fiO/YCN/l6MGYeTzGt5jgkQ= -github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= -github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= -github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= +github.com/hashicorp/terraform-registry-address v0.2.0 h1:92LUg03NhfgZv44zpNTLBGIbiyTokQCDcdH5BhVHT3s= +github.com/hashicorp/terraform-registry-address v0.2.0/go.mod h1:478wuzJPzdmqT6OGbB/iH82EDcI8VFM4yujknh/1nIs= github.com/hashicorp/terraform-svchost v0.1.0 h1:0+RcgZdZYNd81Vw7tu62g9JiLLvbOigp7QtyNh6CjXk= github.com/hashicorp/terraform-svchost v0.1.0/go.mod h1:ut8JaH0vumgdCfJaihdcZULqkAwHdQNwNH7taIDdsZM= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 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= @@ -96,13 +121,18 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go= github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -110,34 +140,29 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl 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/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= -github.com/zclconf/go-cty v1.13.1 h1:0a6bRwuiSHtAmqCqNOE+c2oHgepv0ctoxU4FUe43kwc= -github.com/zclconf/go-cty v1.13.1/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0= +github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= 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.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20191009170851-d66e71096ffb/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-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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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= @@ -146,6 +171,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w 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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -162,22 +188,21 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44 h1:EfLuoKW5WfkgVdDy7dTK8qSbH37AX5mj/MFh+bGPz14= google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= 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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= diff --git a/internal/internal.go b/internal/internal.go new file mode 100644 index 00000000..85eb042d --- /dev/null +++ b/internal/internal.go @@ -0,0 +1,8 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +// Package internal contains implementation of the provider created with Terraform Plugin Framework. +package internal diff --git a/internal/network/resource_linux_bridge.go b/internal/network/resource_linux_bridge.go new file mode 100644 index 00000000..84b00534 --- /dev/null +++ b/internal/network/resource_linux_bridge.go @@ -0,0 +1,488 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package network + +import ( + "context" + "fmt" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + + pvetypes "github.com/bpg/terraform-provider-proxmox/internal/types" + "github.com/bpg/terraform-provider-proxmox/proxmox" + "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" +) + +var ( + _ resource.Resource = &linuxBridgeResource{} + _ resource.ResourceWithConfigure = &linuxBridgeResource{} + _ resource.ResourceWithImportState = &linuxBridgeResource{} +) + +type linuxBridgeResourceModel struct { + // Base attributes + ID types.String `tfsdk:"id"` + NodeName types.String `tfsdk:"node_name"` + Name types.String `tfsdk:"name"` + Address pvetypes.IPCIDRValue `tfsdk:"address"` + Gateway pvetypes.IPAddrValue `tfsdk:"gateway"` + Address6 pvetypes.IPCIDRValue `tfsdk:"address6"` + Gateway6 pvetypes.IPAddrValue `tfsdk:"gateway6"` + Autostart types.Bool `tfsdk:"autostart"` + MTU types.Int64 `tfsdk:"mtu"` + Comment types.String `tfsdk:"comment"` + // Linux bridge attributes + Ports []types.String `tfsdk:"ports"` + VLANAware types.Bool `tfsdk:"vlan_aware"` +} + +//nolint:lll +func (m *linuxBridgeResourceModel) exportToNetworkInterfaceCreateUpdateBody() *nodes.NetworkInterfaceCreateUpdateRequestBody { + body := &nodes.NetworkInterfaceCreateUpdateRequestBody{ + Iface: m.Name.ValueString(), + Type: "bridge", + Autostart: pvetypes.CustomBool(m.Autostart.ValueBool()).Pointer(), + } + + body.CIDR = m.Address.ValueStringPointer() + body.Gateway = m.Gateway.ValueStringPointer() + body.CIDR6 = m.Address6.ValueStringPointer() + body.Gateway6 = m.Gateway6.ValueStringPointer() + + if !m.MTU.IsUnknown() { + body.MTU = m.MTU.ValueInt64Pointer() + } + + body.Comments = m.Comment.ValueStringPointer() + + var sanitizedPorts []string + + for i := 0; i < len(m.Ports); i++ { + port := strings.TrimSpace(m.Ports[i].ValueString()) + if len(port) > 0 { + sanitizedPorts = append(sanitizedPorts, port) + } + } + sort.Strings(sanitizedPorts) + bridgePorts := strings.Join(sanitizedPorts, " ") + + if len(bridgePorts) > 0 { + body.BridgePorts = &bridgePorts + } + + body.BridgeVLANAware = pvetypes.CustomBool(m.VLANAware.ValueBool()).Pointer() + + return body +} + +func (m *linuxBridgeResourceModel) importFromNetworkInterfaceList( + ctx context.Context, + iface *nodes.NetworkInterfaceListResponseData, +) error { + m.Address = pvetypes.NewIPCIDRPointerValue(iface.CIDR) + m.Gateway = pvetypes.NewIPAddrPointerValue(iface.Gateway) + m.Address6 = pvetypes.NewIPCIDRPointerValue(iface.CIDR6) + m.Gateway6 = pvetypes.NewIPAddrPointerValue(iface.Gateway6) + m.Autostart = types.BoolPointerValue(iface.Autostart.PointerBool()) + + if iface.MTU != nil { + if v, err := strconv.Atoi(*iface.MTU); err == nil { + m.MTU = types.Int64Value(int64(v)) + } + } else { + m.MTU = types.Int64Null() + } + + if iface.Comments != nil { + m.Comment = types.StringValue(strings.TrimSpace(*iface.Comments)) + } else { + m.Comment = types.StringNull() + } + + if iface.BridgeVLANAware != nil { + m.VLANAware = types.BoolPointerValue(iface.BridgeVLANAware.PointerBool()) + } else { + m.VLANAware = types.BoolValue(false) + } + + if iface.BridgePorts != nil && len(*iface.BridgePorts) > 0 { + ports, diags := types.ListValueFrom(ctx, types.StringType, strings.Split(*iface.BridgePorts, " ")) + if diags.HasError() { + return fmt.Errorf("failed to parse bridge ports: %s", *iface.BridgePorts) + } + + diags = ports.ElementsAs(ctx, &m.Ports, false) + if diags.HasError() { + return fmt.Errorf("failed to build bridge ports list: %s", *iface.BridgePorts) + } + } + + return nil +} + +// NewLinuxBridgeResource creates a new resource for managing Linux Bridge network interfaces. +func NewLinuxBridgeResource() resource.Resource { + return &linuxBridgeResource{} +} + +type linuxBridgeResource struct { + client proxmox.Client +} + +func (r *linuxBridgeResource) Metadata( + _ context.Context, + req resource.MetadataRequest, + resp *resource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_network_linux_bridge" +} + +// Schema defines the schema for the resource. +func (r *linuxBridgeResource) Schema( + _ context.Context, + _ resource.SchemaRequest, + resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: "Manages a Linux Bridge network interface in a Proxmox VE node.", + Attributes: map[string]schema.Attribute{ + // Base attributes + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Description: "A unique identifier with format ':'", + }, + "node_name": schema.StringAttribute{ + Description: "The name of the node.", + Required: true, + }, + "name": schema.StringAttribute{ + Description: "The interface name.", + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^vmbr(\d{1,4})$`), + `must be "vmbrN", where N is a number between 0 and 9999`, + ), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "address": schema.StringAttribute{ + Description: "The interface IPv4/CIDR address.", + CustomType: pvetypes.IPCIDRType{}, + Optional: true, + }, + "gateway": schema.StringAttribute{ + Description: "Default gateway address.", + CustomType: pvetypes.IPAddrType{}, + Optional: true, + }, + "address6": schema.StringAttribute{ + Description: "The interface IPv6/CIDR address.", + CustomType: pvetypes.IPCIDRType{}, + Optional: true, + }, + "gateway6": schema.StringAttribute{ + Description: "Default IPv6 gateway address.", + CustomType: pvetypes.IPAddrType{}, + Optional: true, + }, + "autostart": schema.BoolAttribute{ + Description: "Automatically start interface on boot.", + Optional: true, + Computed: true, + Default: booldefault.StaticBool(true), + }, + "mtu": schema.Int64Attribute{ + Description: "The interface MTU.", + Optional: true, + Computed: true, + }, + "comment": schema.StringAttribute{ + Description: "Comment for the interface.", + Optional: true, + }, + // Linux Bridge attributes + "ports": schema.ListAttribute{ + Description: "The interface bridge ports.", + Optional: true, + ElementType: types.StringType, + }, + "vlan_aware": schema.BoolAttribute{ + Description: "Whether the interface bridge is VLAN aware.", + Optional: true, + Computed: true, + }, + }, + } +} + +func (r *linuxBridgeResource) Configure( + _ context.Context, + req resource.ConfigureRequest, + resp *resource.ConfigureResponse, +) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(proxmox.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *proxmox.Client, got: %T. Please report this issue to the provider developers.", + req.ProviderData), + ) + + return + } + + r.client = client +} + +//nolint:dupl +func (r *linuxBridgeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan linuxBridgeResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + body := plan.exportToNetworkInterfaceCreateUpdateBody() + + err := r.client.Node(plan.NodeName.ValueString()).CreateNetworkInterface(ctx, body) + if err != nil { + resp.Diagnostics.AddError( + "Error creating Linux Bridge interface", + "Could not create Linux Bridge, unexpected error: "+err.Error(), + ) + + return + } + + plan.ID = types.StringValue(plan.NodeName.ValueString() + ":" + plan.Name.ValueString()) + + r.read(ctx, &plan, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + + err = r.client.Node(plan.NodeName.ValueString()).ReloadNetworkConfiguration(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Error reloading network configuration", + fmt.Sprintf("Could not reload network configuration on node '%s', unexpected error: %s", + plan.NodeName.ValueString(), err.Error()), + ) + } +} + +func (r *linuxBridgeResource) read(ctx context.Context, model *linuxBridgeResourceModel, diags *diag.Diagnostics) { + ifaces, err := r.client.Node(model.NodeName.ValueString()).ListNetworkInterfaces(ctx) + if err != nil { + diags.AddError( + "Error listing network interfaces", + "Could not list network interfaces, unexpected error: "+err.Error(), + ) + + return + } + + for _, iface := range ifaces { + if iface.Iface != model.Name.ValueString() { + continue + } + + err = model.importFromNetworkInterfaceList(ctx, iface) + if err != nil { + diags.AddError( + "Error converting network interface to a model", + "Could not import network interface from API response, unexpected error: "+err.Error(), + ) + + return + } + + break + } +} + +// Read reads a Linux Bridge interface. +func (r *linuxBridgeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // Get current state + var state linuxBridgeResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + r.read(ctx, &state, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} + +// Update updates a Linux Bridge interface. +func (r *linuxBridgeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state linuxBridgeResourceModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + if resp.Diagnostics.HasError() { + return + } + + body := plan.exportToNetworkInterfaceCreateUpdateBody() + + var toDelete []string + + if !plan.MTU.Equal(state.MTU) && (plan.MTU.IsUnknown() || plan.MTU.ValueInt64() == 0) { + toDelete = append(toDelete, "mtu") + body.MTU = nil + } + + // VLANAware is computed, will never be null + if !plan.VLANAware.Equal(state.VLANAware) && !plan.VLANAware.ValueBool() { + toDelete = append(toDelete, "bridge_vlan_aware") + body.BridgeVLANAware = nil + } + + if len(toDelete) > 0 { + body.Delete = &toDelete + } + + err := r.client.Node(plan.NodeName.ValueString()).UpdateNetworkInterface(ctx, plan.Name.ValueString(), body) + if err != nil { + resp.Diagnostics.AddError( + "Error updating Linux Bridge interface", + "Could not update Linux Bridge, unexpected error: "+err.Error(), + ) + + return + } + + r.read(ctx, &plan, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + + err = r.client.Node(state.NodeName.ValueString()).ReloadNetworkConfiguration(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Error reloading network configuration", + fmt.Sprintf("Could not reload network configuration on node '%s', unexpected error: %s", + state.NodeName.ValueString(), err.Error()), + ) + } +} + +// Delete deletes a Linux Bridge interface. +// +//nolint:dupl +func (r *linuxBridgeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state linuxBridgeResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.client.Node(state.NodeName.ValueString()).DeleteNetworkInterface(ctx, state.Name.ValueString()) + if err != nil { + if strings.Contains(err.Error(), "interface does not exist") { + resp.Diagnostics.AddWarning( + "Linux Bridge interface does not exist", + fmt.Sprintf("Could not delete Linux Bridge '%s', interface does not exist, "+ + "or has already been deleted outside of Terraform.", state.Name.ValueString()), + ) + } else { + resp.Diagnostics.AddError( + "Error deleting Linux Bridge interface", + fmt.Sprintf("Could not delete Linux Bridge '%s', unexpected error: %s", + state.Name.ValueString(), err.Error()), + ) + } + + return + } + + err = r.client.Node(state.NodeName.ValueString()).ReloadNetworkConfiguration(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Error reloading network configuration", + fmt.Sprintf("Could not reload network configuration on node '%s', unexpected error: %s", + state.NodeName.ValueString(), err.Error()), + ) + } +} + +func (r *linuxBridgeResource) ImportState( + ctx context.Context, + req resource.ImportStateRequest, + resp *resource.ImportStateResponse, +) { + idParts := strings.Split(req.ID, ":") + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: node_name:iface. Got: %q", req.ID), + ) + + return + } + + nodeName := idParts[0] + iface := idParts[1] + + state := linuxBridgeResourceModel{ + ID: types.StringValue(req.ID), + NodeName: types.StringValue(nodeName), + Name: types.StringValue(iface), + } + r.read(ctx, &state, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + diags := resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} diff --git a/internal/network/resource_linux_vlan.go b/internal/network/resource_linux_vlan.go new file mode 100644 index 00000000..e53fd911 --- /dev/null +++ b/internal/network/resource_linux_vlan.go @@ -0,0 +1,451 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package network + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + + pvetypes "github.com/bpg/terraform-provider-proxmox/internal/types" + "github.com/bpg/terraform-provider-proxmox/proxmox" + "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" +) + +var ( + _ resource.Resource = &linuxVLANResource{} + _ resource.ResourceWithConfigure = &linuxVLANResource{} + _ resource.ResourceWithImportState = &linuxVLANResource{} +) + +type linuxVLANResourceModel struct { + // Base attributes + ID types.String `tfsdk:"id"` + NodeName types.String `tfsdk:"node_name"` + Name types.String `tfsdk:"name"` + Address pvetypes.IPCIDRValue `tfsdk:"address"` + Gateway pvetypes.IPAddrValue `tfsdk:"gateway"` + Address6 pvetypes.IPCIDRValue `tfsdk:"address6"` + Gateway6 pvetypes.IPAddrValue `tfsdk:"gateway6"` + Autostart types.Bool `tfsdk:"autostart"` + MTU types.Int64 `tfsdk:"mtu"` + Comment types.String `tfsdk:"comment"` + // Linux VLAN attributes + Interface types.String `tfsdk:"interface"` + VLAN types.Int64 `tfsdk:"vlan"` +} + +//nolint:lll +func (m *linuxVLANResourceModel) exportToNetworkInterfaceCreateUpdateBody() *nodes.NetworkInterfaceCreateUpdateRequestBody { + body := &nodes.NetworkInterfaceCreateUpdateRequestBody{ + Iface: m.Name.ValueString(), + Type: "vlan", + Autostart: pvetypes.CustomBool(m.Autostart.ValueBool()).Pointer(), + } + + body.CIDR = m.Address.ValueStringPointer() + body.Gateway = m.Gateway.ValueStringPointer() + body.CIDR6 = m.Address6.ValueStringPointer() + body.Gateway6 = m.Gateway6.ValueStringPointer() + body.Comments = m.Comment.ValueStringPointer() + + if !m.MTU.IsUnknown() { + body.MTU = m.MTU.ValueInt64Pointer() + } + + if !m.Interface.IsUnknown() { + body.VLANRawDevice = m.Interface.ValueStringPointer() + } + + if !m.VLAN.IsUnknown() { + body.VLANID = m.VLAN.ValueInt64Pointer() + } + + return body +} + +func (m *linuxVLANResourceModel) importFromNetworkInterfaceList(iface *nodes.NetworkInterfaceListResponseData) { + m.Address = pvetypes.NewIPCIDRPointerValue(iface.CIDR) + m.Gateway = pvetypes.NewIPAddrPointerValue(iface.Gateway) + m.Address6 = pvetypes.NewIPCIDRPointerValue(iface.CIDR6) + m.Gateway6 = pvetypes.NewIPAddrPointerValue(iface.Gateway6) + m.Autostart = types.BoolPointerValue(iface.Autostart.PointerBool()) + + if iface.MTU != nil { + if v, err := strconv.Atoi(*iface.MTU); err == nil { + m.MTU = types.Int64Value(int64(v)) + } + } else { + m.MTU = types.Int64Null() + } + + if iface.Comments != nil { + m.Comment = types.StringValue(strings.TrimSpace(*iface.Comments)) + } else { + m.Comment = types.StringNull() + } + + if iface.VLANID != nil { + if v, err := strconv.Atoi(*iface.VLANID); err == nil { + m.VLAN = types.Int64Value(int64(v)) + } + } else { + // in reality, this should never happen + m.VLAN = types.Int64Unknown() + } + + if iface.VLANRawDevice != nil { + m.Interface = types.StringValue(strings.TrimSpace(*iface.VLANRawDevice)) + } else { + m.Interface = types.StringNull() + } +} + +// NewLinuxVLANResource creates a new resource for managing Linux VLAN network interfaces. +func NewLinuxVLANResource() resource.Resource { + return &linuxVLANResource{} +} + +type linuxVLANResource struct { + client proxmox.Client +} + +func (r *linuxVLANResource) Metadata( + _ context.Context, + req resource.MetadataRequest, + resp *resource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_network_linux_vlan" +} + +// Schema defines the schema for the resource. +func (r *linuxVLANResource) Schema( + _ context.Context, + _ resource.SchemaRequest, + resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: "Manages a Linux VLAN network interface in a Proxmox VE node.", + Attributes: map[string]schema.Attribute{ + // Base attributes + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Description: "A unique identifier with format ':'", + }, + "node_name": schema.StringAttribute{ + Description: "The name of the node.", + Required: true, + }, + "name": schema.StringAttribute{ + Description: "The interface name.", + Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(3), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "address": schema.StringAttribute{ + Description: "The interface IPv4/CIDR address.", + CustomType: pvetypes.IPCIDRType{}, + Optional: true, + }, + "gateway": schema.StringAttribute{ + Description: "Default gateway address.", + CustomType: pvetypes.IPAddrType{}, + Optional: true, + }, + "address6": schema.StringAttribute{ + Description: "The interface IPv6/CIDR address.", + CustomType: pvetypes.IPCIDRType{}, + Optional: true, + }, + "gateway6": schema.StringAttribute{ + Description: "Default IPv6 gateway address.", + CustomType: pvetypes.IPAddrType{}, + Optional: true, + }, + "autostart": schema.BoolAttribute{ + Description: "Automatically start interface on boot.", + Optional: true, + Computed: true, + Default: booldefault.StaticBool(true), + }, + "mtu": schema.Int64Attribute{ + Description: "The interface MTU.", + Optional: true, + Computed: true, + }, + "comment": schema.StringAttribute{ + Description: "Comment for the interface.", + Optional: true, + }, + // Linux VLAN attributes + "interface": schema.StringAttribute{ + // read-only + Description: "The VLAN raw device.", + Computed: true, + }, + "vlan": schema.Int64Attribute{ + // read-only + Description: "The VLAN tag", + Computed: true, + }, + }, + } +} + +func (r *linuxVLANResource) Configure( + _ context.Context, + req resource.ConfigureRequest, + resp *resource.ConfigureResponse, +) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(proxmox.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *proxmox.Client, got: %T. Please report this issue to the provider developers.", + req.ProviderData), + ) + + return + } + + r.client = client +} + +//nolint:dupl +func (r *linuxVLANResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan linuxVLANResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + body := plan.exportToNetworkInterfaceCreateUpdateBody() + + err := r.client.Node(plan.NodeName.ValueString()).CreateNetworkInterface(ctx, body) + if err != nil { + resp.Diagnostics.AddError( + "Error creating Linux VLAN interface", + "Could not create Linux VLAN, unexpected error: "+err.Error(), + ) + + return + } + + plan.ID = types.StringValue(plan.NodeName.ValueString() + ":" + plan.Name.ValueString()) + + r.read(ctx, &plan, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + + err = r.client.Node(plan.NodeName.ValueString()).ReloadNetworkConfiguration(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Error reloading network configuration", + fmt.Sprintf("Could not reload network configuration on node '%s', unexpected error: %s", + plan.NodeName.ValueString(), err.Error()), + ) + } +} + +func (r *linuxVLANResource) read(ctx context.Context, model *linuxVLANResourceModel, diags *diag.Diagnostics) { + ifaces, err := r.client.Node(model.NodeName.ValueString()).ListNetworkInterfaces(ctx) + if err != nil { + diags.AddError( + "Error listing network interfaces", + "Could not list network interfaces, unexpected error: "+err.Error(), + ) + + return + } + + for _, iface := range ifaces { + if iface.Iface != model.Name.ValueString() { + continue + } + + model.importFromNetworkInterfaceList(iface) + + break + } +} + +// Read reads a Linux VLAN interface. +func (r *linuxVLANResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // Get current state + var state linuxVLANResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + r.read(ctx, &state, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} + +// Update updates a Linux VLAN interface. +func (r *linuxVLANResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state linuxVLANResourceModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + if resp.Diagnostics.HasError() { + return + } + + body := plan.exportToNetworkInterfaceCreateUpdateBody() + + var toDelete []string + + if !plan.MTU.Equal(state.MTU) && (plan.MTU.IsUnknown() || plan.MTU.ValueInt64() == 0) { + toDelete = append(toDelete, "mtu") + body.MTU = nil + } + + if len(toDelete) > 0 { + body.Delete = &toDelete + } + + err := r.client.Node(plan.NodeName.ValueString()).UpdateNetworkInterface(ctx, plan.Name.ValueString(), body) + if err != nil { + resp.Diagnostics.AddError( + "Error updating Linux VLAN interface", + "Could not update Linux VLAN, unexpected error: "+err.Error(), + ) + + return + } + + r.read(ctx, &plan, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + + err = r.client.Node(state.NodeName.ValueString()).ReloadNetworkConfiguration(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Error reloading network configuration", + fmt.Sprintf("Could not reload network configuration on node '%s', unexpected error: %s", + state.NodeName.ValueString(), err.Error()), + ) + } +} + +// Delete deletes a Linux VLAN interface. +// +//nolint:dupl +func (r *linuxVLANResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state linuxVLANResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.client.Node(state.NodeName.ValueString()).DeleteNetworkInterface(ctx, state.Name.ValueString()) + if err != nil { + if strings.Contains(err.Error(), "interface does not exist") { + resp.Diagnostics.AddWarning( + "Linux VLAN interface does not exist", + fmt.Sprintf("Could not delete Linux VLAN '%s', interface does not exist, "+ + "or has already been deleted outside of Terraform.", state.Name.ValueString()), + ) + } else { + resp.Diagnostics.AddError( + "Error deleting Linux VLAN interface", + fmt.Sprintf("Could not delete Linux VLAN '%s', unexpected error: %s", + state.Name.ValueString(), err.Error()), + ) + } + + return + } + + err = r.client.Node(state.NodeName.ValueString()).ReloadNetworkConfiguration(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Error reloading network configuration", + fmt.Sprintf("Could not reload network configuration on node '%s', unexpected error: %s", + state.NodeName.ValueString(), err.Error()), + ) + } +} + +func (r *linuxVLANResource) ImportState( + ctx context.Context, + req resource.ImportStateRequest, + resp *resource.ImportStateResponse, +) { + idParts := strings.Split(req.ID, ":") + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: node_name:iface. Got: %q", req.ID), + ) + + return + } + + nodeName := idParts[0] + iface := idParts[1] + + state := linuxVLANResourceModel{ + ID: types.StringValue(req.ID), + NodeName: types.StringValue(nodeName), + Name: types.StringValue(iface), + } + r.read(ctx, &state, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + diags := resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go new file mode 100644 index 00000000..1aba6dae --- /dev/null +++ b/internal/provider/provider.go @@ -0,0 +1,401 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package provider + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/path" + "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/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/bpg/terraform-provider-proxmox/internal/network" + "github.com/bpg/terraform-provider-proxmox/proxmox" + "github.com/bpg/terraform-provider-proxmox/proxmox/api" + "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" + "github.com/bpg/terraform-provider-proxmox/proxmox/ssh" + "github.com/bpg/terraform-provider-proxmox/utils" +) + +// Ensure the implementation satisfies the expected interfaces. +var _ provider.Provider = &proxmoxProvider{} + +// New is a helper function to simplify provider server and testing implementation. +func New(version string) func() provider.Provider { + return func() provider.Provider { + return &proxmoxProvider{ + version: version, + } + } +} + +type proxmoxProvider struct { + // version is set to the provider version on release, "dev" when the + // provider is built and ran locally, and "test" when running acceptance + // testing. + version string +} + +// proxmoxProviderModel maps provider schema data. +type proxmoxProviderModel struct { + APIToken types.String `tfsdk:"api_token"` + Endpoint types.String `tfsdk:"endpoint"` + Insecure types.Bool `tfsdk:"insecure"` + OTP types.String `tfsdk:"otp"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + SSH []struct { + Agent types.Bool `tfsdk:"agent"` + AgentSocket types.String `tfsdk:"agent_socket"` + Password types.String `tfsdk:"password"` + Username types.String `tfsdk:"username"` + + Nodes []struct { + Name types.String `tfsdk:"name"` + Address types.String `tfsdk:"address"` + } `tfsdk:"node"` + } `tfsdk:"ssh"` +} + +func (p *proxmoxProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + // resp.TypeName = "proxmox" + resp.TypeName = "proxmox_virtual_environment" + resp.Version = p.version +} + +func (p *proxmoxProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema.Schema{ + // Attributes specified in alphabetical order. + Attributes: map[string]schema.Attribute{ + "api_token": schema.StringAttribute{ + Description: "The API token for the Proxmox VE API.", + Optional: true, + Sensitive: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^\S+@\w+!\S+=([a-zA-Z0-9-]+)$`), + `must be a valid API token, e.g. 'USER@REALM!TOKENID=UUID'`, + ), + }, + }, + "endpoint": schema.StringAttribute{ + Description: "The endpoint for the Proxmox VE API.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + }, + "insecure": schema.BoolAttribute{ + Description: "Whether to skip the TLS verification step.", + Optional: true, + }, + "otp": schema.StringAttribute{ + Description: "The one-time password for the Proxmox VE API.", + Optional: true, + DeprecationMessage: "The `otp` attribute is deprecated and will be removed in a future release. " + + "Please use the `api_token` attribute instead.", + }, + "password": schema.StringAttribute{ + Description: "The password for the Proxmox VE API.", + Optional: true, + Sensitive: true, + }, + "username": schema.StringAttribute{ + Description: "The username for the Proxmox VE API.", + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + // have to define it as a list due to backwards compatibility + "ssh": schema.ListNestedBlock{ + Description: "The SSH configuration for the Proxmox nodes.", + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "agent": schema.BoolAttribute{ + Description: "Whether to use the SSH agent for authentication. " + + "Defaults to `false`.", + Optional: true, + }, + "agent_socket": schema.StringAttribute{ + Description: "The path to the SSH agent socket. " + + "Defaults to the value of the `SSH_AUTH_SOCK` " + + "environment variable.", + Optional: true, + }, + "password": schema.StringAttribute{ + Description: "The password used for the SSH connection. " + + "Defaults to the value of the `password` field of the " + + "`provider` block.", + Optional: true, + Sensitive: true, + }, + "username": schema.StringAttribute{ + Description: "The username used for the SSH connection. " + + "Defaults to the value of the `username` field of the " + + "`provider` block.", + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "node": schema.ListNestedBlock{ + Description: "Overrides for SSH connection configuration for a Proxmox VE node.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Description: "The name of the Proxmox VE node.", + Required: true, + }, + "address": schema.StringAttribute{ + Description: "The address of the Proxmox VE node.", + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (p *proxmoxProvider) Configure( + ctx context.Context, + req provider.ConfigureRequest, + resp *provider.ConfigureResponse, +) { + tflog.Info(ctx, "Configuring the Proxmox provider...") + + // Retrieve provider data from configuration + var config proxmoxProviderModel + diags := req.Config.Get(ctx, &config) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + // If practitioner provided a configuration value for any of the + // attributes, it must be a known value. + + if config.Endpoint.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("endpoint"), + "Unknown Proxmox VE API Endpoint", + "The provider cannot create the Proxmox VE API client as there is an unknown configuration value "+ + "for the API endpoint. Either target apply the source of the value first, set the value statically in "+ + "the configuration, or use the PROXMOX_VE_ENDPOINT environment variable.", + ) + } + + if resp.Diagnostics.HasError() { + return + } + + // Default values to environment variables, but override + // with Terraform configuration value if set. + + // Check environment variables + apiToken := utils.GetAnyStringEnv("PROXMOX_VE_API_TOKEN") + endpoint := utils.GetAnyStringEnv("PROXMOX_VE_ENDPOINT") + insecure := utils.GetAnyBoolEnv("PROXMOX_VE_INSECURE") + username := utils.GetAnyStringEnv("PROXMOX_VE_USERNAME") + password := utils.GetAnyStringEnv("PROXMOX_VE_PASSWORD") + + if !config.APIToken.IsNull() { + apiToken = config.APIToken.ValueString() + } + + if !config.Endpoint.IsNull() { + endpoint = config.Endpoint.ValueString() + } + + if !config.Insecure.IsNull() { + insecure = config.Insecure.ValueBool() + } + + if !config.Username.IsNull() { + username = config.Username.ValueString() + } + + if !config.Password.IsNull() { + password = config.Password.ValueString() + } + + if endpoint == "" { + resp.Diagnostics.AddAttributeError( + path.Root("endpoint"), + "Missing Proxmox VE API Endpoint", + "The provider cannot create the Proxmox VE API client as there is a missing or empty value for the API endpoint. "+ + "Set the host value in the configuration or use the PROXMOX_VE_ENDPOINT environment variable. "+ + "If either is already set, ensure the value is not empty.", + ) + } + + if resp.Diagnostics.HasError() { + return + } + + // Create the Proxmox VE API client + + creds, err := api.NewCredentials(username, password, "", apiToken) + if err != nil { + resp.Diagnostics.AddError( + "Unable to create Proxmox VE API credentials", + err.Error(), + ) + } + + conn, err := api.NewConnection( + endpoint, + insecure, + ) + if err != nil { + resp.Diagnostics.AddError( + "Unable to create Proxmox VE API connection", + err.Error(), + ) + } + + if resp.Diagnostics.HasError() { + return + } + + apiClient, err := api.NewClient(creds, conn) + if err != nil { + resp.Diagnostics.AddError( + "Unable to create Proxmox VE API client", + err.Error(), + ) + } + + sshUsername := utils.GetAnyStringEnv("PROXMOX_VE_SSH_USERNAME") + sshPassword := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PASSWORD") + sshAgent := utils.GetAnyBoolEnv("PROXMOX_VE_SSH_AGENT") + sshAgentSocket := utils.GetAnyStringEnv("SSH_AUTH_SOCK", "PROXMOX_VE_SSH_AUTH_SOCK") + nodeOverrides := map[string]string{} + + if len(config.SSH) > 0 { + if !config.SSH[0].Username.IsNull() { + sshUsername = config.SSH[0].Username.ValueString() + } + + if !config.SSH[0].Password.IsNull() { + sshPassword = config.SSH[0].Password.ValueString() + } + + if !config.SSH[0].Agent.IsNull() { + sshAgent = config.SSH[0].Agent.ValueBool() + } + + if !config.SSH[0].AgentSocket.IsNull() { + sshAgentSocket = config.SSH[0].AgentSocket.ValueString() + } + + for _, n := range config.SSH[0].Nodes { + nodeOverrides[n.Name.ValueString()] = n.Address.ValueString() + } + } + + if sshUsername == "" { + sshUsername = strings.Split(creds.Username, "@")[0] + } + + if sshPassword == "" { + sshPassword = creds.Password + } + + sshClient, err := ssh.NewClient( + sshUsername, sshPassword, sshAgent, sshAgentSocket, + &apiResolverWithOverrides{ + ar: apiResolver{c: apiClient}, + overrides: nodeOverrides, + }, + ) + if err != nil { + resp.Diagnostics.AddError( + "Unable to create Proxmox VE SSH client", + err.Error(), + ) + } + + if resp.Diagnostics.HasError() { + return + } + + client := proxmox.NewClient(apiClient, sshClient) + + resp.ResourceData = client + resp.DataSourceData = client +} + +func (p *proxmoxProvider) Resources(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + network.NewLinuxBridgeResource, + network.NewLinuxVLANResource, + } +} + +func (p *proxmoxProvider) DataSources(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{} +} + +type apiResolver struct { + c api.Client +} + +func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, error) { + nc := &nodes.Client{Client: r.c, NodeName: nodeName} + + networkDevices, err := nc.ListNetworkInterfaces(ctx) + if err != nil { + return "", fmt.Errorf("failed to list network devices of node \"%s\": %w", nc.NodeName, err) + } + + nodeAddress := "" + + for _, d := range networkDevices { + if d.Address != nil { + nodeAddress = *d.Address + break + } + } + + if nodeAddress == "" { + return "", fmt.Errorf("failed to determine the IP address of node \"%s\"", nc.NodeName) + } + + nodeAddressParts := strings.Split(nodeAddress, "/") + + return nodeAddressParts[0], nil +} + +type apiResolverWithOverrides struct { + ar apiResolver + overrides map[string]string +} + +func (r *apiResolverWithOverrides) Resolve(ctx context.Context, nodeName string) (string, error) { + if ip, ok := r.overrides[nodeName]; ok { + return ip, nil + } + + return r.ar.Resolve(ctx, nodeName) +} diff --git a/internal/test/provider_test.go b/internal/test/provider_test.go new file mode 100644 index 00000000..03131999 --- /dev/null +++ b/internal/test/provider_test.go @@ -0,0 +1,41 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package test + +import ( + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/bpg/terraform-provider-proxmox/internal/provider" +) + +const ( + // ProviderConfig is a shared configuration to combine with the actual + // test configuration so the Proxmox VE client is properly configured. + // It is also possible to use the PROXMOX_VE_ environment variables instead,. + ProviderConfig = ` +provider "proxmox" { + username = "root@pam" + password = "password" + insecure = true + ssh { + agent = true + } +} +` + // such as updating the Makefile and running the testing through that tool. +) + +// AccTestProtoV6ProviderFactories 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. +// +//nolint:gochecknoglobals +var AccTestProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ + "proxmox": providerserver.NewProtocol6WithError(provider.New("test")()), +} diff --git a/internal/test/resource_linux_bridge_test.go b/internal/test/resource_linux_bridge_test.go new file mode 100644 index 00000000..328c8e8d --- /dev/null +++ b/internal/test/resource_linux_bridge_test.go @@ -0,0 +1,74 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func Test_LinuxBridgeResource(t *testing.T) { + t.Parallel() + + resourceName := "proxmox_virtual_environment_network_linux_bridge.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: AccTestProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: ProviderConfig + ` +resource "proxmox_virtual_environment_network_linux_bridge" "test" { + node_name = "pve" + name = "vmbr99" + address = "3.3.3.3/24" + comment = "created by terraform" + mtu = 1499 +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "vmbr99"), + resource.TestCheckResourceAttr(resourceName, "address", "3.3.3.3/24"), + resource.TestCheckResourceAttr(resourceName, "comment", "created by terraform"), + resource.TestCheckResourceAttr(resourceName, "vlan_aware", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1499"), + resource.TestCheckResourceAttrSet(resourceName, "id"), + ), + }, + // ImportState testing + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + // Update testing + { + Config: ProviderConfig + ` +resource "proxmox_virtual_environment_network_linux_bridge" "test" { + node_name = "pve" + name = "vmbr99" + address = "1.1.1.1/24" + address6 = "FE80:0000:0000:0000:0202:B3FF:FE1E:8329/64" + comment = "updated by terraform" + vlan_aware = false + mtu = null +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "vmbr99"), + resource.TestCheckResourceAttr(resourceName, "address", "1.1.1.1/24"), + resource.TestCheckResourceAttr(resourceName, "address6", "FE80:0000:0000:0000:0202:B3FF:FE1E:8329/64"), + resource.TestCheckResourceAttr(resourceName, "comment", "updated by terraform"), + resource.TestCheckResourceAttr(resourceName, "vlan_aware", "false"), + resource.TestCheckNoResourceAttr(resourceName, "mtu"), + resource.TestCheckResourceAttrSet(resourceName, "id"), + ), + }, + }, + }) +} diff --git a/internal/test/resource_linux_vlan_test.go b/internal/test/resource_linux_vlan_test.go new file mode 100644 index 00000000..4171a42e --- /dev/null +++ b/internal/test/resource_linux_vlan_test.go @@ -0,0 +1,72 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func Test_LinuxVLANResource(t *testing.T) { + t.Parallel() + + resourceName := "proxmox_virtual_environment_network_linux_vlan.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: AccTestProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: ProviderConfig + ` +resource "proxmox_virtual_environment_network_linux_vlan" "test" { + node_name = "pve" + name = "ens18.33" + comment = "created by terraform" + mtu = 1499 +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "ens18.33"), + resource.TestCheckResourceAttr(resourceName, "comment", "created by terraform"), + resource.TestCheckResourceAttr(resourceName, "vlan", "33"), + resource.TestCheckResourceAttr(resourceName, "interface", "ens18"), + resource.TestCheckResourceAttrSet(resourceName, "id"), + ), + }, + // ImportState testing + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + // Update testing + { + Config: ProviderConfig + ` +resource "proxmox_virtual_environment_network_linux_vlan" "test" { + node_name = "pve" + name = "ens18.33" + address = "1.1.1.1/24" + address6 = "FE80:0000:0000:0000:0202:B3FF:FE1E:8329/64" + comment = "updated by terraform" + mtu = null +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "ens18.33"), + resource.TestCheckResourceAttr(resourceName, "address", "1.1.1.1/24"), + resource.TestCheckResourceAttr(resourceName, "address6", "FE80:0000:0000:0000:0202:B3FF:FE1E:8329/64"), + resource.TestCheckResourceAttr(resourceName, "comment", "updated by terraform"), + resource.TestCheckResourceAttr(resourceName, "vlan", "33"), + resource.TestCheckResourceAttr(resourceName, "interface", "ens18"), + resource.TestCheckNoResourceAttr(resourceName, "mtu"), + resource.TestCheckResourceAttrSet(resourceName, "id"), + ), + }, + }, + }) +} diff --git a/proxmox/types/common_types.go b/internal/types/common_types.go similarity index 94% rename from proxmox/types/common_types.go rename to internal/types/common_types.go index e503a0f6..0ec037cb 100644 --- a/proxmox/types/common_types.go +++ b/internal/types/common_types.go @@ -53,6 +53,16 @@ func (r *CustomBool) UnmarshalJSON(b []byte) error { return nil } +// Pointer returns a pointers. +func (r CustomBool) Pointer() *CustomBool { + return &r +} + +// PointerBool returns a pointer to a boolean. +func (r *CustomBool) PointerBool() *bool { + return (*bool)(r) +} + // MarshalJSON converts a boolean to a JSON value. func (r *CustomCommaSeparatedList) MarshalJSON() ([]byte, error) { s := strings.Join(*r, ",") diff --git a/internal/types/ip_addr.go b/internal/types/ip_addr.go new file mode 100644 index 00000000..a2fc8bc4 --- /dev/null +++ b/internal/types/ip_addr.go @@ -0,0 +1,118 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package types + +import ( + "context" + "fmt" + "net" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisfies the expected interfaces. +var _ basetypes.StringTypable = IPAddrType{} + +// IPAddrType is a type that represents an IP address. +type IPAddrType struct { + basetypes.StringType +} + +// Equal returns true if the two types are equal. +func (t IPAddrType) Equal(o attr.Type) bool { + other, ok := o.(IPAddrType) + + if !ok { + return false + } + + return t.StringType.Equal(other.StringType) +} + +// String returns a string representation of the type. +func (t IPAddrType) String() string { + return "IPAddrType" +} + +// ValueFromString converts a string value to a StringValuable. +func (t IPAddrType) ValueFromString( + _ context.Context, in basetypes.StringValue, +) (basetypes.StringValuable, diag.Diagnostics) { + value := IPAddrValue{ + StringValue: in, + } + + return value, nil +} + +// ValueFromTerraform converts a Terraform value to a StringValuable. +func (t IPAddrType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + attrValue, err := t.StringType.ValueFromTerraform(ctx, in) + if err != nil { + return nil, fmt.Errorf("unexpected error converting Terraform value to StringValue: %w", err) + } + + stringValue, ok := attrValue.(basetypes.StringValue) + if !ok { + return nil, fmt.Errorf("unexpected value type of %T", attrValue) + } + + stringValuable, diags := t.ValueFromString(ctx, stringValue) + if diags.HasError() { + return nil, fmt.Errorf("unexpected error converting StringValue to StringValuable: %v", diags) + } + + return stringValuable, nil +} + +// ValueType returns the underlying value type. +func (t IPAddrType) ValueType(_ context.Context) attr.Value { + return IPAddrValue{} +} + +// Validate ensures the value is valid IP address. +func (t IPAddrType) Validate(_ context.Context, value tftypes.Value, valuePath path.Path) diag.Diagnostics { + if value.IsNull() || !value.IsKnown() { + return nil + } + + var diags diag.Diagnostics + + var valueString string + + if err := value.As(&valueString); err != nil { + diags.AddAttributeError( + valuePath, + "Invalid Terraform Value", + "An unexpected error occurred while attempting to convert a Terraform value to a string. "+ + "This generally is an issue with the provider schema implementation. "+ + "Please contact the provider developers.\n\n"+ + "Path: "+valuePath.String()+"\n"+ + "Error: "+err.Error(), + ) + + return diags + } + + if ip := net.ParseIP(valueString); ip == nil { + diags.AddAttributeError( + valuePath, + "Invalid IP String Value", + "An unexpected error occurred while converting a string value that was expected to be IPv4/IPv6.\n\n"+ + "Path: "+valuePath.String()+"\n"+ + "Given Value: "+valueString, + ) + + return diags + } + + return diags +} diff --git a/internal/types/ip_addr_test.go b/internal/types/ip_addr_test.go new file mode 100644 index 00000000..63892b82 --- /dev/null +++ b/internal/types/ip_addr_test.go @@ -0,0 +1,122 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package types + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func Test_IPAddrTypeValueFromTerraform(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + val tftypes.Value + expected func(val IPAddrValue) bool + expectError bool + }{ + "null value": { + val: tftypes.NewValue(tftypes.String, nil), + expected: func(val IPAddrValue) bool { + return val.IsNull() + }, + }, + "unknown value": { + val: tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + expected: func(val IPAddrValue) bool { + return val.IsUnknown() + }, + }, + "valid IPv4": { + val: tftypes.NewValue(tftypes.String, "1.2.3.4"), + expected: func(val IPAddrValue) bool { + return val.ValueString() == "1.2.3.4" + }, + }, + "valid IPv6": { + val: tftypes.NewValue(tftypes.String, "2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + expected: func(val IPAddrValue) bool { + return val.ValueString() == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + }, + }, + } + + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.TODO() + val, err := IPAddrType{}.ValueFromTerraform(ctx, test.val) + + if err == nil && test.expectError { + t.Fatal("expected error, got no error") + } + if err != nil && !test.expectError { + t.Fatalf("got unexpected error: %s", err) + } + + if !test.expected(val.(IPAddrValue)) { + t.Errorf("unexpected result") + } + }) + } +} + +func Test_IPAddrTypeValidate(t *testing.T) { + t.Parallel() + + type testCase struct { + val tftypes.Value + expectError bool + } + + tests := map[string]testCase{ + "not a string": { + val: tftypes.NewValue(tftypes.Bool, true), + expectError: true, + }, + "unknown string": { + val: tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + }, + "null string": { + val: tftypes.NewValue(tftypes.String, nil), + }, + "valid IPv4 string": { + val: tftypes.NewValue(tftypes.String, "1.2.3.4"), + }, + "valid IPv6 string": { + val: tftypes.NewValue(tftypes.String, "2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + }, + "invalid string": { + val: tftypes.NewValue(tftypes.String, "not ok"), + expectError: true, + }, + } + + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.TODO() + + diags := IPAddrType{}.Validate(ctx, test.val, path.Root("test")) + + if !diags.HasError() && test.expectError { + t.Fatal("expected error, got no error") + } + + if diags.HasError() && !test.expectError { + t.Fatalf("got unexpected error: %s", diags) + } + }) + } +} diff --git a/internal/types/ip_addr_value.go b/internal/types/ip_addr_value.go new file mode 100644 index 00000000..f1a8a706 --- /dev/null +++ b/internal/types/ip_addr_value.go @@ -0,0 +1,46 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package types + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the expected interfaces. +var _ basetypes.StringValuable = IPAddrValue{} + +// IPAddrValue is a type that represents an IP address value. +type IPAddrValue struct { + basetypes.StringValue +} + +// Equal returns true if the two values are equal. +func (v IPAddrValue) Equal(o attr.Value) bool { + other, ok := o.(IPAddrValue) + + if !ok { + return false + } + + return v.StringValue.Equal(other.StringValue) +} + +// Type returns the type of the value. +func (v IPAddrValue) Type(_ context.Context) attr.Type { + return IPAddrType{} +} + +// NewIPAddrPointerValue returns a new IPAddrValue from a string pointer. +func NewIPAddrPointerValue(value *string) IPAddrValue { + return IPAddrValue{ + StringValue: types.StringPointerValue(value), + } +} diff --git a/internal/types/ip_cidr.go b/internal/types/ip_cidr.go new file mode 100644 index 00000000..ceb3955f --- /dev/null +++ b/internal/types/ip_cidr.go @@ -0,0 +1,119 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package types + +import ( + "context" + "fmt" + "net" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisfies the expected interfaces. +var _ basetypes.StringTypable = IPCIDRType{} + +// IPCIDRType is a type that represents an IP address in CIDR notation. +type IPCIDRType struct { + basetypes.StringType +} + +// Equal returns true if the two types are equal. +func (t IPCIDRType) Equal(o attr.Type) bool { + other, ok := o.(IPCIDRType) + + if !ok { + return false + } + + return t.StringType.Equal(other.StringType) +} + +// String returns a string representation of the type. +func (t IPCIDRType) String() string { + return "IPCIDRType" +} + +// ValueFromString converts a string value to a StringValuable. +func (t IPCIDRType) ValueFromString( + _ context.Context, in basetypes.StringValue, +) (basetypes.StringValuable, diag.Diagnostics) { + value := IPCIDRValue{ + StringValue: in, + } + + return value, nil +} + +// ValueFromTerraform converts a Terraform value to a StringValuable. +func (t IPCIDRType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + attrValue, err := t.StringType.ValueFromTerraform(ctx, in) + if err != nil { + return nil, fmt.Errorf("unexpected error converting Terraform value to StringValue: %w", err) + } + + stringValue, ok := attrValue.(basetypes.StringValue) + if !ok { + return nil, fmt.Errorf("unexpected value type of %T", attrValue) + } + + stringValuable, diags := t.ValueFromString(ctx, stringValue) + if diags.HasError() { + return nil, fmt.Errorf("unexpected error converting StringValue to StringValuable: %v", diags) + } + + return stringValuable, nil +} + +// ValueType returns the underlying value type. +func (t IPCIDRType) ValueType(_ context.Context) attr.Value { + return IPCIDRValue{} +} + +// Validate ensures the value is valid IP address in CIDR notation. +func (t IPCIDRType) Validate(_ context.Context, value tftypes.Value, valuePath path.Path) diag.Diagnostics { + if value.IsNull() || !value.IsKnown() { + return nil + } + + var diags diag.Diagnostics + + var valueString string + + if err := value.As(&valueString); err != nil { + diags.AddAttributeError( + valuePath, + "Invalid Terraform Value", + "An unexpected error occurred while attempting to convert a Terraform value to a string. "+ + "This generally is an issue with the provider schema implementation. "+ + "Please contact the provider developers.\n\n"+ + "Path: "+valuePath.String()+"\n"+ + "Error: "+err.Error(), + ) + + return diags + } + + if _, _, err := net.ParseCIDR(valueString); err != nil { + diags.AddAttributeError( + valuePath, + "Invalid IP/CIDR String Value", + "An unexpected error occurred while converting a string value that was expected to be IP/CIDR.\n\n"+ + "Path: "+valuePath.String()+"\n"+ + "Given Value: "+valueString+"\n"+ + "Error: "+err.Error(), + ) + + return diags + } + + return diags +} diff --git a/internal/types/ip_cidr_test.go b/internal/types/ip_cidr_test.go new file mode 100644 index 00000000..bf37d841 --- /dev/null +++ b/internal/types/ip_cidr_test.go @@ -0,0 +1,130 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package types + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func Test_IPCIDRTypeValueFromTerraform(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + val tftypes.Value + expected func(val IPCIDRValue) bool + expectError bool + }{ + "null value": { + val: tftypes.NewValue(tftypes.String, nil), + expected: func(val IPCIDRValue) bool { + return val.IsNull() + }, + }, + "unknown value": { + val: tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + expected: func(val IPCIDRValue) bool { + return val.IsUnknown() + }, + }, + "valid IPv4/CIDR": { + val: tftypes.NewValue(tftypes.String, "1.2.3.4/32"), + expected: func(val IPCIDRValue) bool { + return val.ValueString() == "1.2.3.4/32" + }, + }, + "valid IPv6/CIDR": { + val: tftypes.NewValue(tftypes.String, "2001:db8::/32"), + expected: func(val IPCIDRValue) bool { + return val.ValueString() == "2001:db8::/32" + }, + }, + } + + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.TODO() + val, err := IPCIDRType{}.ValueFromTerraform(ctx, test.val) + + if err == nil && test.expectError { + t.Fatal("expected error, got no error") + } + if err != nil && !test.expectError { + t.Fatalf("got unexpected error: %s", err) + } + + if !test.expected(val.(IPCIDRValue)) { + t.Errorf("unexpected result") + } + }) + } +} + +func Test_IPCIDRTypeValidate(t *testing.T) { + t.Parallel() + + type testCase struct { + val tftypes.Value + expectError bool + } + + tests := map[string]testCase{ + "not a string": { + val: tftypes.NewValue(tftypes.Bool, true), + expectError: true, + }, + "unknown string": { + val: tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + }, + "null string": { + val: tftypes.NewValue(tftypes.String, nil), + }, + "valid IPv4 string": { + val: tftypes.NewValue(tftypes.String, "1.2.3.4/32"), + }, + "valid IPv6 string": { + val: tftypes.NewValue(tftypes.String, "2001:db8::/32"), + }, + "invalid string": { + val: tftypes.NewValue(tftypes.String, "not ok"), + expectError: true, + }, + "invalid IPv4 string no CIDR": { + val: tftypes.NewValue(tftypes.String, "1.2.3.4"), + expectError: true, + }, + "invalid IPv6 string no CIDR": { + val: tftypes.NewValue(tftypes.String, "2001:db8::"), + expectError: true, + }, + } + + for name, test := range tests { + name, test := name, test + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.TODO() + + diags := IPCIDRType{}.Validate(ctx, test.val, path.Root("test")) + + if !diags.HasError() && test.expectError { + t.Fatal("expected error, got no error") + } + + if diags.HasError() && !test.expectError { + t.Fatalf("got unexpected error: %s", diags) + } + }) + } +} diff --git a/internal/types/ip_cidr_value.go b/internal/types/ip_cidr_value.go new file mode 100644 index 00000000..c01f169c --- /dev/null +++ b/internal/types/ip_cidr_value.go @@ -0,0 +1,46 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package types + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the expected interfaces. +var _ basetypes.StringValuable = IPCIDRValue{} + +// IPCIDRValue is a type that represents an IP address in CIDR notation. +type IPCIDRValue struct { + basetypes.StringValue +} + +// Equal returns true if the two values are equal. +func (v IPCIDRValue) Equal(o attr.Value) bool { + other, ok := o.(IPCIDRValue) + + if !ok { + return false + } + + return v.StringValue.Equal(other.StringValue) +} + +// Type returns the type of the value. +func (v IPCIDRValue) Type(_ context.Context) attr.Type { + return IPCIDRType{} +} + +// NewIPCIDRPointerValue returns a new IPCIDRValue from a string pointer. +func NewIPCIDRPointerValue(value *string) IPCIDRValue { + return IPCIDRValue{ + StringValue: types.StringPointerValue(value), + } +} diff --git a/main.go b/main.go index 5b39cac1..920d4dc9 100644 --- a/main.go +++ b/main.go @@ -1,31 +1,80 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* + * 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 https://mozilla.org/MPL/2.0/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ package main import ( + "context" "flag" + "log" + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server" + "github.com/hashicorp/terraform-plugin-mux/tf5to6server" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" + newProvider "github.com/bpg/terraform-provider-proxmox/internal/provider" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/provider" ) +// If you do not have terraform installed, you can remove the formatting command, but it's suggested to +// ensure the documentation is formatted properly. +//go:generate terraform fmt -recursive ./example/ + +// Run the docs generation tool, check its repository for more information on how it works and how docs +// can be customized. +//go:generate go run -modfile=tools/go.mod github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs + func main() { + ctx := context.Background() + var debug bool flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") flag.Parse() - opts := &plugin.ServeOpts{ - Debug: debug, - ProviderAddr: "registry.terraform.io/bpg/proxmox", - ProviderFunc: func() *schema.Provider { - return provider.ProxmoxVirtualEnvironment() + upgradedSdkServer, err := tf5to6server.UpgradeServer( + ctx, + func() tfprotov5.ProviderServer { + return schema.NewGRPCProviderServer( + provider.ProxmoxVirtualEnvironment(), + ) + }, + ) + if err != nil { + log.Fatal(err) + } + + providers := []func() tfprotov6.ProviderServer{ + providerserver.NewProtocol6(newProvider.New("dev")()), + func() tfprotov6.ProviderServer { + return upgradedSdkServer }, } - plugin.Serve(opts) + muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...) + if err != nil { + log.Fatal(err) + } + + var serveOpts []tf6server.ServeOpt + + if debug { + serveOpts = append(serveOpts, tf6server.WithManagedDebug()) + } + + err = tf6server.Serve( + "registry.terraform.io/bpg/proxmox", + muxServer.ProviderServer, + serveOpts..., + ) + if err != nil { + log.Fatal(err) + } } diff --git a/proxmox/access/acl_types.go b/proxmox/access/acl_types.go index db7eba15..851c8606 100644 --- a/proxmox/access/acl_types.go +++ b/proxmox/access/acl_types.go @@ -6,7 +6,9 @@ package access -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // ACLGetResponseBody contains the body from an access control list response. type ACLGetResponseBody struct { diff --git a/proxmox/access/roles.go b/proxmox/access/roles.go index 9bc3266f..009ded95 100644 --- a/proxmox/access/roles.go +++ b/proxmox/access/roles.go @@ -13,8 +13,8 @@ import ( "net/url" "sort" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" ) func (c *Client) rolesPath() string { diff --git a/proxmox/access/roles_types.go b/proxmox/access/roles_types.go index 36cc6f76..e9096b92 100644 --- a/proxmox/access/roles_types.go +++ b/proxmox/access/roles_types.go @@ -6,7 +6,9 @@ package access -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // RoleCreateRequestBody contains the data for an access group create request. type RoleCreateRequestBody struct { diff --git a/proxmox/access/users.go b/proxmox/access/users.go index d7fed0c3..a1f3dfec 100644 --- a/proxmox/access/users.go +++ b/proxmox/access/users.go @@ -14,8 +14,8 @@ import ( "sort" "time" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" ) func (c *Client) usersPath() string { diff --git a/proxmox/access/users_types.go b/proxmox/access/users_types.go index 26a1f380..90ac5083 100644 --- a/proxmox/access/users_types.go +++ b/proxmox/access/users_types.go @@ -6,7 +6,9 @@ package access -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // UserChangePasswordRequestBody contains the data for a user password change request. type UserChangePasswordRequestBody struct { diff --git a/proxmox/api/authenticator.go b/proxmox/api/authenticator.go index 4054d875..7fc483f4 100644 --- a/proxmox/api/authenticator.go +++ b/proxmox/api/authenticator.go @@ -7,6 +7,7 @@ package api import ( + "context" "net/http" ) @@ -18,5 +19,5 @@ type Authenticator interface { IsRoot() bool // AuthenticateRequest adds authentication data to a new request. - AuthenticateRequest(req *http.Request) error + AuthenticateRequest(ctx context.Context, req *http.Request) error } diff --git a/proxmox/api/client.go b/proxmox/api/client.go index 594b80b5..e4979989 100644 --- a/proxmox/api/client.go +++ b/proxmox/api/client.go @@ -17,6 +17,7 @@ import ( "net/http" "net/url" "strings" + "time" "github.com/google/go-querystring/query" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -29,7 +30,9 @@ import ( var ErrNoDataObjectInResponse = errors.New("the server did not include a data object in the response") const ( - basePathJSONAPI = "api2/json" + // the large timeout is to allow for large file uploads. + httpClientTimeout = 5 * time.Minute + basePathJSONAPI = "api2/json" ) // Client is an interface for performing requests against the Proxmox API. @@ -83,8 +86,11 @@ func NewConnection(endpoint string, insecure bool) (*Connection, error) { } return &Connection{ - endpoint: strings.TrimRight(u.String(), "/"), - httpClient: &http.Client{Transport: transport}, + endpoint: strings.TrimRight(u.String(), "/"), + httpClient: &http.Client{ + Transport: transport, + Timeout: httpClientTimeout, + }, }, nil } @@ -95,7 +101,7 @@ type client struct { } // NewClient creates and initializes a VirtualEnvironmentClient instance. -func NewClient(ctx context.Context, creds *Credentials, conn *Connection) (Client, error) { +func NewClient(creds *Credentials, conn *Connection) (Client, error) { if creds == nil { return nil, errors.New("credentials must not be nil") } @@ -111,7 +117,7 @@ func NewClient(ctx context.Context, creds *Credentials, conn *Connection) (Clien if creds.APIToken != nil { auth, err = NewTokenAuthenticator(*creds.APIToken) } else { - auth, err = NewTicketAuthenticator(ctx, conn, creds) + auth, err = NewTicketAuthenticator(conn, creds) } if err != nil { @@ -202,7 +208,7 @@ func (c *client) DoRequest( req.Header.Add("Content-Type", reqBodyType) } - err = c.auth.AuthenticateRequest(req) + err = c.auth.AuthenticateRequest(ctx, req) if err != nil { return fmt.Errorf("failed to authenticate HTTP %s request (path: %s) - Reason: %w", method, @@ -228,6 +234,7 @@ func (c *client) DoRequest( return err } + //nolint:nestif if responseBody != nil { err = json.NewDecoder(res.Body).Decode(responseBody) if err != nil { @@ -248,14 +255,27 @@ func (c *client) DoRequest( err, ) } - tflog.Warn(ctx, "unhandled HTTP response body", map[string]interface{}{ - "data": string(data), - }) + if len(data) > 0 { + dr := dataResponse{} + + if err2 := json.NewDecoder(bytes.NewReader(data)).Decode(&dr); err2 == nil { + if dr.Data == nil { + return nil + } + } + tflog.Warn(ctx, "unhandled HTTP response body", map[string]interface{}{ + "data": dr.Data, + }) + } } return nil } +type dataResponse struct { + Data interface{} `json:"data"` +} + // ExpandPath expands the given path to an absolute path. func (c *client) ExpandPath(path string) string { return path diff --git a/proxmox/api/ticket_auth.go b/proxmox/api/ticket_auth.go index 213af82e..263c1145 100644 --- a/proxmox/api/ticket_auth.go +++ b/proxmox/api/ticket_auth.go @@ -21,12 +21,14 @@ import ( ) type ticketAuthenticator struct { - authenticationData *AuthenticationResponseData + conn *Connection + authRequest string + authData *AuthenticationResponseData } // NewTicketAuthenticator returns a new ticket authenticator. -func NewTicketAuthenticator(ctx context.Context, conn *Connection, creds *Credentials) (Authenticator, error) { - reqStr := fmt.Sprintf( +func NewTicketAuthenticator(conn *Connection, creds *Credentials) (Authenticator, error) { + authRequest := fmt.Sprintf( "username=%s&password=%s", url.QueryEscape(creds.Username), url.QueryEscape(creds.Password), @@ -34,14 +36,25 @@ func NewTicketAuthenticator(ctx context.Context, conn *Connection, creds *Creden // OTP is optional, and probably doesn't make much sense for most provider users. if creds.OTP != nil { - reqStr = fmt.Sprintf("%s&otp=%s", reqStr, url.QueryEscape(*creds.OTP)) + authRequest = fmt.Sprintf("%s&otp=%s", authRequest, url.QueryEscape(*creds.OTP)) + } + + return &ticketAuthenticator{ + conn: conn, + authRequest: authRequest, + }, nil +} + +func (t *ticketAuthenticator) authenticate(ctx context.Context) (*AuthenticationResponseData, error) { + if t.authData != nil { + return t.authData, nil } req, err := http.NewRequestWithContext( ctx, http.MethodPost, - fmt.Sprintf("%s/%s/access/ticket", conn.endpoint, basePathJSONAPI), - bytes.NewBufferString(reqStr), + fmt.Sprintf("%s/%s/access/ticket", t.conn.endpoint, basePathJSONAPI), + bytes.NewBufferString(t.authRequest), ) if err != nil { return nil, fmt.Errorf("failed to create authentication request: %w", err) @@ -49,12 +62,12 @@ func NewTicketAuthenticator(ctx context.Context, conn *Connection, creds *Creden req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - tflog.Debug(ctx, "sending authentication request", map[string]interface{}{ + tflog.Debug(ctx, "Sending authentication request", map[string]interface{}{ "path": req.URL.Path, }) //nolint:bodyclose - res, err := conn.httpClient.Do(req) + res, err := t.conn.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to retrieve authentication response: %w", err) } @@ -91,28 +104,29 @@ func NewTicketAuthenticator(ctx context.Context, conn *Connection, creds *Creden return nil, errors.New("the server did not include the username in the authentication response") } - return &ticketAuthenticator{ - authenticationData: resBody.Data, - }, nil + t.authData = resBody.Data + + return resBody.Data, nil } func (t *ticketAuthenticator) IsRoot() bool { - return t.authenticationData.Username == rootUsername + return t.authData != nil && t.authData.Username == rootUsername } // AuthenticateRequest adds authentication data to a new request. -func (t *ticketAuthenticator) AuthenticateRequest(req *http.Request) error { - if t.authenticationData == nil { - return errors.New("failed to authenticate: no ticket") +func (t *ticketAuthenticator) AuthenticateRequest(ctx context.Context, req *http.Request) error { + a, err := t.authenticate(ctx) + if err != nil { + return fmt.Errorf("failed to authenticate: %w", err) } req.AddCookie(&http.Cookie{ Name: "PVEAuthCookie", - Value: *t.authenticationData.Ticket, + Value: *a.Ticket, }) if req.Method != http.MethodGet { - req.Header.Add("CSRFPreventionToken", *t.authenticationData.CSRFPreventionToken) + req.Header.Add("CSRFPreventionToken", *a.CSRFPreventionToken) } return nil diff --git a/proxmox/api/ticket_auth_types.go b/proxmox/api/ticket_auth_types.go index 6fae4162..97fcd38b 100644 --- a/proxmox/api/ticket_auth_types.go +++ b/proxmox/api/ticket_auth_types.go @@ -6,7 +6,9 @@ package api -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // AuthenticationResponseBody contains the body from an authentication response. type AuthenticationResponseBody struct { diff --git a/proxmox/api/token_auth.go b/proxmox/api/token_auth.go index ea70c2f9..77aae2c1 100644 --- a/proxmox/api/token_auth.go +++ b/proxmox/api/token_auth.go @@ -7,6 +7,7 @@ package api import ( + "context" "net/http" "strings" ) @@ -29,7 +30,7 @@ func (t *tokenAuthenticator) IsRoot() bool { return t.username == rootUsername } -func (t *tokenAuthenticator) AuthenticateRequest(req *http.Request) error { +func (t *tokenAuthenticator) AuthenticateRequest(_ context.Context, req *http.Request) error { req.Header.Set("Authorization", "PVEAPIToken="+t.token) return nil } diff --git a/proxmox/cluster/cluster_types.go b/proxmox/cluster/cluster_types.go index fd8316df..c77cf6d5 100644 --- a/proxmox/cluster/cluster_types.go +++ b/proxmox/cluster/cluster_types.go @@ -1,10 +1,14 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* + * 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 https://mozilla.org/MPL/2.0/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ package cluster -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // NextIDRequestBody contains the data for a cluster next id request. type NextIDRequestBody struct { diff --git a/proxmox/cluster/firewall/options_types.go b/proxmox/cluster/firewall/options_types.go index 102391dc..f618bf31 100644 --- a/proxmox/cluster/firewall/options_types.go +++ b/proxmox/cluster/firewall/options_types.go @@ -13,7 +13,7 @@ import ( "strconv" "strings" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" + "github.com/bpg/terraform-provider-proxmox/internal/types" ) // OptionsPutRequestBody is the request body for the PUT /cluster/firewall/options API call. diff --git a/proxmox/firewall/ipset_types.go b/proxmox/firewall/ipset_types.go index 425c3a83..87d31e25 100644 --- a/proxmox/firewall/ipset_types.go +++ b/proxmox/firewall/ipset_types.go @@ -6,7 +6,9 @@ package firewall -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // IPSetListResponseBody contains the data from an IPSet get response. type IPSetListResponseBody struct { diff --git a/proxmox/firewall/options_types.go b/proxmox/firewall/options_types.go index 967231c0..3f924ab0 100644 --- a/proxmox/firewall/options_types.go +++ b/proxmox/firewall/options_types.go @@ -6,7 +6,9 @@ package firewall -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // OptionsPutRequestBody is the request body for the PUT /cluster/firewall/options API call. type OptionsPutRequestBody struct { diff --git a/proxmox/firewall/rules.go b/proxmox/firewall/rules.go index b6bb62d3..74f3b985 100644 --- a/proxmox/firewall/rules.go +++ b/proxmox/firewall/rules.go @@ -14,8 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" ) // Rule is an interface for the Proxmox firewall rule API. diff --git a/proxmox/firewall/rules_types.go b/proxmox/firewall/rules_types.go index 927c9595..8bf212e6 100644 --- a/proxmox/firewall/rules_types.go +++ b/proxmox/firewall/rules_types.go @@ -26,9 +26,9 @@ type RuleGetResponseData struct { BaseRule // NOTE: This is `int` in the PVE API docs, but it's actually a string in the response. - Pos string `json:"pos" url:"pos"` - Action string `json:"action" url:"action"` - Type string `json:"type" url:"type"` + Pos string `json:"pos" url:"pos"` + Action string `json:"action" url:"action"` + Type string `json:"type" url:"type"` } // RuleListResponseBody contains the data from a firewall rule get response. diff --git a/proxmox/nodes/certificate_types.go b/proxmox/nodes/certificate_types.go index aff0484d..f81f82e3 100644 --- a/proxmox/nodes/certificate_types.go +++ b/proxmox/nodes/certificate_types.go @@ -6,7 +6,9 @@ package nodes -import "github.com/bpg/terraform-provider-proxmox/proxmox/types" +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) // CertificateDeleteRequestBody contains the data for a custom certificate delete request. type CertificateDeleteRequestBody struct { diff --git a/proxmox/nodes/client.go b/proxmox/nodes/client.go index 58c88231..6b8d6529 100644 --- a/proxmox/nodes/client.go +++ b/proxmox/nodes/client.go @@ -12,6 +12,7 @@ import ( "github.com/bpg/terraform-provider-proxmox/proxmox/api" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers" + "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/tasks" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" ) @@ -41,3 +42,10 @@ func (c *Client) VM(vmID int) *vms.Client { VMID: vmID, } } + +// Tasks returns a client for managing VM tasks. +func (c *Client) Tasks() *tasks.Client { + return &tasks.Client{ + Client: c, + } +} diff --git a/proxmox/nodes/containers/container_types.go b/proxmox/nodes/containers/container_types.go index c95eba45..ce6bde41 100644 --- a/proxmox/nodes/containers/container_types.go +++ b/proxmox/nodes/containers/container_types.go @@ -13,26 +13,27 @@ import ( "strconv" "strings" + types2 "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/types" ) // CloneRequestBody contains the data for an container clone request. type CloneRequestBody struct { - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - Description *string `json:"description,omitempty" url:"description,omitempty"` - FullCopy *types.CustomBool `json:"full,omitempty" url:"full,omitempty,int"` - Hostname *string `json:"hostname,omitempty" url:"hostname,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - SnapshotName *string `json:"snapname,omitempty" url:"snapname,omitempty"` - TargetNodeName *string `json:"target,omitempty" url:"target,omitempty"` - TargetStorage *string `json:"storage,omitempty" url:"storage,omitempty"` - VMIDNew int `json:"newid" url:"newid"` + BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` + Description *string `json:"description,omitempty" url:"description,omitempty"` + FullCopy *types2.CustomBool `json:"full,omitempty" url:"full,omitempty,int"` + Hostname *string `json:"hostname,omitempty" url:"hostname,omitempty"` + PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` + SnapshotName *string `json:"snapname,omitempty" url:"snapname,omitempty"` + TargetNodeName *string `json:"target,omitempty" url:"target,omitempty"` + TargetStorage *string `json:"storage,omitempty" url:"storage,omitempty"` + VMIDNew int `json:"newid" url:"newid"` } // CreateRequestBody contains the data for a user create request. type CreateRequestBody struct { BandwidthLimit *float64 `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - ConsoleEnabled *types.CustomBool `json:"console,omitempty" url:"console,omitempty,int"` + ConsoleEnabled *types2.CustomBool `json:"console,omitempty" url:"console,omitempty,int"` ConsoleMode *string `json:"cmode,omitempty" url:"cmode,omitempty"` CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` @@ -45,10 +46,10 @@ type CreateRequestBody struct { DNSDomain *string `json:"searchdomain,omitempty" url:"searchdomain,omitempty"` DNSServer *string `json:"nameserver,omitempty" url:"nameserver,omitempty"` Features *CustomFeatures `json:"features,omitempty" url:"features,omitempty"` - Force *types.CustomBool `json:"force,omitempty" url:"force,omitempty,int"` + Force *types2.CustomBool `json:"force,omitempty" url:"force,omitempty,int"` HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"` Hostname *string `json:"hostname,omitempty" url:"hostname,omitempty"` - IgnoreUnpackErrors *types.CustomBool `json:"ignore-unpack-errors,omitempty" url:"force,omitempty,int"` + IgnoreUnpackErrors *types2.CustomBool `json:"ignore-unpack-errors,omitempty" url:"force,omitempty,int"` Lock *string `json:"lock,omitempty" url:"lock,omitempty,int"` MountPoints CustomMountPointArray `json:"mp,omitempty" url:"mp,omitempty,numbered"` NetworkInterfaces CustomNetworkInterfaceArray `json:"net,omitempty" url:"net,omitempty,numbered"` @@ -56,43 +57,43 @@ type CreateRequestBody struct { OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"` Password *string `json:"password,omitempty" url:"password,omitempty"` PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - Protection *types.CustomBool `json:"protection,omitempty" url:"protection,omitempty,int"` - Restore *types.CustomBool `json:"restore,omitempty" url:"restore,omitempty,int"` + Protection *types2.CustomBool `json:"protection,omitempty" url:"protection,omitempty,int"` + Restore *types2.CustomBool `json:"restore,omitempty" url:"restore,omitempty,int"` RootFS *CustomRootFS `json:"rootfs,omitempty" url:"rootfs,omitempty"` SSHKeys *CustomSSHKeys `json:"ssh-public-keys,omitempty" url:"ssh-public-keys,omitempty"` - Start *types.CustomBool `json:"start,omitempty" url:"start,omitempty,int"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"` + Start *types2.CustomBool `json:"start,omitempty" url:"start,omitempty,int"` + StartOnBoot *types2.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"` StartupBehavior *CustomStartupBehavior `json:"startup,omitempty" url:"startup,omitempty"` Swap *int `json:"swap,omitempty" url:"swap,omitempty"` Tags *string `json:"tags,omitempty" url:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty" url:"template,omitempty,int"` + Template *types2.CustomBool `json:"template,omitempty" url:"template,omitempty,int"` TTY *int `json:"tty,omitempty" url:"tty,omitempty"` - Unique *types.CustomBool `json:"unique,omitempty" url:"unique,omitempty,int"` - Unprivileged *types.CustomBool `json:"unprivileged,omitempty" url:"unprivileged,omitempty,int"` + Unique *types2.CustomBool `json:"unique,omitempty" url:"unique,omitempty,int"` + Unprivileged *types2.CustomBool `json:"unprivileged,omitempty" url:"unprivileged,omitempty,int"` VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"` } // CustomFeatures contains the values for the "features" property. type CustomFeatures struct { - FUSE *types.CustomBool `json:"fuse,omitempty" url:"fuse,omitempty,int"` - KeyControl *types.CustomBool `json:"keyctl,omitempty" url:"keyctl,omitempty,int"` - MountTypes *[]string `json:"mount,omitempty" url:"mount,omitempty"` - Nesting *types.CustomBool `json:"nesting,omitempty" url:"nesting,omitempty,int"` + FUSE *types2.CustomBool `json:"fuse,omitempty" url:"fuse,omitempty,int"` + KeyControl *types2.CustomBool `json:"keyctl,omitempty" url:"keyctl,omitempty,int"` + MountTypes *[]string `json:"mount,omitempty" url:"mount,omitempty"` + Nesting *types2.CustomBool `json:"nesting,omitempty" url:"nesting,omitempty,int"` } // CustomMountPoint contains the values for the "mp[n]" properties. type CustomMountPoint struct { - ACL *types.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"` - Backup *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` - DiskSize *string `json:"size,omitempty" url:"size,omitempty"` - Enabled bool `json:"-" url:"-"` - MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"` - MountPoint string `json:"mp" url:"mp"` - Quota *types.CustomBool `json:"quota,omitempty" url:"quota,omitempty,int"` - ReadOnly *types.CustomBool `json:"ro,omitempty" url:"ro,omitempty,int"` - Replicate *types.CustomBool `json:"replicate,omitempty" url:"replicate,omitempty,int"` - Shared *types.CustomBool `json:"shared,omitempty" url:"shared,omitempty,int"` - Volume string `json:"volume" url:"volume"` + ACL *types2.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"` + Backup *types2.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` + DiskSize *string `json:"size,omitempty" url:"size,omitempty"` + Enabled bool `json:"-" url:"-"` + MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"` + MountPoint string `json:"mp" url:"mp"` + Quota *types2.CustomBool `json:"quota,omitempty" url:"quota,omitempty,int"` + ReadOnly *types2.CustomBool `json:"ro,omitempty" url:"ro,omitempty,int"` + Replicate *types2.CustomBool `json:"replicate,omitempty" url:"replicate,omitempty,int"` + Shared *types2.CustomBool `json:"shared,omitempty" url:"shared,omitempty,int"` + Volume string `json:"volume" url:"volume"` } // CustomMountPointArray is an array of CustomMountPoint. @@ -100,20 +101,20 @@ type CustomMountPointArray []CustomMountPoint // CustomNetworkInterface contains the values for the "net[n]" properties. type CustomNetworkInterface struct { - Bridge *string `json:"bridge,omitempty" url:"bridge,omitempty"` - Enabled bool `json:"-" url:"-"` - Firewall *types.CustomBool `json:"firewall,omitempty" url:"firewall,omitempty,int"` - IPv4Address *string `json:"ip,omitempty" url:"ip,omitempty"` - IPv4Gateway *string `json:"gw,omitempty" url:"gw,omitempty"` - IPv6Address *string `json:"ip6,omitempty" url:"ip6,omitempty"` - IPv6Gateway *string `json:"gw6,omitempty" url:"gw6,omitempty"` - MACAddress *string `json:"hwaddr,omitempty" url:"hwaddr,omitempty"` - MTU *int `json:"mtu,omitempty" url:"mtu,omitempty"` - Name string `json:"name" url:"name"` - RateLimit *float64 `json:"rate,omitempty" url:"rate,omitempty"` - Tag *int `json:"tag,omitempty" url:"tag,omitempty"` - Trunks *[]int `json:"trunks,omitempty" url:"trunks,omitempty"` - Type *string `json:"type,omitempty" url:"type,omitempty"` + Bridge *string `json:"bridge,omitempty" url:"bridge,omitempty"` + Enabled bool `json:"-" url:"-"` + Firewall *types2.CustomBool `json:"firewall,omitempty" url:"firewall,omitempty,int"` + IPv4Address *string `json:"ip,omitempty" url:"ip,omitempty"` + IPv4Gateway *string `json:"gw,omitempty" url:"gw,omitempty"` + IPv6Address *string `json:"ip6,omitempty" url:"ip6,omitempty"` + IPv6Gateway *string `json:"gw6,omitempty" url:"gw6,omitempty"` + MACAddress *string `json:"hwaddr,omitempty" url:"hwaddr,omitempty"` + MTU *int `json:"mtu,omitempty" url:"mtu,omitempty"` + Name string `json:"name" url:"name"` + RateLimit *float64 `json:"rate,omitempty" url:"rate,omitempty"` + Tag *int `json:"tag,omitempty" url:"tag,omitempty"` + Trunks *[]int `json:"trunks,omitempty" url:"trunks,omitempty"` + Type *string `json:"type,omitempty" url:"type,omitempty"` } // CustomNetworkInterfaceArray is an array of CustomNetworkInterface. @@ -121,14 +122,14 @@ type CustomNetworkInterfaceArray []CustomNetworkInterface // CustomRootFS contains the values for the "rootfs" property. type CustomRootFS struct { - ACL *types.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"` - Size *types.DiskSize `json:"size,omitempty" url:"size,omitempty"` - MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"` - Quota *types.CustomBool `json:"quota,omitempty" url:"quota,omitempty,int"` - ReadOnly *types.CustomBool `json:"ro,omitempty" url:"ro,omitempty,int"` - Replicate *types.CustomBool `json:"replicate,omitempty" url:"replicate,omitempty,int"` - Shared *types.CustomBool `json:"shared,omitempty" url:"shared,omitempty,int"` - Volume string `json:"volume" url:"volume"` + ACL *types2.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"` + Size *types.DiskSize `json:"size,omitempty" url:"size,omitempty"` + MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"` + Quota *types2.CustomBool `json:"quota,omitempty" url:"quota,omitempty,int"` + ReadOnly *types2.CustomBool `json:"ro,omitempty" url:"ro,omitempty,int"` + Replicate *types2.CustomBool `json:"replicate,omitempty" url:"replicate,omitempty,int"` + Shared *types2.CustomBool `json:"shared,omitempty" url:"shared,omitempty,int"` + Volume string `json:"volume" url:"volume"` } // CustomSSHKeys contains the values for the "ssh-public-keys" property. @@ -148,7 +149,7 @@ type GetResponseBody struct { // GetResponseData contains the data from a user get response. type GetResponseData struct { - ConsoleEnabled *types.CustomBool `json:"console,omitempty"` + ConsoleEnabled *types2.CustomBool `json:"console,omitempty"` ConsoleMode *string `json:"cmode,omitempty"` CPUArchitecture *string `json:"arch,omitempty"` CPUCores *int `json:"cores,omitempty"` @@ -162,7 +163,7 @@ type GetResponseData struct { Features *CustomFeatures `json:"features,omitempty"` HookScript *string `json:"hookscript,omitempty"` Hostname *string `json:"hostname,omitempty"` - Lock *types.CustomBool `json:"lock,omitempty"` + Lock *types2.CustomBool `json:"lock,omitempty"` LXCConfiguration *[][2]string `json:"lxc,omitempty"` MountPoint0 CustomMountPoint `json:"mp0,omitempty"` MountPoint1 CustomMountPoint `json:"mp1,omitempty"` @@ -177,15 +178,15 @@ type GetResponseData struct { NetworkInterface6 *CustomNetworkInterface `json:"net6,omitempty"` NetworkInterface7 *CustomNetworkInterface `json:"net7,omitempty"` OSType *string `json:"ostype,omitempty"` - Protection *types.CustomBool `json:"protection,omitempty"` + Protection *types2.CustomBool `json:"protection,omitempty"` RootFS *CustomRootFS `json:"rootfs,omitempty"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty"` + StartOnBoot *types2.CustomBool `json:"onboot,omitempty"` StartupBehavior *CustomStartupBehavior `json:"startup,omitempty"` Swap *int `json:"swap,omitempty"` Tags *string `json:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty"` + Template *types2.CustomBool `json:"template,omitempty"` TTY *int `json:"tty,omitempty"` - Unprivileged *types.CustomBool `json:"unprivileged,omitempty"` + Unprivileged *types2.CustomBool `json:"unprivileged,omitempty"` } // GetStatusResponseBody contains the body from a container get status response. @@ -214,8 +215,8 @@ type RebootRequestBody struct { // ShutdownRequestBody contains the body for a container shutdown request. type ShutdownRequestBody struct { - ForceStop *types.CustomBool `json:"forceStop,omitempty" url:"forceStop,omitempty,int"` - Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` + ForceStop *types2.CustomBool `json:"forceStop,omitempty" url:"forceStop,omitempty,int"` + Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` } // UpdateRequestBody contains the data for an user update request. @@ -550,10 +551,10 @@ func (r *CustomFeatures) UnmarshalJSON(b []byte) error { if len(v) == 2 { switch v[0] { case "fuse": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.FUSE = &bv case "keyctl": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.KeyControl = &bv case "mount": if v[1] != "" { @@ -564,7 +565,7 @@ func (r *CustomFeatures) UnmarshalJSON(b []byte) error { r.MountTypes = &a } case "nesting": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Nesting = &bv } } @@ -592,10 +593,10 @@ func (r *CustomMountPoint) UnmarshalJSON(b []byte) error { } else if len(v) == 2 { switch v[0] { case "acl": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.ACL = &bv case "backup": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Backup = &bv case "mountoptions": if v[1] != "" { @@ -608,16 +609,16 @@ func (r *CustomMountPoint) UnmarshalJSON(b []byte) error { case "mp": r.MountPoint = v[1] case "quota": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Quota = &bv case "ro": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.ReadOnly = &bv case "replicate": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Replicate = &bv case "shared": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Shared = &bv case "size": r.DiskSize = &v[1] @@ -650,7 +651,7 @@ func (r *CustomNetworkInterface) UnmarshalJSON(b []byte) error { case "bridge": r.Bridge = &v[1] case "firewall": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Firewall = &bv case "gw": r.IPv4Gateway = &v[1] @@ -731,7 +732,7 @@ func (r *CustomRootFS) UnmarshalJSON(b []byte) error { } else if len(v) == 2 { switch v[0] { case "acl": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.ACL = &bv case "mountoptions": if v[1] != "" { @@ -742,16 +743,16 @@ func (r *CustomRootFS) UnmarshalJSON(b []byte) error { r.MountOptions = &a } case "quota": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Quota = &bv case "ro": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.ReadOnly = &bv case "replicate": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Replicate = &bv case "shared": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Shared = &bv case "size": r.Size = new(types.DiskSize) diff --git a/proxmox/nodes/network.go b/proxmox/nodes/network.go new file mode 100644 index 00000000..b055e17b --- /dev/null +++ b/proxmox/nodes/network.go @@ -0,0 +1,125 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package nodes + +import ( + "context" + "fmt" + "net/http" + "net/url" + "sort" + + "github.com/bpg/terraform-provider-proxmox/proxmox/api" +) + +const ( + networkReloadTimeoutSec = 5 +) + +// ListNetworkInterfaces retrieves a list of network interfaces for a specific nodes. +func (c *Client) ListNetworkInterfaces(ctx context.Context) ([]*NetworkInterfaceListResponseData, error) { + resBody := &NetworkInterfaceListResponseBody{} + + err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("network"), nil, resBody) + if err != nil { + return nil, fmt.Errorf("failed to get network interfaces for node \"%s\": %w", c.NodeName, err) + } + + if resBody.Data == nil { + return nil, api.ErrNoDataObjectInResponse + } + + sort.Slice(resBody.Data, func(i, j int) bool { + return resBody.Data[i].Priority < resBody.Data[j].Priority + }) + + return resBody.Data, nil +} + +// CreateNetworkInterface creates a network interface for a specific node. +func (c *Client) CreateNetworkInterface(ctx context.Context, d *NetworkInterfaceCreateUpdateRequestBody) error { + err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("network"), d, nil) + if err != nil { + return fmt.Errorf( + "failed to create network interface \"%s\" for node \"%s\": %w", + d.Iface, c.NodeName, err, + ) + } + + return nil +} + +// ReloadNetworkConfiguration reloads the network configuration for a specific node. +func (c *Client) ReloadNetworkConfiguration(ctx context.Context) error { + resBody := &ReloadNetworkResponseBody{} + + err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("network"), nil, resBody) + if err != nil { + return fmt.Errorf("failed to reload network configuration for node \"%s\": %w", c.NodeName, err) + } + + if resBody.Data == nil { + return api.ErrNoDataObjectInResponse + } + + err = c.Tasks().WaitForTask(ctx, *resBody.Data, networkReloadTimeoutSec, 1) + if err == nil { + return nil + } + + return nil +} + +// RevertNetworkConfiguration reverts the network configuration changes for a specific node. +func (c *Client) RevertNetworkConfiguration(ctx context.Context) error { + err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath("network"), nil, nil) + if err != nil { + return fmt.Errorf("failed to revert network configuration for node \"%s\": %w", c.NodeName, err) + } + + return nil +} + +// UpdateNetworkInterface updates a network interface for a specific node. +func (c *Client) UpdateNetworkInterface( + ctx context.Context, + iface string, + d *NetworkInterfaceCreateUpdateRequestBody, +) error { + err := c.DoRequest( + ctx, + http.MethodPut, + c.ExpandPath(fmt.Sprintf("network/%s", url.PathEscape(iface))), + d, + nil, + ) + if err != nil { + return fmt.Errorf("failed to update network interface \"%s\" for node \"%s\": %w", + d.Iface, c.NodeName, err, + ) + } + + return nil +} + +// DeleteNetworkInterface deletes a network interface configuration for a specific node. +func (c *Client) DeleteNetworkInterface(ctx context.Context, iface string) error { + err := c.DoRequest( + ctx, + http.MethodDelete, + c.ExpandPath(fmt.Sprintf("network/%s", url.PathEscape(iface))), + nil, + nil, + ) + if err != nil { + return fmt.Errorf("failed to delete network interface \"%s\" for node \"%s\": %w", + iface, c.NodeName, err, + ) + } + + return nil +} diff --git a/proxmox/nodes/network_types.go b/proxmox/nodes/network_types.go new file mode 100644 index 00000000..f0a9ff71 --- /dev/null +++ b/proxmox/nodes/network_types.go @@ -0,0 +1,83 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package nodes + +import ( + "github.com/bpg/terraform-provider-proxmox/internal/types" +) + +// NetworkInterfaceListResponseBody contains the body from a node network interface list response. +type NetworkInterfaceListResponseBody struct { + Data []*NetworkInterfaceListResponseData `json:"data,omitempty"` +} + +// NetworkInterfaceListResponseData contains the data from a node network interface list response. +type NetworkInterfaceListResponseData struct { + Active *types.CustomBool `json:"active,omitempty"` + Address *string `json:"address,omitempty"` + Address6 *string `json:"address6,omitempty"` + Autostart *types.CustomBool `json:"autostart,omitempty"` + BridgeFD *string `json:"bridge_fd,omitempty"` + BridgePorts *string `json:"bridge_ports,omitempty"` + BridgeSTP *string `json:"bridge_stp,omitempty"` + BridgeVIDs *string `json:"bridge_vids,omitempty"` + BridgeVLANAware *types.CustomBool `json:"bridge_vlan_aware,omitempty"` + CIDR *string `json:"cidr,omitempty"` + CIDR6 *string `json:"cidr6,omitempty"` + Comments *string `json:"comments,omitempty"` + Exists *types.CustomBool `json:"exists,omitempty"` + Families *[]string `json:"families,omitempty"` + Gateway *string `json:"gateway,omitempty"` + Gateway6 *string `json:"gateway6,omitempty"` + Iface string `json:"iface"` + MethodIPv4 *string `json:"method,omitempty"` + MethodIPv6 *string `json:"method6,omitempty"` + MTU *string `json:"mtu,omitempty"` + Netmask *string `json:"netmask,omitempty"` + VLANID *string `json:"vlan-id,omitempty"` + VLANRawDevice *string `json:"vlan-raw-device,omitempty"` + Priority int `json:"priority"` + Type string `json:"type"` +} + +// NetworkInterfaceCreateUpdateRequestBody contains the body for a node network interface create / update request. +type NetworkInterfaceCreateUpdateRequestBody struct { + Iface string `json:"iface" url:"iface"` + Type string `json:"type" url:"type"` + + Address *string `json:"address,omitempty" url:"address,omitempty"` + Address6 *string `json:"address6,omitempty" url:"address6,omitempty"` + Autostart *types.CustomBool `json:"autostart,omitempty" url:"autostart,omitempty,int"` + BondPrimary *string `json:"bond-primary,omitempty" url:"bond-primary,omitempty"` + BondMode *string `json:"bond_mode,omitempty" url:"bond_mode,omitempty"` + BondXmitHashPolicy *string `json:"bond_xmit_hash_policy,omitempty" url:"bond_xmit_hash_policy,omitempty"` + BridgePorts *string `json:"bridge_ports,omitempty" url:"bridge_ports,omitempty"` + BridgeVLANAware *types.CustomBool `json:"bridge_vlan_aware,omitempty" url:"bridge_vlan_aware,omitempty,int"` + CIDR *string `json:"cidr,omitempty" url:"cidr,omitempty"` + CIDR6 *string `json:"cidr6,omitempty" url:"cidr6,omitempty"` + Comments *string `json:"comments,omitempty" url:"comments,omitempty"` + Comments6 *string `json:"comments6,omitempty" url:"comments6,omitempty"` + Gateway *string `json:"gateway,omitempty" url:"gateway,omitempty"` + Gateway6 *string `json:"gateway6,omitempty" url:"gateway6,omitempty"` + Delete *[]string `json:"delete,omitempty" url:"delete,omitempty"` + MTU *int64 `json:"mtu,omitempty" url:"mtu,omitempty"` + Netmask *string `json:"netmask,omitempty" url:"netmask,omitempty"` + Netmask6 *string `json:"netmask6,omitempty" url:"netmask6,omitempty"` + OVSBonds *string `json:"ovs_bonds,omitempty" url:"ovs_bonds,omitempty"` + OVSBridge *string `json:"ovs_bridge,omitempty" url:"ovs_bridge,omitempty"` + OVSOptions *string `json:"ovs_options,omitempty" url:"ovs_options,omitempty"` + OVSPorts *string `json:"ovs_ports,omitempty" url:"ovs_ports,omitempty"` + OVSTag *string `json:"ovs_tag,omitempty" url:"ovs_tag,omitempty"` + Slaves *string `json:"slaves,omitempty" url:"slaves,omitempty"` + VLANID *int64 `json:"vlan_id,omitempty" url:"vlan_id,omitempty"` + VLANRawDevice *string `json:"vlan-raw-device,omitempty" url:"vlan-raw-device,omitempty"` +} + +// ReloadNetworkResponseBody contains the body from a node network reload response. +type ReloadNetworkResponseBody struct { + Data *string `json:"data,omitempty"` +} diff --git a/proxmox/nodes/nodes.go b/proxmox/nodes/nodes.go index ce211bad..d11519ee 100644 --- a/proxmox/nodes/nodes.go +++ b/proxmox/nodes/nodes.go @@ -11,72 +11,10 @@ import ( "fmt" "net/http" "sort" - "strings" "github.com/bpg/terraform-provider-proxmox/proxmox/api" ) -// GetIP retrieves the IP address of a node. -func (c *Client) GetIP(ctx context.Context) (string, error) { - networkDevices, err := c.ListNetworkDevices(ctx) - if err != nil { - return "", err - } - - nodeAddress := "" - - for _, d := range networkDevices { - if d.Address != nil { - nodeAddress = *d.Address - break - } - } - - if nodeAddress == "" { - return "", fmt.Errorf("failed to determine the IP address of node \"%s\"", c.NodeName) - } - - nodeAddressParts := strings.Split(nodeAddress, "/") - - return nodeAddressParts[0], nil -} - -// GetTime retrieves the time information for a node. -func (c *Client) GetTime(ctx context.Context) (*GetTimeResponseData, error) { - resBody := &GetTimeResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("time"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get time information for node \"%s\": %w", c.NodeName, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// ListNetworkDevices retrieves a list of network devices for a specific nodes. -func (c *Client) ListNetworkDevices(ctx context.Context) ([]*NetworkDeviceListResponseData, error) { - resBody := &NetworkDeviceListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("network"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get network devices for node \"%s\": %w", c.NodeName, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].Priority < resBody.Data[j].Priority - }) - - return resBody.Data, nil -} - // ListNodes retrieves a list of nodes. func (c *Client) ListNodes(ctx context.Context) ([]*ListResponseData, error) { resBody := &ListResponseBody{} @@ -97,6 +35,22 @@ func (c *Client) ListNodes(ctx context.Context) ([]*ListResponseData, error) { return resBody.Data, nil } +// GetTime retrieves the time information for a node. +func (c *Client) GetTime(ctx context.Context) (*GetTimeResponseData, error) { + resBody := &GetTimeResponseBody{} + + err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("time"), nil, resBody) + if err != nil { + return nil, fmt.Errorf("failed to get time information for node \"%s\": %w", c.NodeName, err) + } + + if resBody.Data == nil { + return nil, api.ErrNoDataObjectInResponse + } + + return resBody.Data, nil +} + // UpdateTime updates the time on a node. func (c *Client) UpdateTime(ctx context.Context, d *UpdateTimeRequestBody) error { err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("time"), d, nil) diff --git a/proxmox/nodes/nodes_types.go b/proxmox/nodes/nodes_types.go index 2d31bbef..c86da585 100644 --- a/proxmox/nodes/nodes_types.go +++ b/proxmox/nodes/nodes_types.go @@ -11,7 +11,7 @@ import ( "fmt" "net/url" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" + "github.com/bpg/terraform-provider-proxmox/internal/types" ) // CustomCommands contains an array of commands to execute. @@ -52,31 +52,6 @@ type ListResponseData struct { Uptime *int `json:"uptime"` } -// NetworkDeviceListResponseBody contains the body from a node network device list response. -type NetworkDeviceListResponseBody struct { - Data []*NetworkDeviceListResponseData `json:"data,omitempty"` -} - -// NetworkDeviceListResponseData contains the data from a node network device list response. -type NetworkDeviceListResponseData struct { - Active *types.CustomBool `json:"active,omitempty"` - Address *string `json:"address,omitempty"` - Autostart *types.CustomBool `json:"autostart,omitempty"` - BridgeFD *string `json:"bridge_fd,omitempty"` - BridgePorts *string `json:"bridge_ports,omitempty"` - BridgeSTP *string `json:"bridge_stp,omitempty"` - CIDR *string `json:"cidr,omitempty"` - Exists *types.CustomBool `json:"exists,omitempty"` - Families *[]string `json:"families,omitempty"` - Gateway *string `json:"gateway,omitempty"` - Iface string `json:"iface"` - MethodIPv4 *string `json:"method,omitempty"` - MethodIPv6 *string `json:"method6,omitempty"` - Netmask *string `json:"netmask,omitempty"` - Priority int `json:"priority"` - Type string `json:"type"` -} - // UpdateTimeRequestBody contains the body for a node time update request. type UpdateTimeRequestBody struct { TimeZone string `json:"timezone" url:"timezone"` diff --git a/proxmox/nodes/storage_types.go b/proxmox/nodes/storage_types.go index c381bf50..ac54232b 100644 --- a/proxmox/nodes/storage_types.go +++ b/proxmox/nodes/storage_types.go @@ -7,7 +7,7 @@ package nodes import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/types" + "github.com/bpg/terraform-provider-proxmox/internal/types" ) // DatastoreFileListResponseBody contains the body from a datastore content list response. diff --git a/proxmox/nodes/tasks/tasks.go b/proxmox/nodes/tasks/tasks.go index ce25ca5f..a1499da0 100644 --- a/proxmox/nodes/tasks/tasks.go +++ b/proxmox/nodes/tasks/tasks.go @@ -39,9 +39,9 @@ func (c *Client) GetTaskStatus(ctx context.Context, upid string) (*GetTaskStatus } // WaitForTask waits for a specific task to complete. -func (c *Client) WaitForTask(ctx context.Context, upid string, timeout, delay int) error { - timeDelay := int64(delay) - timeMax := float64(timeout) +func (c *Client) WaitForTask(ctx context.Context, upid string, timeoutSec, delaySec int) error { + timeDelay := int64(delaySec) + timeMax := float64(timeoutSec) timeStart := time.Now() timeElapsed := timeStart.Sub(timeStart) @@ -80,7 +80,7 @@ func (c *Client) WaitForTask(ctx context.Context, upid string, timeout, delay in } return fmt.Errorf( - "timeout while waiting for task \"%s\" to complete", + "timeoutSec while waiting for task \"%s\" to complete", upid, ) } diff --git a/proxmox/nodes/vms/vms_types.go b/proxmox/nodes/vms/vms_types.go index 6186b26c..65cb509c 100644 --- a/proxmox/nodes/vms/vms_types.go +++ b/proxmox/nodes/vms/vms_types.go @@ -15,14 +15,15 @@ import ( "strconv" "strings" + types2 "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/types" ) // CustomAgent handles QEMU agent parameters. type CustomAgent struct { - Enabled *types.CustomBool `json:"enabled,omitempty" url:"enabled,int"` - TrimClonedDisks *types.CustomBool `json:"fstrim_cloned_disks" url:"fstrim_cloned_disks,int"` - Type *string `json:"type" url:"type"` + Enabled *types2.CustomBool `json:"enabled,omitempty" url:"enabled,int"` + TrimClonedDisks *types2.CustomBool `json:"fstrim_cloned_disks" url:"fstrim_cloned_disks,int"` + Type *string `json:"type" url:"type"` } // CustomAudioDevice handles QEMU audio parameters. @@ -73,10 +74,10 @@ type CustomCloudInitSSHKeys []string // CustomCPUEmulation handles QEMU CPU emulation parameters. type CustomCPUEmulation struct { - Flags *[]string `json:"flags,omitempty" url:"flags,omitempty,semicolon"` - Hidden *types.CustomBool `json:"hidden,omitempty" url:"hidden,omitempty,int"` - HVVendorID *string `json:"hv-vendor-id,omitempty" url:"hv-vendor-id,omitempty"` - Type string `json:"cputype,omitempty" url:"cputype,omitempty"` + Flags *[]string `json:"flags,omitempty" url:"flags,omitempty,semicolon"` + Hidden *types2.CustomBool `json:"hidden,omitempty" url:"hidden,omitempty,int"` + HVVendorID *string `json:"hv-vendor-id,omitempty" url:"hv-vendor-id,omitempty"` + Type string `json:"cputype,omitempty" url:"cputype,omitempty"` } // CustomEFIDisk handles QEMU EFI disk parameters. @@ -88,17 +89,17 @@ type CustomEFIDisk struct { // CustomNetworkDevice handles QEMU network device parameters. type CustomNetworkDevice struct { - Model string `json:"model" url:"model"` - Bridge *string `json:"bridge,omitempty" url:"bridge,omitempty"` - Enabled bool `json:"-" url:"-"` - Firewall *types.CustomBool `json:"firewall,omitempty" url:"firewall,omitempty,int"` - LinkDown *types.CustomBool `json:"link_down,omitempty" url:"link_down,omitempty,int"` - MACAddress *string `json:"macaddr,omitempty" url:"macaddr,omitempty"` - Queues *int `json:"queues,omitempty" url:"queues,omitempty"` - RateLimit *float64 `json:"rate,omitempty" url:"rate,omitempty"` - Tag *int `json:"tag,omitempty" url:"tag,omitempty"` - MTU *int `json:"mtu,omitempty" url:"mtu,omitempty"` - Trunks []int `json:"trunks,omitempty" url:"trunks,omitempty"` + Model string `json:"model" url:"model"` + Bridge *string `json:"bridge,omitempty" url:"bridge,omitempty"` + Enabled bool `json:"-" url:"-"` + Firewall *types2.CustomBool `json:"firewall,omitempty" url:"firewall,omitempty,int"` + LinkDown *types2.CustomBool `json:"link_down,omitempty" url:"link_down,omitempty,int"` + MACAddress *string `json:"macaddr,omitempty" url:"macaddr,omitempty"` + Queues *int `json:"queues,omitempty" url:"queues,omitempty"` + RateLimit *float64 `json:"rate,omitempty" url:"rate,omitempty"` + Tag *int `json:"tag,omitempty" url:"tag,omitempty"` + MTU *int `json:"mtu,omitempty" url:"mtu,omitempty"` + Trunks []int `json:"trunks,omitempty" url:"trunks,omitempty"` } // CustomNetworkDevices handles QEMU network device parameters. @@ -117,12 +118,12 @@ type CustomNUMADevices []CustomNUMADevice // CustomPCIDevice handles QEMU host PCI device mapping parameters. type CustomPCIDevice struct { - DeviceIDs []string `json:"host" url:"host,semicolon"` - MDev *string `json:"mdev,omitempty" url:"mdev,omitempty"` - PCIExpress *types.CustomBool `json:"pcie,omitempty" url:"pcie,omitempty,int"` - ROMBAR *types.CustomBool `json:"rombar,omitempty" url:"rombar,omitempty,int"` - ROMFile *string `json:"romfile,omitempty" url:"romfile,omitempty"` - XVGA *types.CustomBool `json:"x-vga,omitempty" url:"x-vga,omitempty,int"` + DeviceIDs []string `json:"host" url:"host,semicolon"` + MDev *string `json:"mdev,omitempty" url:"mdev,omitempty"` + PCIExpress *types2.CustomBool `json:"pcie,omitempty" url:"pcie,omitempty,int"` + ROMBAR *types2.CustomBool `json:"rombar,omitempty" url:"rombar,omitempty,int"` + ROMFile *string `json:"romfile,omitempty" url:"romfile,omitempty"` + XVGA *types2.CustomBool `json:"x-vga,omitempty" url:"x-vga,omitempty,int"` } // CustomPCIDevices handles QEMU host PCI device mapping parameters. @@ -139,20 +140,20 @@ type CustomSharedMemory struct { // CustomSMBIOS handles QEMU SMBIOS parameters. type CustomSMBIOS struct { - Base64 *types.CustomBool `json:"base64,omitempty" url:"base64,omitempty"` - Family *string `json:"family,omitempty" url:"family,omitempty"` - Manufacturer *string `json:"manufacturer,omitempty" url:"manufacturer,omitempty"` - Product *string `json:"product,omitempty" url:"product,omitempty"` - Serial *string `json:"serial,omitempty" url:"serial,omitempty"` - SKU *string `json:"sku,omitempty" url:"sku,omitempty"` - UUID *string `json:"uuid,omitempty" url:"uuid,omitempty"` - Version *string `json:"version,omitempty" url:"version,omitempty"` + Base64 *types2.CustomBool `json:"base64,omitempty" url:"base64,omitempty"` + Family *string `json:"family,omitempty" url:"family,omitempty"` + Manufacturer *string `json:"manufacturer,omitempty" url:"manufacturer,omitempty"` + Product *string `json:"product,omitempty" url:"product,omitempty"` + Serial *string `json:"serial,omitempty" url:"serial,omitempty"` + SKU *string `json:"sku,omitempty" url:"sku,omitempty"` + UUID *string `json:"uuid,omitempty" url:"uuid,omitempty"` + Version *string `json:"version,omitempty" url:"version,omitempty"` } // CustomSpiceEnhancements handles QEMU spice enhancement parameters. type CustomSpiceEnhancements struct { - FolderSharing *types.CustomBool `json:"foldersharing,omitempty" url:"foldersharing,omitempty"` - VideoStreaming *string `json:"videostreaming,omitempty" url:"videostreaming,omitempty"` + FolderSharing *types2.CustomBool `json:"foldersharing,omitempty" url:"foldersharing,omitempty"` + VideoStreaming *string `json:"videostreaming,omitempty" url:"videostreaming,omitempty"` } // CustomStartupOrder handles QEMU startup order parameters. @@ -164,20 +165,20 @@ type CustomStartupOrder struct { // CustomStorageDevice handles QEMU SATA device parameters. type CustomStorageDevice struct { - AIO *string `json:"aio,omitempty" url:"aio,omitempty"` - BackupEnabled *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` - BurstableReadSpeedMbps *int `json:"mbps_rd_max,omitempty" url:"mbps_rd_max,omitempty"` - BurstableWriteSpeedMbps *int `json:"mbps_wr_max,omitempty" url:"mbps_wr_max,omitempty"` - Discard *string `json:"discard,omitempty" url:"discard,omitempty"` - Enabled bool `json:"-" url:"-"` - FileVolume string `json:"file" url:"file"` - Format *string `json:"format,omitempty" url:"format,omitempty"` - IOThread *types.CustomBool `json:"iothread,omitempty" url:"iothread,omitempty,int"` - SSD *types.CustomBool `json:"ssd,omitempty" url:"ssd,omitempty,int"` - MaxReadSpeedMbps *int `json:"mbps_rd,omitempty" url:"mbps_rd,omitempty"` - MaxWriteSpeedMbps *int `json:"mbps_wr,omitempty" url:"mbps_wr,omitempty"` - Media *string `json:"media,omitempty" url:"media,omitempty"` - Size *types.DiskSize `json:"size,omitempty" url:"size,omitempty"` + AIO *string `json:"aio,omitempty" url:"aio,omitempty"` + BackupEnabled *types2.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` + BurstableReadSpeedMbps *int `json:"mbps_rd_max,omitempty" url:"mbps_rd_max,omitempty"` + BurstableWriteSpeedMbps *int `json:"mbps_wr_max,omitempty" url:"mbps_wr_max,omitempty"` + Discard *string `json:"discard,omitempty" url:"discard,omitempty"` + Enabled bool `json:"-" url:"-"` + FileVolume string `json:"file" url:"file"` + Format *string `json:"format,omitempty" url:"format,omitempty"` + IOThread *types2.CustomBool `json:"iothread,omitempty" url:"iothread,omitempty,int"` + SSD *types2.CustomBool `json:"ssd,omitempty" url:"ssd,omitempty,int"` + MaxReadSpeedMbps *int `json:"mbps_rd,omitempty" url:"mbps_rd,omitempty"` + MaxWriteSpeedMbps *int `json:"mbps_wr,omitempty" url:"mbps_wr,omitempty"` + Media *string `json:"media,omitempty" url:"media,omitempty"` + Size *types.DiskSize `json:"size,omitempty" url:"size,omitempty"` Interface *string ID *string FileID *string @@ -189,8 +190,8 @@ type CustomStorageDevices map[string]CustomStorageDevice // CustomUSBDevice handles QEMU USB device parameters. type CustomUSBDevice struct { - HostDevice string `json:"host" url:"host"` - USB3 *types.CustomBool `json:"usb3,omitempty" url:"usb3,omitempty,int"` + HostDevice string `json:"host" url:"host"` + USB3 *types2.CustomBool `json:"usb3,omitempty" url:"usb3,omitempty,int"` } // CustomUSBDevices handles QEMU USB device parameters. @@ -204,10 +205,10 @@ type CustomVGADevice struct { // CustomVirtualIODevice handles QEMU VirtIO device parameters. type CustomVirtualIODevice struct { - AIO *string `json:"aio,omitempty" url:"aio,omitempty"` - BackupEnabled *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` - Enabled bool `json:"-" url:"-"` - FileVolume string `json:"file" url:"file"` + AIO *string `json:"aio,omitempty" url:"aio,omitempty"` + BackupEnabled *types2.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` + Enabled bool `json:"-" url:"-"` + FileVolume string `json:"file" url:"file"` } // CustomVirtualIODevices handles QEMU VirtIO device parameters. @@ -221,89 +222,89 @@ type CustomWatchdogDevice struct { // CloneRequestBody contains the data for an virtual machine clone request. type CloneRequestBody struct { - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - Description *string `json:"description,omitempty" url:"description,omitempty"` - FullCopy *types.CustomBool `json:"full,omitempty" url:"full,omitempty,int"` - Name *string `json:"name,omitempty" url:"name,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - SnapshotName *string `json:"snapname,omitempty" url:"snapname,omitempty"` - TargetNodeName *string `json:"target,omitempty" url:"target,omitempty"` - TargetStorage *string `json:"storage,omitempty" url:"storage,omitempty"` - TargetStorageFormat *string `json:"format,omitempty" url:"format,omitempty"` - VMIDNew int `json:"newid" url:"newid"` + BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` + Description *string `json:"description,omitempty" url:"description,omitempty"` + FullCopy *types2.CustomBool `json:"full,omitempty" url:"full,omitempty,int"` + Name *string `json:"name,omitempty" url:"name,omitempty"` + PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` + SnapshotName *string `json:"snapname,omitempty" url:"snapname,omitempty"` + TargetNodeName *string `json:"target,omitempty" url:"target,omitempty"` + TargetStorage *string `json:"storage,omitempty" url:"storage,omitempty"` + TargetStorageFormat *string `json:"format,omitempty" url:"format,omitempty"` + VMIDNew int `json:"newid" url:"newid"` } // CreateRequestBody contains the data for a virtual machine create request. type CreateRequestBody struct { - ACPI *types.CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"` - Agent *CustomAgent `json:"agent,omitempty" url:"agent,omitempty"` - AllowReboot *types.CustomBool `json:"reboot,omitempty" url:"reboot,omitempty,int"` - AudioDevices CustomAudioDevices `json:"audio,omitempty" url:"audio,omitempty"` - Autostart *types.CustomBool `json:"autostart,omitempty" url:"autostart,omitempty,int"` - BackupFile *string `json:"archive,omitempty" url:"archive,omitempty"` - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - BIOS *string `json:"bios,omitempty" url:"bios,omitempty"` - Boot *CustomBoot `json:"boot,omitempty" url:"boot,omitempty"` - CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"` - CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"` - CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` - CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"` - CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` - CPUSockets *int `json:"sockets,omitempty" url:"sockets,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` - DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` - Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"` - DeletionProtection *types.CustomBool `json:"protection,omitempty" url:"force,omitempty,int"` - Description *string `json:"description,omitempty" url:"description,omitempty"` - EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty" url:"efidisk0,omitempty"` - FloatingMemory *int `json:"balloon,omitempty" url:"balloon,omitempty"` - FloatingMemoryShares *int `json:"shares,omitempty" url:"shares,omitempty"` - Freeze *types.CustomBool `json:"freeze,omitempty" url:"freeze,omitempty,int"` - HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"` - Hotplug types.CustomCommaSeparatedList `json:"hotplug,omitempty" url:"hotplug,omitempty,comma"` - Hugepages *string `json:"hugepages,omitempty" url:"hugepages,omitempty"` - IDEDevices CustomStorageDevices `json:"ide,omitempty" url:",omitempty"` - KeyboardLayout *string `json:"keyboard,omitempty" url:"keyboard,omitempty"` - KVMArguments *string `json:"args,omitempty" url:"args,omitempty,space"` - KVMEnabled *types.CustomBool `json:"kvm,omitempty" url:"kvm,omitempty,int"` - LocalTime *types.CustomBool `json:"localtime,omitempty" url:"localtime,omitempty,int"` - Lock *string `json:"lock,omitempty" url:"lock,omitempty"` - Machine *string `json:"machine,omitempty" url:"machine,omitempty"` - MigrateDowntime *float64 `json:"migrate_downtime,omitempty" url:"migrate_downtime,omitempty"` - MigrateSpeed *int `json:"migrate_speed,omitempty" url:"migrate_speed,omitempty"` - Name *string `json:"name,omitempty" url:"name,omitempty"` - NetworkDevices CustomNetworkDevices `json:"net,omitempty" url:"net,omitempty"` - NUMADevices CustomNUMADevices `json:"numa_devices,omitempty" url:"numa,omitempty"` - NUMAEnabled *types.CustomBool `json:"numa,omitempty" url:"numa,omitempty,int"` - OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"` - Overwrite *types.CustomBool `json:"force,omitempty" url:"force,omitempty,int"` - PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - Revert *string `json:"revert,omitempty" url:"revert,omitempty"` - SATADevices CustomStorageDevices `json:"sata,omitempty" url:"sata,omitempty"` - SCSIDevices CustomStorageDevices `json:"scsi,omitempty" url:"scsi,omitempty"` - SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"` - SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"` - SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"` - SkipLock *types.CustomBool `json:"skiplock,omitempty" url:"skiplock,omitempty,int"` - SMBIOS *CustomSMBIOS `json:"smbios1,omitempty" url:"smbios1,omitempty"` - SpiceEnhancements *CustomSpiceEnhancements `json:"spice_enhancements,omitempty" url:"spice_enhancements,omitempty"` - StartDate *string `json:"startdate,omitempty" url:"startdate,omitempty"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"` - StartupOrder *CustomStartupOrder `json:"startup,omitempty" url:"startup,omitempty"` - TabletDeviceEnabled *types.CustomBool `json:"tablet,omitempty" url:"tablet,omitempty,int"` - Tags *string `json:"tags,omitempty" url:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty" url:"template,omitempty,int"` - TimeDriftFixEnabled *types.CustomBool `json:"tdf,omitempty" url:"tdf,omitempty,int"` - USBDevices CustomUSBDevices `json:"usb,omitempty" url:"usb,omitempty"` - VGADevice *CustomVGADevice `json:"vga,omitempty" url:"vga,omitempty"` - VirtualCPUCount *int `json:"vcpus,omitempty" url:"vcpus,omitempty"` - VirtualIODevices CustomStorageDevices `json:"virtio,omitempty" url:"virtio,omitempty"` - VMGenerationID *string `json:"vmgenid,omitempty" url:"vmgenid,omitempty"` - VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"` - VMStateDatastoreID *string `json:"vmstatestorage,omitempty" url:"vmstatestorage,omitempty"` - WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"` + ACPI *types2.CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"` + Agent *CustomAgent `json:"agent,omitempty" url:"agent,omitempty"` + AllowReboot *types2.CustomBool `json:"reboot,omitempty" url:"reboot,omitempty,int"` + AudioDevices CustomAudioDevices `json:"audio,omitempty" url:"audio,omitempty"` + Autostart *types2.CustomBool `json:"autostart,omitempty" url:"autostart,omitempty,int"` + BackupFile *string `json:"archive,omitempty" url:"archive,omitempty"` + BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` + BIOS *string `json:"bios,omitempty" url:"bios,omitempty"` + Boot *CustomBoot `json:"boot,omitempty" url:"boot,omitempty"` + CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"` + CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"` + CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` + CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` + CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"` + CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` + CPUSockets *int `json:"sockets,omitempty" url:"sockets,omitempty"` + CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` + DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` + Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"` + DeletionProtection *types2.CustomBool `json:"protection,omitempty" url:"force,omitempty,int"` + Description *string `json:"description,omitempty" url:"description,omitempty"` + EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty" url:"efidisk0,omitempty"` + FloatingMemory *int `json:"balloon,omitempty" url:"balloon,omitempty"` + FloatingMemoryShares *int `json:"shares,omitempty" url:"shares,omitempty"` + Freeze *types2.CustomBool `json:"freeze,omitempty" url:"freeze,omitempty,int"` + HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"` + Hotplug types2.CustomCommaSeparatedList `json:"hotplug,omitempty" url:"hotplug,omitempty,comma"` + Hugepages *string `json:"hugepages,omitempty" url:"hugepages,omitempty"` + IDEDevices CustomStorageDevices `json:"ide,omitempty" url:",omitempty"` + KeyboardLayout *string `json:"keyboard,omitempty" url:"keyboard,omitempty"` + KVMArguments *string `json:"args,omitempty" url:"args,omitempty,space"` + KVMEnabled *types2.CustomBool `json:"kvm,omitempty" url:"kvm,omitempty,int"` + LocalTime *types2.CustomBool `json:"localtime,omitempty" url:"localtime,omitempty,int"` + Lock *string `json:"lock,omitempty" url:"lock,omitempty"` + Machine *string `json:"machine,omitempty" url:"machine,omitempty"` + MigrateDowntime *float64 `json:"migrate_downtime,omitempty" url:"migrate_downtime,omitempty"` + MigrateSpeed *int `json:"migrate_speed,omitempty" url:"migrate_speed,omitempty"` + Name *string `json:"name,omitempty" url:"name,omitempty"` + NetworkDevices CustomNetworkDevices `json:"net,omitempty" url:"net,omitempty"` + NUMADevices CustomNUMADevices `json:"numa_devices,omitempty" url:"numa,omitempty"` + NUMAEnabled *types2.CustomBool `json:"numa,omitempty" url:"numa,omitempty,int"` + OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"` + Overwrite *types2.CustomBool `json:"force,omitempty" url:"force,omitempty,int"` + PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"` + PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` + Revert *string `json:"revert,omitempty" url:"revert,omitempty"` + SATADevices CustomStorageDevices `json:"sata,omitempty" url:"sata,omitempty"` + SCSIDevices CustomStorageDevices `json:"scsi,omitempty" url:"scsi,omitempty"` + SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"` + SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"` + SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"` + SkipLock *types2.CustomBool `json:"skiplock,omitempty" url:"skiplock,omitempty,int"` + SMBIOS *CustomSMBIOS `json:"smbios1,omitempty" url:"smbios1,omitempty"` + SpiceEnhancements *CustomSpiceEnhancements `json:"spice_enhancements,omitempty" url:"spice_enhancements,omitempty"` + StartDate *string `json:"startdate,omitempty" url:"startdate,omitempty"` + StartOnBoot *types2.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"` + StartupOrder *CustomStartupOrder `json:"startup,omitempty" url:"startup,omitempty"` + TabletDeviceEnabled *types2.CustomBool `json:"tablet,omitempty" url:"tablet,omitempty,int"` + Tags *string `json:"tags,omitempty" url:"tags,omitempty"` + Template *types2.CustomBool `json:"template,omitempty" url:"template,omitempty,int"` + TimeDriftFixEnabled *types2.CustomBool `json:"tdf,omitempty" url:"tdf,omitempty,int"` + USBDevices CustomUSBDevices `json:"usb,omitempty" url:"usb,omitempty"` + VGADevice *CustomVGADevice `json:"vga,omitempty" url:"vga,omitempty"` + VirtualCPUCount *int `json:"vcpus,omitempty" url:"vcpus,omitempty"` + VirtualIODevices CustomStorageDevices `json:"virtio,omitempty" url:"virtio,omitempty"` + VMGenerationID *string `json:"vmgenid,omitempty" url:"vmgenid,omitempty"` + VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"` + VMStateDatastoreID *string `json:"vmstatestorage,omitempty" url:"vmstatestorage,omitempty"` + WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"` } // CreateResponseBody contains the body from a create response. @@ -355,137 +356,137 @@ type GetResponseBody struct { // GetResponseData contains the data from an virtual machine get response. type GetResponseData struct { - ACPI *types.CustomBool `json:"acpi,omitempty"` - Agent *CustomAgent `json:"agent,omitempty"` - AllowReboot *types.CustomBool `json:"reboot,omitempty"` - AudioDevice *CustomAudioDevice `json:"audio0,omitempty"` - Autostart *types.CustomBool `json:"autostart,omitempty"` - BackupFile *string `json:"archive,omitempty"` - BandwidthLimit *int `json:"bwlimit,omitempty"` - BIOS *string `json:"bios,omitempty"` - BootDisk *string `json:"bootdisk,omitempty"` - BootOrder *string `json:"boot,omitempty"` - CDROM *string `json:"cdrom,omitempty"` - CloudInitDNSDomain *string `json:"searchdomain,omitempty"` - CloudInitDNSServer *string `json:"nameserver,omitempty"` - CloudInitFiles *CustomCloudInitFiles `json:"cicustom,omitempty"` - CloudInitPassword *string `json:"cipassword,omitempty"` - CloudInitSSHKeys *CustomCloudInitSSHKeys `json:"sshkeys,omitempty"` - CloudInitType *string `json:"citype,omitempty"` - CloudInitUsername *string `json:"ciuser,omitempty"` - CPUArchitecture *string `json:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty"` - CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty"` - CPULimit *int `json:"cpulimit,omitempty"` - CPUSockets *int `json:"sockets,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty"` - DedicatedMemory *int `json:"memory,omitempty"` - DeletionProtection *types.CustomBool `json:"protection,omitempty"` - Description *string `json:"description,omitempty"` - EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty"` - FloatingMemory *int `json:"balloon,omitempty"` - FloatingMemoryShares *int `json:"shares,omitempty"` - Freeze *types.CustomBool `json:"freeze,omitempty"` - HookScript *string `json:"hookscript,omitempty"` - Hotplug *types.CustomCommaSeparatedList `json:"hotplug,omitempty"` - Hugepages *string `json:"hugepages,omitempty"` - IDEDevice0 *CustomStorageDevice `json:"ide0,omitempty"` - IDEDevice1 *CustomStorageDevice `json:"ide1,omitempty"` - IDEDevice2 *CustomStorageDevice `json:"ide2,omitempty"` - IDEDevice3 *CustomStorageDevice `json:"ide3,omitempty"` - IPConfig0 *CustomCloudInitIPConfig `json:"ipconfig0,omitempty"` - IPConfig1 *CustomCloudInitIPConfig `json:"ipconfig1,omitempty"` - IPConfig2 *CustomCloudInitIPConfig `json:"ipconfig2,omitempty"` - IPConfig3 *CustomCloudInitIPConfig `json:"ipconfig3,omitempty"` - IPConfig4 *CustomCloudInitIPConfig `json:"ipconfig4,omitempty"` - IPConfig5 *CustomCloudInitIPConfig `json:"ipconfig5,omitempty"` - IPConfig6 *CustomCloudInitIPConfig `json:"ipconfig6,omitempty"` - IPConfig7 *CustomCloudInitIPConfig `json:"ipconfig7,omitempty"` - KeyboardLayout *string `json:"keyboard,omitempty"` - KVMArguments *string `json:"args,omitempty"` - KVMEnabled *types.CustomBool `json:"kvm,omitempty"` - LocalTime *types.CustomBool `json:"localtime,omitempty"` - Lock *string `json:"lock,omitempty"` - Machine *string `json:"machine,omitempty"` - MigrateDowntime *float64 `json:"migrate_downtime,omitempty"` - MigrateSpeed *int `json:"migrate_speed,omitempty"` - Name *string `json:"name,omitempty"` - NetworkDevice0 *CustomNetworkDevice `json:"net0,omitempty"` - NetworkDevice1 *CustomNetworkDevice `json:"net1,omitempty"` - NetworkDevice2 *CustomNetworkDevice `json:"net2,omitempty"` - NetworkDevice3 *CustomNetworkDevice `json:"net3,omitempty"` - NetworkDevice4 *CustomNetworkDevice `json:"net4,omitempty"` - NetworkDevice5 *CustomNetworkDevice `json:"net5,omitempty"` - NetworkDevice6 *CustomNetworkDevice `json:"net6,omitempty"` - NetworkDevice7 *CustomNetworkDevice `json:"net7,omitempty"` - NUMADevices *CustomNUMADevices `json:"numa_devices,omitempty"` - NUMAEnabled *types.CustomBool `json:"numa,omitempty"` - OSType *string `json:"ostype,omitempty"` - Overwrite *types.CustomBool `json:"force,omitempty"` - PCIDevice0 *CustomPCIDevice `json:"hostpci0,omitempty"` - PCIDevice1 *CustomPCIDevice `json:"hostpci1,omitempty"` - PCIDevice2 *CustomPCIDevice `json:"hostpci2,omitempty"` - PCIDevice3 *CustomPCIDevice `json:"hostpci3,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - Revert *string `json:"revert,omitempty"` - SATADevice0 *CustomStorageDevice `json:"sata0,omitempty"` - SATADevice1 *CustomStorageDevice `json:"sata1,omitempty"` - SATADevice2 *CustomStorageDevice `json:"sata2,omitempty"` - SATADevice3 *CustomStorageDevice `json:"sata3,omitempty"` - SATADevice4 *CustomStorageDevice `json:"sata4,omitempty"` - SATADevice5 *CustomStorageDevice `json:"sata5,omitempty"` - SCSIDevice0 *CustomStorageDevice `json:"scsi0,omitempty"` - SCSIDevice1 *CustomStorageDevice `json:"scsi1,omitempty"` - SCSIDevice2 *CustomStorageDevice `json:"scsi2,omitempty"` - SCSIDevice3 *CustomStorageDevice `json:"scsi3,omitempty"` - SCSIDevice4 *CustomStorageDevice `json:"scsi4,omitempty"` - SCSIDevice5 *CustomStorageDevice `json:"scsi5,omitempty"` - SCSIDevice6 *CustomStorageDevice `json:"scsi6,omitempty"` - SCSIDevice7 *CustomStorageDevice `json:"scsi7,omitempty"` - SCSIDevice8 *CustomStorageDevice `json:"scsi8,omitempty"` - SCSIDevice9 *CustomStorageDevice `json:"scsi9,omitempty"` - SCSIDevice10 *CustomStorageDevice `json:"scsi10,omitempty"` - SCSIDevice11 *CustomStorageDevice `json:"scsi11,omitempty"` - SCSIDevice12 *CustomStorageDevice `json:"scsi12,omitempty"` - SCSIDevice13 *CustomStorageDevice `json:"scsi13,omitempty"` - SCSIHardware *string `json:"scsihw,omitempty"` - SerialDevice0 *string `json:"serial0,omitempty"` - SerialDevice1 *string `json:"serial1,omitempty"` - SerialDevice2 *string `json:"serial2,omitempty"` - SerialDevice3 *string `json:"serial3,omitempty"` - SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty"` - SkipLock *types.CustomBool `json:"skiplock,omitempty"` - SMBIOS *CustomSMBIOS `json:"smbios1,omitempty"` - SpiceEnhancements *CustomSpiceEnhancements `json:"spice_enhancements,omitempty"` - StartDate *string `json:"startdate,omitempty"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty"` - StartupOrder *CustomStartupOrder `json:"startup,omitempty"` - TabletDeviceEnabled *types.CustomBool `json:"tablet,omitempty"` - Tags *string `json:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty"` - TimeDriftFixEnabled *types.CustomBool `json:"tdf,omitempty"` - USBDevices *CustomUSBDevices `json:"usb,omitempty"` - VGADevice *CustomVGADevice `json:"vga,omitempty"` - VirtualCPUCount *int `json:"vcpus,omitempty"` - VirtualIODevice0 *CustomStorageDevice `json:"virtio0,omitempty"` - VirtualIODevice1 *CustomStorageDevice `json:"virtio1,omitempty"` - VirtualIODevice2 *CustomStorageDevice `json:"virtio2,omitempty"` - VirtualIODevice3 *CustomStorageDevice `json:"virtio3,omitempty"` - VirtualIODevice4 *CustomStorageDevice `json:"virtio4,omitempty"` - VirtualIODevice5 *CustomStorageDevice `json:"virtio5,omitempty"` - VirtualIODevice6 *CustomStorageDevice `json:"virtio6,omitempty"` - VirtualIODevice7 *CustomStorageDevice `json:"virtio7,omitempty"` - VirtualIODevice8 *CustomStorageDevice `json:"virtio8,omitempty"` - VirtualIODevice9 *CustomStorageDevice `json:"virtio9,omitempty"` - VirtualIODevice10 *CustomStorageDevice `json:"virtio10,omitempty"` - VirtualIODevice11 *CustomStorageDevice `json:"virtio11,omitempty"` - VirtualIODevice12 *CustomStorageDevice `json:"virtio12,omitempty"` - VirtualIODevice13 *CustomStorageDevice `json:"virtio13,omitempty"` - VirtualIODevice14 *CustomStorageDevice `json:"virtio14,omitempty"` - VirtualIODevice15 *CustomStorageDevice `json:"virtio15,omitempty"` - VMGenerationID *string `json:"vmgenid,omitempty"` - VMStateDatastoreID *string `json:"vmstatestorage,omitempty"` - WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"` + ACPI *types2.CustomBool `json:"acpi,omitempty"` + Agent *CustomAgent `json:"agent,omitempty"` + AllowReboot *types2.CustomBool `json:"reboot,omitempty"` + AudioDevice *CustomAudioDevice `json:"audio0,omitempty"` + Autostart *types2.CustomBool `json:"autostart,omitempty"` + BackupFile *string `json:"archive,omitempty"` + BandwidthLimit *int `json:"bwlimit,omitempty"` + BIOS *string `json:"bios,omitempty"` + BootDisk *string `json:"bootdisk,omitempty"` + BootOrder *string `json:"boot,omitempty"` + CDROM *string `json:"cdrom,omitempty"` + CloudInitDNSDomain *string `json:"searchdomain,omitempty"` + CloudInitDNSServer *string `json:"nameserver,omitempty"` + CloudInitFiles *CustomCloudInitFiles `json:"cicustom,omitempty"` + CloudInitPassword *string `json:"cipassword,omitempty"` + CloudInitSSHKeys *CustomCloudInitSSHKeys `json:"sshkeys,omitempty"` + CloudInitType *string `json:"citype,omitempty"` + CloudInitUsername *string `json:"ciuser,omitempty"` + CPUArchitecture *string `json:"arch,omitempty"` + CPUCores *int `json:"cores,omitempty"` + CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty"` + CPULimit *int `json:"cpulimit,omitempty"` + CPUSockets *int `json:"sockets,omitempty"` + CPUUnits *int `json:"cpuunits,omitempty"` + DedicatedMemory *int `json:"memory,omitempty"` + DeletionProtection *types2.CustomBool `json:"protection,omitempty"` + Description *string `json:"description,omitempty"` + EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty"` + FloatingMemory *int `json:"balloon,omitempty"` + FloatingMemoryShares *int `json:"shares,omitempty"` + Freeze *types2.CustomBool `json:"freeze,omitempty"` + HookScript *string `json:"hookscript,omitempty"` + Hotplug *types2.CustomCommaSeparatedList `json:"hotplug,omitempty"` + Hugepages *string `json:"hugepages,omitempty"` + IDEDevice0 *CustomStorageDevice `json:"ide0,omitempty"` + IDEDevice1 *CustomStorageDevice `json:"ide1,omitempty"` + IDEDevice2 *CustomStorageDevice `json:"ide2,omitempty"` + IDEDevice3 *CustomStorageDevice `json:"ide3,omitempty"` + IPConfig0 *CustomCloudInitIPConfig `json:"ipconfig0,omitempty"` + IPConfig1 *CustomCloudInitIPConfig `json:"ipconfig1,omitempty"` + IPConfig2 *CustomCloudInitIPConfig `json:"ipconfig2,omitempty"` + IPConfig3 *CustomCloudInitIPConfig `json:"ipconfig3,omitempty"` + IPConfig4 *CustomCloudInitIPConfig `json:"ipconfig4,omitempty"` + IPConfig5 *CustomCloudInitIPConfig `json:"ipconfig5,omitempty"` + IPConfig6 *CustomCloudInitIPConfig `json:"ipconfig6,omitempty"` + IPConfig7 *CustomCloudInitIPConfig `json:"ipconfig7,omitempty"` + KeyboardLayout *string `json:"keyboard,omitempty"` + KVMArguments *string `json:"args,omitempty"` + KVMEnabled *types2.CustomBool `json:"kvm,omitempty"` + LocalTime *types2.CustomBool `json:"localtime,omitempty"` + Lock *string `json:"lock,omitempty"` + Machine *string `json:"machine,omitempty"` + MigrateDowntime *float64 `json:"migrate_downtime,omitempty"` + MigrateSpeed *int `json:"migrate_speed,omitempty"` + Name *string `json:"name,omitempty"` + NetworkDevice0 *CustomNetworkDevice `json:"net0,omitempty"` + NetworkDevice1 *CustomNetworkDevice `json:"net1,omitempty"` + NetworkDevice2 *CustomNetworkDevice `json:"net2,omitempty"` + NetworkDevice3 *CustomNetworkDevice `json:"net3,omitempty"` + NetworkDevice4 *CustomNetworkDevice `json:"net4,omitempty"` + NetworkDevice5 *CustomNetworkDevice `json:"net5,omitempty"` + NetworkDevice6 *CustomNetworkDevice `json:"net6,omitempty"` + NetworkDevice7 *CustomNetworkDevice `json:"net7,omitempty"` + NUMADevices *CustomNUMADevices `json:"numa_devices,omitempty"` + NUMAEnabled *types2.CustomBool `json:"numa,omitempty"` + OSType *string `json:"ostype,omitempty"` + Overwrite *types2.CustomBool `json:"force,omitempty"` + PCIDevice0 *CustomPCIDevice `json:"hostpci0,omitempty"` + PCIDevice1 *CustomPCIDevice `json:"hostpci1,omitempty"` + PCIDevice2 *CustomPCIDevice `json:"hostpci2,omitempty"` + PCIDevice3 *CustomPCIDevice `json:"hostpci3,omitempty"` + PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` + Revert *string `json:"revert,omitempty"` + SATADevice0 *CustomStorageDevice `json:"sata0,omitempty"` + SATADevice1 *CustomStorageDevice `json:"sata1,omitempty"` + SATADevice2 *CustomStorageDevice `json:"sata2,omitempty"` + SATADevice3 *CustomStorageDevice `json:"sata3,omitempty"` + SATADevice4 *CustomStorageDevice `json:"sata4,omitempty"` + SATADevice5 *CustomStorageDevice `json:"sata5,omitempty"` + SCSIDevice0 *CustomStorageDevice `json:"scsi0,omitempty"` + SCSIDevice1 *CustomStorageDevice `json:"scsi1,omitempty"` + SCSIDevice2 *CustomStorageDevice `json:"scsi2,omitempty"` + SCSIDevice3 *CustomStorageDevice `json:"scsi3,omitempty"` + SCSIDevice4 *CustomStorageDevice `json:"scsi4,omitempty"` + SCSIDevice5 *CustomStorageDevice `json:"scsi5,omitempty"` + SCSIDevice6 *CustomStorageDevice `json:"scsi6,omitempty"` + SCSIDevice7 *CustomStorageDevice `json:"scsi7,omitempty"` + SCSIDevice8 *CustomStorageDevice `json:"scsi8,omitempty"` + SCSIDevice9 *CustomStorageDevice `json:"scsi9,omitempty"` + SCSIDevice10 *CustomStorageDevice `json:"scsi10,omitempty"` + SCSIDevice11 *CustomStorageDevice `json:"scsi11,omitempty"` + SCSIDevice12 *CustomStorageDevice `json:"scsi12,omitempty"` + SCSIDevice13 *CustomStorageDevice `json:"scsi13,omitempty"` + SCSIHardware *string `json:"scsihw,omitempty"` + SerialDevice0 *string `json:"serial0,omitempty"` + SerialDevice1 *string `json:"serial1,omitempty"` + SerialDevice2 *string `json:"serial2,omitempty"` + SerialDevice3 *string `json:"serial3,omitempty"` + SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty"` + SkipLock *types2.CustomBool `json:"skiplock,omitempty"` + SMBIOS *CustomSMBIOS `json:"smbios1,omitempty"` + SpiceEnhancements *CustomSpiceEnhancements `json:"spice_enhancements,omitempty"` + StartDate *string `json:"startdate,omitempty"` + StartOnBoot *types2.CustomBool `json:"onboot,omitempty"` + StartupOrder *CustomStartupOrder `json:"startup,omitempty"` + TabletDeviceEnabled *types2.CustomBool `json:"tablet,omitempty"` + Tags *string `json:"tags,omitempty"` + Template *types2.CustomBool `json:"template,omitempty"` + TimeDriftFixEnabled *types2.CustomBool `json:"tdf,omitempty"` + USBDevices *CustomUSBDevices `json:"usb,omitempty"` + VGADevice *CustomVGADevice `json:"vga,omitempty"` + VirtualCPUCount *int `json:"vcpus,omitempty"` + VirtualIODevice0 *CustomStorageDevice `json:"virtio0,omitempty"` + VirtualIODevice1 *CustomStorageDevice `json:"virtio1,omitempty"` + VirtualIODevice2 *CustomStorageDevice `json:"virtio2,omitempty"` + VirtualIODevice3 *CustomStorageDevice `json:"virtio3,omitempty"` + VirtualIODevice4 *CustomStorageDevice `json:"virtio4,omitempty"` + VirtualIODevice5 *CustomStorageDevice `json:"virtio5,omitempty"` + VirtualIODevice6 *CustomStorageDevice `json:"virtio6,omitempty"` + VirtualIODevice7 *CustomStorageDevice `json:"virtio7,omitempty"` + VirtualIODevice8 *CustomStorageDevice `json:"virtio8,omitempty"` + VirtualIODevice9 *CustomStorageDevice `json:"virtio9,omitempty"` + VirtualIODevice10 *CustomStorageDevice `json:"virtio10,omitempty"` + VirtualIODevice11 *CustomStorageDevice `json:"virtio11,omitempty"` + VirtualIODevice12 *CustomStorageDevice `json:"virtio12,omitempty"` + VirtualIODevice13 *CustomStorageDevice `json:"virtio13,omitempty"` + VirtualIODevice14 *CustomStorageDevice `json:"virtio14,omitempty"` + VirtualIODevice15 *CustomStorageDevice `json:"virtio15,omitempty"` + VMGenerationID *string `json:"vmgenid,omitempty"` + VMStateDatastoreID *string `json:"vmstatestorage,omitempty"` + WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"` } // GetStatusResponseBody contains the body from a VM get status response. @@ -495,19 +496,19 @@ type GetStatusResponseBody struct { // GetStatusResponseData contains the data from a VM get status response. type GetStatusResponseData struct { - AgentEnabled *types.CustomBool `json:"agent,omitempty"` - CPUCount *float64 `json:"cpus,omitempty"` - Lock *string `json:"lock,omitempty"` - MemoryAllocation *int `json:"maxmem,omitempty"` - Name *string `json:"name,omitempty"` - PID *int `json:"pid,omitempty"` - QMPStatus *string `json:"qmpstatus,omitempty"` - RootDiskSize *int `json:"maxdisk,omitempty"` - SpiceSupport *types.CustomBool `json:"spice,omitempty"` - Status string `json:"status,omitempty"` - Tags *string `json:"tags,omitempty"` - Uptime *int `json:"uptime,omitempty"` - VMID *int `json:"vmid,omitempty"` + AgentEnabled *types2.CustomBool `json:"agent,omitempty"` + CPUCount *float64 `json:"cpus,omitempty"` + Lock *string `json:"lock,omitempty"` + MemoryAllocation *int `json:"maxmem,omitempty"` + Name *string `json:"name,omitempty"` + PID *int `json:"pid,omitempty"` + QMPStatus *string `json:"qmpstatus,omitempty"` + RootDiskSize *int `json:"maxdisk,omitempty"` + SpiceSupport *types2.CustomBool `json:"spice,omitempty"` + Status string `json:"status,omitempty"` + Tags *string `json:"tags,omitempty"` + Uptime *int `json:"uptime,omitempty"` + VMID *int `json:"vmid,omitempty"` } // ListResponseBody contains the body from a virtual machine list response. @@ -524,10 +525,10 @@ type ListResponseData struct { // MigrateRequestBody contains the body for a VM migration request. type MigrateRequestBody struct { - OnlineMigration *types.CustomBool `json:"online,omitempty" url:"online,omitempty"` - TargetNode string `json:"target" url:"target"` - TargetStorage *string `json:"targetstorage,omitempty" url:"targetstorage,omitempty"` - WithLocalDisks *types.CustomBool `json:"with-local-disks,omitempty" url:"with-local-disks,omitempty,int"` + OnlineMigration *types2.CustomBool `json:"online,omitempty" url:"online,omitempty"` + TargetNode string `json:"target" url:"target"` + TargetStorage *string `json:"targetstorage,omitempty" url:"targetstorage,omitempty"` + WithLocalDisks *types2.CustomBool `json:"with-local-disks,omitempty" url:"with-local-disks,omitempty,int"` } // MigrateResponseBody contains the body from a VM migrate response. @@ -537,12 +538,12 @@ type MigrateResponseBody struct { // MoveDiskRequestBody contains the body for a VM move disk request. type MoveDiskRequestBody struct { - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - DeleteOriginalDisk *types.CustomBool `json:"delete,omitempty" url:"delete,omitempty,int"` - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` - Disk string `json:"disk" url:"disk"` - TargetStorage string `json:"storage" url:"storage"` - TargetStorageFormat *string `json:"format,omitempty" url:"format,omitempty"` + BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` + DeleteOriginalDisk *types2.CustomBool `json:"delete,omitempty" url:"delete,omitempty,int"` + Digest *string `json:"digest,omitempty" url:"digest,omitempty"` + Disk string `json:"disk" url:"disk"` + TargetStorage string `json:"storage" url:"storage"` + TargetStorageFormat *string `json:"format,omitempty" url:"format,omitempty"` } // MoveDiskResponseBody contains the body from a VM move disk response. @@ -562,18 +563,18 @@ type RebootResponseBody struct { // ResizeDiskRequestBody contains the body for a VM resize disk request. type ResizeDiskRequestBody struct { - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` - Disk string `json:"disk" url:"disk"` - Size types.DiskSize `json:"size" url:"size"` - SkipLock *types.CustomBool `json:"skiplock,omitempty" url:"skiplock,omitempty,int"` + Digest *string `json:"digest,omitempty" url:"digest,omitempty"` + Disk string `json:"disk" url:"disk"` + Size types.DiskSize `json:"size" url:"size"` + SkipLock *types2.CustomBool `json:"skiplock,omitempty" url:"skiplock,omitempty,int"` } // ShutdownRequestBody contains the body for a VM shutdown request. type ShutdownRequestBody struct { - ForceStop *types.CustomBool `json:"forceStop,omitempty" url:"forceStop,omitempty,int"` - KeepActive *types.CustomBool `json:"keepActive,omitempty" url:"keepActive,omitempty,int"` - SkipLock *types.CustomBool `json:"skipLock,omitempty" url:"skipLock,omitempty,int"` - Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` + ForceStop *types2.CustomBool `json:"forceStop,omitempty" url:"forceStop,omitempty,int"` + KeepActive *types2.CustomBool `json:"keepActive,omitempty" url:"keepActive,omitempty,int"` + SkipLock *types2.CustomBool `json:"skipLock,omitempty" url:"skipLock,omitempty,int"` + Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` } // ShutdownResponseBody contains the body from a VM shutdown response. @@ -1267,15 +1268,15 @@ func (r *CustomAgent) UnmarshalJSON(b []byte) error { v := strings.Split(strings.TrimSpace(p), "=") if len(v) == 1 { - enabled := types.CustomBool(v[0] == "1") + enabled := types2.CustomBool(v[0] == "1") r.Enabled = &enabled } else if len(v) == 2 { switch v[0] { case "enabled": - enabled := types.CustomBool(v[1] == "1") + enabled := types2.CustomBool(v[1] == "1") r.Enabled = &enabled case "fstrim_cloned_disks": - fstrim := types.CustomBool(v[1] == "1") + fstrim := types2.CustomBool(v[1] == "1") r.TrimClonedDisks = &fstrim case "type": r.Type = &v[1] @@ -1450,7 +1451,7 @@ func (r *CustomCPUEmulation) UnmarshalJSON(b []byte) error { r.Flags = &f } case "hidden": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Hidden = &bv case "hv-vendor-id": r.HVVendorID = &v[1] @@ -1513,10 +1514,10 @@ func (r *CustomNetworkDevice) UnmarshalJSON(b []byte) error { case "bridge": r.Bridge = &v[1] case "firewall": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.Firewall = &bv case "link_down": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.LinkDown = &bv case "macaddr": r.MACAddress = &v[1] @@ -1597,15 +1598,15 @@ func (r *CustomPCIDevice) UnmarshalJSON(b []byte) error { case "mdev": r.MDev = &v[1] case "pcie": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.PCIExpress = &bv case "rombar": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.ROMBAR = &bv case "romfile": r.ROMFile = &v[1] case "x-vga": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.XVGA = &bv } } @@ -1661,7 +1662,7 @@ func (r *CustomSMBIOS) UnmarshalJSON(b []byte) error { if len(v) == 2 { switch v[0] { case "base64": - base64 := types.CustomBool(v[1] == "1") + base64 := types2.CustomBool(v[1] == "1") r.Base64 = &base64 case "family": r.Family = &v[1] @@ -1711,7 +1712,7 @@ func (r *CustomStorageDevice) UnmarshalJSON(b []byte) error { case "aio": r.AIO = &v[1] case "backup": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.BackupEnabled = &bv case "file": r.FileVolume = v[1] @@ -1754,10 +1755,10 @@ func (r *CustomStorageDevice) UnmarshalJSON(b []byte) error { case "format": r.Format = &v[1] case "iothread": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.IOThread = &bv case "ssd": - bv := types.CustomBool(v[1] == "1") + bv := types2.CustomBool(v[1] == "1") r.SSD = &bv case "discard": r.Discard = &v[1] diff --git a/proxmox/storage/storage_types.go b/proxmox/storage/storage_types.go index bf223172..2c2a71b7 100644 --- a/proxmox/storage/storage_types.go +++ b/proxmox/storage/storage_types.go @@ -7,7 +7,7 @@ package storage import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/types" + "github.com/bpg/terraform-provider-proxmox/internal/types" ) // DatastoreGetResponseBody contains the body from a datastore get response. diff --git a/proxmox/types/helpers.go b/proxmox/types/helpers.go index 68349517..3cbed241 100644 --- a/proxmox/types/helpers.go +++ b/proxmox/types/helpers.go @@ -6,13 +6,15 @@ package types +import "github.com/bpg/terraform-provider-proxmox/internal/types" + // StrPtr returns a pointer to a string. func StrPtr(s string) *string { return &s } // BoolPtr returns a pointer to a bool. -func BoolPtr(s bool) *CustomBool { - customBool := CustomBool(s) +func BoolPtr(s bool) *types.CustomBool { + customBool := types.CustomBool(s) return &customBool } diff --git a/proxmoxtf/provider/provider.go b/proxmoxtf/provider/provider.go index 3bb7edab..402da9d2 100644 --- a/proxmoxtf/provider/provider.go +++ b/proxmoxtf/provider/provider.go @@ -30,7 +30,7 @@ func ProxmoxVirtualEnvironment() *schema.Provider { } } -func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { +func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { var err error var diags diag.Diagnostics @@ -43,44 +43,25 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{} var conn *api.Connection - // Legacy configuration, wrapped in the deprecated `virtual_environment` block - veConfigBlock := d.Get(mkProviderVirtualEnvironment).([]interface{}) - if len(veConfigBlock) > 0 { - veConfig := veConfigBlock[0].(map[string]interface{}) - creds, err = api.NewCredentials( - veConfig[mkProviderUsername].(string), - veConfig[mkProviderPassword].(string), - veConfig[mkProviderOTP].(string), - "", - ) - diags = append(diags, diag.FromErr(err)...) + creds, err = api.NewCredentials( + d.Get(mkProviderUsername).(string), + d.Get(mkProviderPassword).(string), + d.Get(mkProviderOTP).(string), + d.Get(mkProviderAPIToken).(string), + ) + diags = append(diags, diag.FromErr(err)...) - conn, err = api.NewConnection( - veConfig[mkProviderEndpoint].(string), - veConfig[mkProviderInsecure].(bool), - ) - diags = append(diags, diag.FromErr(err)...) - } else { - creds, err = api.NewCredentials( - d.Get(mkProviderUsername).(string), - d.Get(mkProviderPassword).(string), - d.Get(mkProviderOTP).(string), - d.Get(mkProviderAPIToken).(string), - ) - diags = append(diags, diag.FromErr(err)...) - - conn, err = api.NewConnection( - d.Get(mkProviderEndpoint).(string), - d.Get(mkProviderInsecure).(bool), - ) - diags = append(diags, diag.FromErr(err)...) - } + conn, err = api.NewConnection( + d.Get(mkProviderEndpoint).(string), + d.Get(mkProviderInsecure).(bool), + ) + diags = append(diags, diag.FromErr(err)...) if diags.HasError() { return nil, diags } - apiClient, err = api.NewClient(ctx, creds, conn) + apiClient, err = api.NewClient(creds, conn) if err != nil { return nil, diag.Errorf("error creating virtual environment client: %s", err) } @@ -145,12 +126,27 @@ type apiResolver struct { func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, error) { nc := &nodes.Client{Client: r.c, NodeName: nodeName} - ip, err := nc.GetIP(ctx) + networkDevices, err := nc.ListNetworkInterfaces(ctx) if err != nil { - return "", fmt.Errorf("failed to get node IP: %w", err) + return "", fmt.Errorf("failed to list network devices of node \"%s\": %w", nc.NodeName, err) } - return ip, nil + nodeAddress := "" + + for _, d := range networkDevices { + if d.Address != nil { + nodeAddress = *d.Address + break + } + } + + if nodeAddress == "" { + return "", fmt.Errorf("failed to determine the IP address of node \"%s\"", nc.NodeName) + } + + nodeAddressParts := strings.Split(nodeAddress, "/") + + return nodeAddressParts[0], nil } type apiResolverWithOverrides struct { diff --git a/proxmoxtf/provider/provider_test.go b/proxmoxtf/provider/provider_test.go index cf897d64..9156cf73 100644 --- a/proxmoxtf/provider/provider_test.go +++ b/proxmoxtf/provider/provider_test.go @@ -33,7 +33,6 @@ func TestProviderSchema(t *testing.T) { } test.AssertOptionalArguments(t, s, []string{ - mkProviderVirtualEnvironment, mkProviderUsername, mkProviderPassword, mkProviderEndpoint, @@ -42,32 +41,11 @@ func TestProviderSchema(t *testing.T) { }) test.AssertValueTypes(t, s, map[string]schema.ValueType{ - mkProviderVirtualEnvironment: schema.TypeList, - mkProviderUsername: schema.TypeString, - mkProviderPassword: schema.TypeString, - mkProviderEndpoint: schema.TypeString, - mkProviderInsecure: schema.TypeBool, - mkProviderOTP: schema.TypeString, - }) - - veSchema := test.AssertNestedSchemaExistence(t, s, mkProviderVirtualEnvironment) - - test.AssertOptionalArguments(t, veSchema, []string{ - mkProviderEndpoint, - mkProviderInsecure, - mkProviderOTP, - mkProviderPassword, - mkProviderUsername, - mkProviderSSH, - }) - - test.AssertValueTypes(t, veSchema, map[string]schema.ValueType{ + mkProviderUsername: schema.TypeString, + mkProviderPassword: schema.TypeString, mkProviderEndpoint: schema.TypeString, mkProviderInsecure: schema.TypeBool, mkProviderOTP: schema.TypeString, - mkProviderPassword: schema.TypeString, - mkProviderUsername: schema.TypeString, - mkProviderSSH: schema.TypeList, }) providerSSHSchema := test.AssertNestedSchemaExistence(t, s, mkProviderSSH) diff --git a/proxmoxtf/provider/resources.go b/proxmoxtf/provider/resources.go index 19fa1001..f914b7c4 100644 --- a/proxmoxtf/provider/resources.go +++ b/proxmoxtf/provider/resources.go @@ -17,21 +17,22 @@ import ( func createResourceMap() map[string]*schema.Resource { return map[string]*schema.Resource{ "proxmox_virtual_environment_certificate": resource.Certificate(), - "proxmox_virtual_environment_firewall_alias": firewall.Alias(), - "proxmox_virtual_environment_firewall_ipset": firewall.IPSet(), - "proxmox_virtual_environment_firewall_rules": firewall.Rules(), - "proxmox_virtual_environment_firewall_options": firewall.Options(), - "proxmox_virtual_environment_cluster_firewall_security_group": clusterfirewall.SecurityGroup(), "proxmox_virtual_environment_cluster_firewall": clusterfirewall.Firewall(), + "proxmox_virtual_environment_cluster_firewall_security_group": clusterfirewall.SecurityGroup(), "proxmox_virtual_environment_container": resource.Container(), "proxmox_virtual_environment_dns": resource.DNS(), "proxmox_virtual_environment_file": resource.File(), + "proxmox_virtual_environment_firewall_alias": firewall.Alias(), + "proxmox_virtual_environment_firewall_ipset": firewall.IPSet(), + "proxmox_virtual_environment_firewall_options": firewall.Options(), + "proxmox_virtual_environment_firewall_rules": firewall.Rules(), "proxmox_virtual_environment_group": resource.Group(), "proxmox_virtual_environment_hosts": resource.Hosts(), - "proxmox_virtual_environment_pool": resource.Pool(), - "proxmox_virtual_environment_role": resource.Role(), - "proxmox_virtual_environment_time": resource.Time(), - "proxmox_virtual_environment_user": resource.User(), - "proxmox_virtual_environment_vm": resource.VM(), + // "proxmox_virtual_environment_network_linux_bridge": resource.NetworkLinuxBridge(), + "proxmox_virtual_environment_pool": resource.Pool(), + "proxmox_virtual_environment_role": resource.Role(), + "proxmox_virtual_environment_time": resource.Time(), + "proxmox_virtual_environment_user": resource.User(), + "proxmox_virtual_environment_vm": resource.VM(), } } diff --git a/proxmoxtf/provider/schema.go b/proxmoxtf/provider/schema.go index 0e19e75a..3b0357a8 100644 --- a/proxmoxtf/provider/schema.go +++ b/proxmoxtf/provider/schema.go @@ -7,7 +7,6 @@ package provider import ( - "fmt" "os" "regexp" @@ -16,19 +15,18 @@ import ( ) const ( - dvProviderOTP = "" - mkProviderVirtualEnvironment = "virtual_environment" - mkProviderEndpoint = "endpoint" - mkProviderInsecure = "insecure" - mkProviderOTP = "otp" - mkProviderPassword = "password" - mkProviderUsername = "username" - mkProviderAPIToken = "api_token" - mkProviderSSH = "ssh" - mkProviderSSHUsername = "username" - mkProviderSSHPassword = "password" - mkProviderSSHAgent = "agent" - mkProviderSSHAgentSocket = "agent_socket" + dvProviderOTP = "" + mkProviderEndpoint = "endpoint" + mkProviderInsecure = "insecure" + mkProviderOTP = "otp" + mkProviderPassword = "password" + mkProviderUsername = "username" + mkProviderAPIToken = "api_token" + mkProviderSSH = "ssh" + mkProviderSSHUsername = "username" + mkProviderSSHPassword = "password" + mkProviderSSHAgent = "agent" + mkProviderSSHAgentSocket = "agent_socket" mkProviderSSHNode = "node" mkProviderSSHNodeName = "name" @@ -36,40 +34,21 @@ const ( ) func createSchema() map[string]*schema.Schema { - providerSchema := nestedProviderSchema() - providerSchema[mkProviderVirtualEnvironment] = &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: nestedProviderSchema(), - }, - MaxItems: 1, - Deprecated: "Move attributes out of virtual_environment block", - } - - return providerSchema -} - -func nestedProviderSchema() map[string]*schema.Schema { return map[string]*schema.Schema{ mkProviderEndpoint: { Type: schema.TypeString, Optional: true, - Description: "The endpoint for the Proxmox Virtual Environment API", + Description: "The endpoint for the Proxmox VE API.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_ENDPOINT", "PM_VE_ENDPOINT"}, nil, ), - AtLeastOneOf: []string{ - mkProviderEndpoint, - fmt.Sprintf("%s.0.%s", mkProviderVirtualEnvironment, mkProviderEndpoint), - }, ValidateFunc: validation.IsURLWithHTTPorHTTPS, }, mkProviderInsecure: { Type: schema.TypeBool, Optional: true, - Description: "Whether to skip the TLS verification step", + Description: "Whether to skip the TLS verification step.", DefaultFunc: func() (interface{}, error) { for _, k := range []string{"PROXMOX_VE_INSECURE", "PM_VE_INSECURE"} { v := os.Getenv(k) @@ -85,24 +64,25 @@ func nestedProviderSchema() map[string]*schema.Schema { mkProviderOTP: { Type: schema.TypeString, Optional: true, - Description: "The one-time password for the Proxmox Virtual Environment API", + Description: "The one-time password for the Proxmox VE API.", + Deprecated: "The `otp` attribute is deprecated and will be removed in a future release. " + + "Please use the `api_token` attribute instead.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_OTP", "PM_VE_OTP"}, - dvProviderOTP, + nil, ), }, mkProviderPassword: { Type: schema.TypeString, Optional: true, Sensitive: true, - Description: "The password for the Proxmox Virtual Environment API", + Description: "The password for the Proxmox VE API.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_PASSWORD", "PM_VE_PASSWORD"}, nil, ), AtLeastOneOf: []string{ mkProviderPassword, - fmt.Sprintf("%s.0.%s", mkProviderVirtualEnvironment, mkProviderPassword), mkProviderAPIToken, }, ValidateFunc: validation.StringIsNotEmpty, @@ -110,14 +90,13 @@ func nestedProviderSchema() map[string]*schema.Schema { mkProviderUsername: { Type: schema.TypeString, Optional: true, - Description: "The username for the Proxmox Virtual Environment API", + Description: "The username for the Proxmox VE API.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_USERNAME", "PM_VE_USERNAME"}, nil, ), AtLeastOneOf: []string{ mkProviderUsername, - fmt.Sprintf("%s.0.%s", mkProviderVirtualEnvironment, mkProviderUsername), mkProviderAPIToken, }, ValidateFunc: validation.StringIsNotEmpty, @@ -126,7 +105,7 @@ func nestedProviderSchema() map[string]*schema.Schema { Type: schema.TypeString, Optional: true, Sensitive: true, - Description: "The API token for the Proxmox Virtual Environment API", + Description: "The API token for the Proxmox VE API.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_API_TOKEN", "PM_VE_API_TOKEN"}, nil, @@ -140,14 +119,15 @@ func nestedProviderSchema() map[string]*schema.Schema { Type: schema.TypeList, Optional: true, MaxItems: 1, - Description: "The SSH connection configuration to a Proxmox node", + Description: "The SSH configuration for the Proxmox nodes.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ mkProviderSSHUsername: { Type: schema.TypeString, Optional: true, - Description: fmt.Sprintf("The username used for the SSH connection, "+ - "defaults to the user specified in '%s'", mkProviderUsername), + Description: "The username used for the SSH connection. " + + "Defaults to the value of the `username` field of the " + + "`provider` block.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_SSH_USERNAME", "PM_VE_SSH_USERNAME"}, nil, @@ -158,8 +138,9 @@ func nestedProviderSchema() map[string]*schema.Schema { Type: schema.TypeString, Optional: true, Sensitive: true, - Description: fmt.Sprintf("The password used for the SSH connection, "+ - "defaults to the password specified in '%s'", mkProviderPassword), + Description: "The password used for the SSH connection. " + + "Defaults to the value of the `password` field of the " + + "`provider` block.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"PROXMOX_VE_SSH_PASSWORD", "PM_VE_SSH_PASSWORD"}, nil, @@ -167,9 +148,10 @@ func nestedProviderSchema() map[string]*schema.Schema { ValidateFunc: validation.StringIsNotEmpty, }, mkProviderSSHAgent: { - Type: schema.TypeBool, - Optional: true, - Description: "Whether to use the SSH agent for the SSH authentication. Defaults to false", + Type: schema.TypeBool, + Optional: true, + Description: "Whether to use the SSH agent for authentication. " + + "Defaults to `false`.", DefaultFunc: func() (interface{}, error) { for _, k := range []string{"PROXMOX_VE_SSH_AGENT", "PM_VE_SSH_AGENT"} { v := os.Getenv(k) @@ -186,7 +168,7 @@ func nestedProviderSchema() map[string]*schema.Schema { Type: schema.TypeString, Optional: true, Description: "The path to the SSH agent socket. Defaults to the value of the `SSH_AUTH_SOCK` " + - "environment variable", + "environment variable.", DefaultFunc: schema.MultiEnvDefaultFunc( []string{"SSH_AUTH_SOCK", "PROXMOX_VE_SSH_AUTH_SOCK", "PM_VE_SSH_AUTH_SOCK"}, nil, @@ -197,19 +179,19 @@ func nestedProviderSchema() map[string]*schema.Schema { Type: schema.TypeList, Optional: true, MinItems: 0, - Description: "Overrides for SSH connection configuration to a Proxmox node", + Description: "Overrides for SSH connection configuration for a Proxmox VE node.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ mkProviderSSHNodeName: { Type: schema.TypeString, Required: true, - Description: "The name of the node to connect to", + Description: "The name of the Proxmox VE node.", ValidateFunc: validation.StringIsNotEmpty, }, mkProviderSSHNodeAddress: { Type: schema.TypeString, Required: true, - Description: "The address that should be used to connect to the node", + Description: "The address of the Proxmox VE node.", ValidateFunc: validation.IsIPAddress, }, }, diff --git a/proxmoxtf/resource/certificate.go b/proxmoxtf/resource/certificate.go index 927d45d0..65ed7cd2 100644 --- a/proxmoxtf/resource/certificate.go +++ b/proxmoxtf/resource/certificate.go @@ -15,8 +15,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/cluster/firewall/firewall.go b/proxmoxtf/resource/cluster/firewall/firewall.go index 06fa1d09..ebee985e 100644 --- a/proxmoxtf/resource/cluster/firewall/firewall.go +++ b/proxmoxtf/resource/cluster/firewall/firewall.go @@ -12,8 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/firewall" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" ) diff --git a/proxmoxtf/resource/container.go b/proxmoxtf/resource/container.go index ac632da4..e179b383 100644 --- a/proxmoxtf/resource/container.go +++ b/proxmoxtf/resource/container.go @@ -17,8 +17,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/firewall/ipset.go b/proxmoxtf/resource/firewall/ipset.go index b7373e94..ea132c1b 100644 --- a/proxmoxtf/resource/firewall/ipset.go +++ b/proxmoxtf/resource/firewall/ipset.go @@ -13,8 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" ) diff --git a/proxmoxtf/resource/firewall/options.go b/proxmoxtf/resource/firewall/options.go index 77b2f9c7..c7461d8c 100644 --- a/proxmoxtf/resource/firewall/options.go +++ b/proxmoxtf/resource/firewall/options.go @@ -12,8 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" ) diff --git a/proxmoxtf/resource/firewall/rules.go b/proxmoxtf/resource/firewall/rules.go index c3d79b53..70cb4e2f 100644 --- a/proxmoxtf/resource/firewall/rules.go +++ b/proxmoxtf/resource/firewall/rules.go @@ -15,8 +15,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" ) diff --git a/proxmoxtf/resource/group.go b/proxmoxtf/resource/group.go index 61453cae..3b670a59 100644 --- a/proxmoxtf/resource/group.go +++ b/proxmoxtf/resource/group.go @@ -13,8 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/access" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/role.go b/proxmoxtf/resource/role.go index 0a4ec053..6905d3b2 100644 --- a/proxmoxtf/resource/role.go +++ b/proxmoxtf/resource/role.go @@ -13,8 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/access" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/user.go b/proxmoxtf/resource/user.go index 78d3f891..10607a21 100644 --- a/proxmoxtf/resource/user.go +++ b/proxmoxtf/resource/user.go @@ -15,8 +15,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/access" - "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/utils.go b/proxmoxtf/resource/utils.go index bbb997f2..b65221b2 100644 --- a/proxmoxtf/resource/utils.go +++ b/proxmoxtf/resource/utils.go @@ -352,39 +352,6 @@ func getSCSIHardwareValidator() schema.SchemaValidateDiagFunc { }, false)) } -//nolint:unused -func getVLANIDsValidator() schema.SchemaValidateDiagFunc { - return validation.ToDiagFunc(func(i interface{}, k string) (ws []string, es []error) { - min := 1 - max := 4094 - - list, ok := i.([]interface{}) - - if !ok { - es = append(es, fmt.Errorf("expected type of %s to be []interface{}", k)) - return - } - - for li, lv := range list { - v, ok := lv.(int) - - if !ok { - es = append(es, fmt.Errorf("expected type of %s[%d] to be int", k, li)) - return - } - - if v != -1 { - if v < min || v > max { - es = append(es, fmt.Errorf("expected %s[%d] to be in the range (%d - %d), got %d", k, li, min, max, v)) - return - } - } - } - - return - }) -} - func getVMIDValidator() schema.SchemaValidateDiagFunc { return validation.ToDiagFunc(func(i interface{}, k string) (ws []string, es []error) { min := 100 diff --git a/proxmoxtf/resource/validator/network.go b/proxmoxtf/resource/validator/network.go new file mode 100644 index 00000000..698a6b1a --- /dev/null +++ b/proxmoxtf/resource/validator/network.go @@ -0,0 +1,76 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package validator + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +// VLANIDsValidator returns a schema validation function for VLAN IDs. +func VLANIDsValidator() schema.SchemaValidateDiagFunc { + return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) { + min := 1 + max := 4094 + + var ws []string + var es []error + + list, ok := i.([]interface{}) + + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be []interface{}", k)) + return ws, es + } + + for li, lv := range list { + v, ok := lv.(int) + + if !ok { + es = append(es, fmt.Errorf("expected type of %s[%d] to be int", k, li)) + return ws, es + } + + if v != -1 { + if v < min || v > max { + es = append(es, fmt.Errorf("expected %s[%d] to be in the range (%d - %d), got %d", k, li, min, max, v)) + return ws, es + } + } + } + + return ws, es + }) +} + +// NodeNetworkInterfaceBondingModes returns a schema validation function for a node network interface bonding mode. +func NodeNetworkInterfaceBondingModes() schema.SchemaValidateDiagFunc { + return validation.ToDiagFunc(validation.StringInSlice([]string{ + "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", + "balance-slb", + "lacp-balance-slb", + "lacp-balance-tcp", + }, false)) +} + +// NodeNetworkInterfaceBondingTransmitHashPolicies returns a schema validation function for a node network interface +// bonding transmit hash policy. +func NodeNetworkInterfaceBondingTransmitHashPolicies() schema.SchemaValidateDiagFunc { + return validation.ToDiagFunc(validation.StringInSlice([]string{ + "layer2", + "layer2+3", + "layer3+4", + }, false)) +} diff --git a/proxmoxtf/resource/vm.go b/proxmoxtf/resource/vm.go index 3a5f473f..73be6be9 100644 --- a/proxmoxtf/resource/vm.go +++ b/proxmoxtf/resource/vm.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + types2 "github.com/bpg/terraform-provider-proxmox/internal/types" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" "github.com/bpg/terraform-provider-proxmox/proxmox/types" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" @@ -1311,7 +1312,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d } } - fullCopy := types.CustomBool(cloneFull) + fullCopy := types2.CustomBool(cloneFull) cloneBody := &vms.CloneRequestBody{ FullCopy: &fullCopy, @@ -1392,7 +1393,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d } // Migrate to target node - withLocalDisks := types.CustomBool(true) + withLocalDisks := types2.CustomBool(true) migrateBody := &vms.MigrateRequestBody{ TargetNode: nodeName, WithLocalDisks: &withLocalDisks, @@ -1426,7 +1427,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d } // Now that the virtual machine has been cloned, we need to perform some modifications. - acpi := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)) + acpi := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)) agent := d.Get(mkResourceVirtualEnvironmentVMAgent).([]interface{}) audioDevices := vmGetAudioDeviceList(d) @@ -1442,9 +1443,9 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{}) operatingSystem := d.Get(mkResourceVirtualEnvironmentVMOperatingSystem).([]interface{}) serialDevice := d.Get(mkResourceVirtualEnvironmentVMSerialDevice).([]interface{}) - onBoot := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool)) - tabletDevice := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool)) - template := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)) + onBoot := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool)) + tabletDevice := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool)) + template := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)) vga := d.Get(mkResourceVirtualEnvironmentVMVGA).([]interface{}) updateBody := &vms.UpdateRequestBody{ @@ -1463,10 +1464,10 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d if len(agent) > 0 { agentBlock := agent[0].(map[string]interface{}) - agentEnabled := types.CustomBool( + agentEnabled := types2.CustomBool( agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool), ) - agentTrim := types.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool)) + agentTrim := types2.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool)) agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string) updateBody.Agent = &vms.CustomAgent{ @@ -1753,7 +1754,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d ) } - deleteOriginalDisk := types.CustomBool(true) + deleteOriginalDisk := types2.CustomBool(true) diskMoveBody := &vms.MoveDiskRequestBody{ DeleteOriginalDisk: &deleteOriginalDisk, @@ -1806,7 +1807,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) resource := VM() - acpi := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)) + acpi := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)) agentBlock, err := getSchemaBlock( resource, @@ -1819,10 +1820,10 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) return diag.FromErr(err) } - agentEnabled := types.CustomBool( + agentEnabled := types2.CustomBool( agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool), ) - agentTrim := types.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool)) + agentTrim := types2.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool)) agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string) kvmArguments := d.Get(mkResourceVirtualEnvironmentVMKVMArguments).(string) @@ -1936,9 +1937,9 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) serialDevices := vmGetSerialDeviceList(d) - onBoot := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool)) - tabletDevice := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool)) - template := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)) + onBoot := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool)) + tabletDevice := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool)) + template := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)) vgaDevice, err := vmGetVGADeviceObject(d) if err != nil { @@ -2161,8 +2162,8 @@ func vmCreateCustomDisks(ctx context.Context, d *schema.ResourceData, m interfac size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int) speed := block[mkResourceVirtualEnvironmentVMDiskSpeed].([]interface{}) diskInterface, _ := block[mkResourceVirtualEnvironmentVMDiskInterface].(string) - ioThread := types.CustomBool(block[mkResourceVirtualEnvironmentVMDiskIOThread].(bool)) - ssd := types.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool)) + ioThread := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskIOThread].(bool)) + ssd := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool)) discard, _ := block[mkResourceVirtualEnvironmentVMDiskDiscard].(string) if fileFormat == "" { @@ -2510,8 +2511,8 @@ func vmGetDiskDeviceObjects( fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string) size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int) diskInterface, _ := block[mkResourceVirtualEnvironmentVMDiskInterface].(string) - ioThread := types.CustomBool(block[mkResourceVirtualEnvironmentVMDiskIOThread].(bool)) - ssd := types.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool)) + ioThread := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskIOThread].(bool)) + ssd := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool)) discard := block[mkResourceVirtualEnvironmentVMDiskDiscard].(string) speedBlock, err := getSchemaBlock( @@ -2601,12 +2602,12 @@ func vmGetHostPCIDeviceObjects(d *schema.ResourceData) vms.CustomPCIDevices { ids, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceID].(string) mdev, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceMDev].(string) - pcie := types.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDevicePCIE].(bool)) - rombar := types.CustomBool( + pcie := types2.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDevicePCIE].(bool)) + rombar := types2.CustomBool( block[mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR].(bool), ) romfile, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile].(string) - xvga := types.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA].(bool)) + xvga := types2.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA].(bool)) device := vms.CustomPCIDevice{ DeviceIDs: strings.Split(ids, ";"), @@ -2641,7 +2642,7 @@ func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices bridge := block[mkResourceVirtualEnvironmentVMNetworkDeviceBridge].(string) enabled := block[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled].(bool) - firewall := types.CustomBool(block[mkResourceVirtualEnvironmentVMNetworkDeviceFirewall].(bool)) + firewall := types2.CustomBool(block[mkResourceVirtualEnvironmentVMNetworkDeviceFirewall].(bool)) macAddress := block[mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress].(string) model := block[mkResourceVirtualEnvironmentVMNetworkDeviceModel].(string) rateLimit := block[mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit].(float64) @@ -2757,7 +2758,7 @@ func vmGetVGADeviceObject(d *schema.ResourceData) (*vms.CustomVGADevice, error) return nil, err } - vgaEnabled := types.CustomBool(vgaBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool)) + vgaEnabled := types2.CustomBool(vgaBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool)) vgaMemory := vgaBlock[mkResourceVirtualEnvironmentVMVGAMemory].(int) vgaType := vgaBlock[mkResourceVirtualEnvironmentVMVGAType].(string) @@ -4008,7 +4009,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D // Prepare the new primitive configuration values. if d.HasChange(mkResourceVirtualEnvironmentVMACPI) { - acpi := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)) + acpi := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)) updateBody.ACPI = &acpi rebootRequired = true } @@ -4031,7 +4032,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D } if d.HasChange(mkResourceVirtualEnvironmentVMOnBoot) { - startOnBoot := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool)) + startOnBoot := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool)) updateBody.StartOnBoot = &startOnBoot } @@ -4061,12 +4062,12 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D } if d.HasChange(mkResourceVirtualEnvironmentVMTabletDevice) { - tabletDevice := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool)) + tabletDevice := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool)) updateBody.TabletDeviceEnabled = &tabletDevice rebootRequired = true } - template := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)) + template := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)) if d.HasChange(mkResourceVirtualEnvironmentVMTemplate) { updateBody.Template = &template @@ -4086,10 +4087,10 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D return diag.FromErr(err) } - agentEnabled := types.CustomBool( + agentEnabled := types2.CustomBool( agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool), ) - agentTrim := types.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool)) + agentTrim := types2.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool)) agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string) updateBody.Agent = &vms.CustomAgent{ @@ -4436,7 +4437,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D return diag.FromErr(e) } } else { - forceStop := types.CustomBool(true) + forceStop := types2.CustomBool(true) shutdownTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutShutdownVM).(int) e = vmAPI.ShutdownVM(ctx, &vms.ShutdownRequestBody{ @@ -4518,7 +4519,7 @@ func vmUpdateDiskLocationAndSize( } if *oldDisk.ID != *diskNewEntries[prefix][oldKey].ID { - deleteOriginalDisk := types.CustomBool(true) + deleteOriginalDisk := types2.CustomBool(true) diskMoveBodies = append( diskMoveBodies, @@ -4546,7 +4547,7 @@ func vmUpdateDiskLocationAndSize( } if shutdownForDisksRequired && !template { - forceStop := types.CustomBool(true) + forceStop := types2.CustomBool(true) shutdownTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutShutdownVM).(int) err = vmAPI.ShutdownVM( @@ -4630,7 +4631,7 @@ func vmDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D } if status.Status != "stopped" { - forceStop := types.CustomBool(true) + forceStop := types2.CustomBool(true) shutdownTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutShutdownVM).(int) err = vmAPI.ShutdownVM( diff --git a/tools/go.mod b/tools/go.mod index 6dccc200..95e820db 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/golangci/golangci-lint v1.53.3 github.com/goreleaser/goreleaser v1.18.2 + github.com/hashicorp/terraform-plugin-docs v0.15.0 ) require ( @@ -46,15 +47,18 @@ require ( github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.1.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f // indirect - github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/acomagu/bufpipe v1.0.4 // indirect github.com/alessio/shellescape v1.4.1 // indirect github.com/alexkohler/nakedret/v2 v2.0.2 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/ashanbrown/forbidigo v1.5.3 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect @@ -85,6 +89,7 @@ require ( github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/speakeasy v0.1.0 // indirect github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect @@ -106,6 +111,7 @@ require ( github.com/charmbracelet/lipgloss v0.7.1 // indirect github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08 // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect @@ -127,7 +133,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elliotchance/orderedmap/v2 v2.2.0 // indirect - github.com/emirpasic/gods v1.12.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/esimonov/ifshort v1.0.4 // indirect github.com/ettle/strcase v0.1.1 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -138,8 +144,8 @@ require ( github.com/fzipp/gocyclo v0.6.0 // indirect github.com/go-critic/go-critic v0.8.1 // indirect github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.3.1 // indirect - github.com/go-git/go-git/v5 v5.4.2 // indirect + github.com/go-git/go-billy/v5 v5.4.1 // indirect + github.com/go-git/go-git/v5 v5.6.1 // indirect github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/errors v0.20.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -195,11 +201,16 @@ require ( github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // 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-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/hc-install v0.5.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/terraform-exec v0.18.1 // indirect + github.com/hashicorp/terraform-json v0.16.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect @@ -213,7 +224,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/julz/importas v0.1.0 // indirect - github.com/kevinburke/ssh_config v1.1.0 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kisielk/errcheck v1.6.3 // indirect github.com/kisielk/gotool v1.0.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect @@ -241,6 +252,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect github.com/mgechev/revive v1.3.2 // indirect + github.com/mitchellh/cli v1.1.5 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -263,10 +275,12 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polyfloyd/go-errorlint v1.4.2 // indirect + github.com/posener/complete v1.2.3 // indirect github.com/prometheus/client_golang v1.15.1 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect @@ -276,6 +290,7 @@ require ( github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect github.com/rivo/uniseg v0.4.2 // indirect + github.com/russross/blackfriday v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryancurrah/gomodguard v1.3.0 // indirect github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect @@ -286,6 +301,7 @@ require ( github.com/securego/gosec/v2 v2.16.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect + github.com/shopspring/decimal v1.3.1 // indirect github.com/sigstore/cosign/v2 v2.0.0 // indirect github.com/sigstore/rekor v1.2.0 // indirect github.com/sigstore/sigstore v1.6.4 // indirect @@ -293,6 +309,7 @@ require ( github.com/sivchari/containedctx v1.0.3 // indirect github.com/sivchari/nosnakecase v1.7.0 // indirect github.com/sivchari/tenv v1.7.1 // indirect + github.com/skeema/knownhosts v1.1.0 // indirect github.com/slack-go/slack v0.12.2 // indirect github.com/sonatard/noctx v0.0.2 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect @@ -325,11 +342,12 @@ require ( github.com/vbatts/tar-split v0.11.2 // indirect github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 // indirect github.com/xanzy/go-gitlab v0.83.0 // indirect - github.com/xanzy/ssh-agent v0.3.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xen0n/gosmopolitan v1.2.1 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect github.com/ykadowak/zerologlint v0.1.2 // indirect + github.com/zclconf/go-cty v1.13.2 // indirect gitlab.com/bosi/decorder v0.2.3 // indirect gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect diff --git a/tools/go.sum b/tools/go.sum index f9d2ad5a..235ea1db 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -535,6 +535,9 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= @@ -543,8 +546,8 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= @@ -566,9 +569,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard/v2 v2.1.0 h1:aQl70G173h/GZYhWf36aE5H0KaujXfVMnn/f1kSDVYY= github.com/OpenPeeDeeP/depguard/v2 v2.1.0/go.mod h1:PUBgk35fX4i7JDmwzlJwJ+GMe6NfO1723wmJMgPThNQ= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 h1:XcF0cTDJeiuZ5NU8w7WUDge0HRwwNRmxj/GGk6KSA6g= -github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko= github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs= @@ -580,8 +582,8 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= +github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -600,9 +602,11 @@ github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pO github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +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/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -610,6 +614,7 @@ github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4 github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= 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= @@ -726,6 +731,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= @@ -755,6 +761,7 @@ github.com/butuzov/ireturn v0.2.0 h1:kCHi+YzC150GE98WFuZQu9yrTn6GEydO2AuPLbTgnO4 github.com/butuzov/ireturn v0.2.0/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/caarlos0/ctrlc v1.2.0 h1:AtbThhmbeYx1WW3WXdWrd94EHKi+0NPRGS4/4pzrjwk= github.com/caarlos0/ctrlc v1.2.0/go.mod h1:n3gDlSjsXZ7rbD9/RprIR040b7oaLfNStikPd4gFago= github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= @@ -816,6 +823,9 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 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= @@ -1049,8 +1059,8 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +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= @@ -1091,7 +1101,6 @@ github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y= github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -1118,19 +1127,19 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-critic/go-critic v0.8.1 h1:16omCF1gN3gTzt4j4J6fKI/HnRojhEp+Eks6EuKw3vw= github.com/go-critic/go-critic v0.8.1/go.mod h1:kpzXl09SIJX1cr9TB/g/sAG+eFEl7ZS9f9cqvZtyNl0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= +github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= +github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= +github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8= 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= @@ -1535,6 +1544,8 @@ github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FK 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= @@ -1566,6 +1577,8 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv 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.2/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.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -1574,6 +1587,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ 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 v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hc-install v0.5.2 h1:SfwMFnEXVVirpwkDuSF5kymUOhrUxrTq3udEseZdOD0= +github.com/hashicorp/hc-install v0.5.2/go.mod h1:9QISwe6newMWIfEiXpzuu1k9HAGtQYgnSH8H9T8wmoI= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -1591,6 +1606,12 @@ github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKEN github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hashicorp/terraform-exec v0.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= +github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= +github.com/hashicorp/terraform-json v0.16.0 h1:UKkeWRWb23do5LNAFlh/K3N0ymn1qTOO8c+85Albo3s= +github.com/hashicorp/terraform-json v0.16.0/go.mod h1:v0Ufk9jJnk6tcIZvScHvetlKfiNTC+WS21mnXIlc0B0= +github.com/hashicorp/terraform-plugin-docs v0.15.0 h1:W5xYB5kCUBqO7lyjE2UMmUBh95c0aAf4jwO0Xuuw2Ec= +github.com/hashicorp/terraform-plugin-docs v0.15.0/go.mod h1:K5Taof1Y7sL4dw6Ie0qMFyQnHN0W+RSVMD0iIyFDFJc= github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME= github.com/hetznercloud/hcloud-go v1.39.0/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -1598,6 +1619,7 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/honeycombio/beeline-go v1.10.0 h1:cUDe555oqvw8oD76BQJ8alk7FP0JZ/M/zXpNvOEDLDc= github.com/honeycombio/libhoney-go v1.16.0 h1:kPpqoz6vbOzgp7jC6SR7SkNj7rua7rgxvznI6M3KdHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= @@ -1615,6 +1637,7 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 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.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -1713,9 +1736,8 @@ github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSX github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o= -github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -1865,6 +1887,9 @@ github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLT github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= +github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= +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.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1884,8 +1909,10 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +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/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -2052,6 +2079,8 @@ github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= @@ -2069,6 +2098,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/polyfloyd/go-errorlint v1.4.2 h1:CU+O4181IxFDdPH6t/HT7IiDj1I7zxNi1RIUxYwn8d0= github.com/polyfloyd/go-errorlint v1.4.2/go.mod h1:k6fU/+fQe38ednoZS51T7gSIGQW1y94d6TkSr35OzH8= 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/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= @@ -2163,6 +2193,8 @@ github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -2201,6 +2233,8 @@ github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAx github.com/shoenig/test v0.6.0/go.mod h1:xYtyGBC5Q3kzCNyJg/SjgNpfAa2kvmgA0i5+lQso8x0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 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/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= @@ -2229,6 +2263,8 @@ github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= +github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= +github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -2386,9 +2422,8 @@ github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38 github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1/go.mod h1:nmuySobZb4kFgFy6BptpXp/BBw+xFSyvVPP6auoJB4k= github.com/xanzy/go-gitlab v0.83.0 h1:37p0MpTPNbsTMKX/JnmJtY8Ch1sFiJzVF342+RvZEGw= github.com/xanzy/go-gitlab v0.83.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= -github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= -github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= +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/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -2423,6 +2458,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0= +github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= @@ -2546,11 +2583,11 @@ go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= gocloud.dev v0.29.0 h1:fBy0jwJSmxs0IjT0fE32MO+Mj+307VZQwyHaTyFZbC4= gocloud.dev v0.29.0/go.mod h1:E3dAjji80g+lIkq4CQeF/BTWqv1CBeTftmOb+gpyapQ= +golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/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-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -2563,8 +2600,10 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -2584,8 +2623,10 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= @@ -2699,7 +2740,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v 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-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= 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-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= @@ -2727,6 +2767,7 @@ golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220921155015-db77216a4ee9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -2738,6 +2779,7 @@ golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmL golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2890,7 +2932,6 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/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= @@ -2940,6 +2981,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2954,6 +2996,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= @@ -3436,6 +3479,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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.0/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 v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= diff --git a/tools/tools.go b/tools/tools.go index 1ccae15d..f147c932 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -1,6 +1,12 @@ //go:build tools // +build tools +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + package tools // Manage tool dependencies via go.mod. @@ -10,4 +16,5 @@ package tools import ( _ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/goreleaser/goreleaser" + _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" ) diff --git a/utils/env.go b/utils/env.go new file mode 100644 index 00000000..43c36622 --- /dev/null +++ b/utils/env.go @@ -0,0 +1,34 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package utils + +import "os" + +// GetAnyStringEnv returns the first non-empty string value from the environment variables. +func GetAnyStringEnv(ks ...string) string { + for _, k := range ks { + if v := os.Getenv(k); v != "" { + return v + } + } + + return "" +} + +// GetAnyBoolEnv returns the first non-empty boolean value from the environment variables. +func GetAnyBoolEnv(ks ...string) bool { + val := "" + + for _, k := range ks { + if v := os.Getenv(k); v != "" { + val = v + break + } + } + + return val == "true" || val == "1" +}