forked from mirrors/terraform-provider-binarylane
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 725fa71248 | |||
| 6970691546 | |||
| d8ca224107 | |||
| d168435ed6 | |||
| 85f65c9391 | |||
| 262bf0a571 | |||
| 77271b0efd | |||
| 34bfbcfc2b | |||
| 50a112c60c | |||
| c09c34f780 | |||
| a0da966714 | |||
| 14a275e06f | |||
| f7c0e23fad | |||
| 9a58a8112c | |||
| 08a37caf51 | |||
| 709fb3ce28 | |||
| 5313e1c7ab | |||
| ce349f53f0 | |||
| 9d3d3e9af3 | |||
| a0a227c88c | |||
| f8edcd0e06 | |||
| 04552e0fcf | |||
| 66fcb6f85e | |||
| 806bdebaf8 | |||
| dd06380c63 | |||
| 322a8fda80 | |||
| a50daac8e9 | |||
| 424828123d | |||
| 4c50298eca | |||
| cbef2be0fe |
@@ -6,7 +6,7 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- "v*"
|
||||
|
||||
# Releases need permissions to read and write the repository contents.
|
||||
# GitHub considers creating releases and uploading assets as writing contents.
|
||||
@@ -23,25 +23,30 @@ jobs:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
# Allow goreleaser to access older tag information.
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
|
||||
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
|
||||
id: import_gpg
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
|
||||
with:
|
||||
args: release --clean
|
||||
env:
|
||||
# GitHub sets the GITHUB_TOKEN secret automatically.
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
|
||||
- name: Publish Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: gh release edit "${{ github.ref }}" --draft=false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -23,11 +23,11 @@ jobs:
|
||||
group: test
|
||||
cancel-in-progress: true # would be nice if GH offered cancel-pending instead, https://github.com/orgs/community/discussions/63136
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- uses: hashicorp/setup-terraform@v3
|
||||
- uses: hashicorp/setup-terraform@v4
|
||||
with:
|
||||
terraform_version: '1.5.*'
|
||||
terraform_wrapper: false
|
||||
@@ -54,8 +54,8 @@ jobs:
|
||||
# name: Unit Tests
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - uses: actions/setup-go@v5
|
||||
# - uses: actions/checkout@v5
|
||||
# - uses: actions/setup-go@v6
|
||||
# with:
|
||||
# go-version-file: 'go.mod'
|
||||
# - run: go test -v -cover ./...
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Before starting
|
||||
|
||||
Run `go generate` to pull in the latest API schema before making changes.
|
||||
|
||||
## Code style
|
||||
|
||||
- Follow patterns in existing resources, especially `server_resource.go` and `server_data_source.go`.
|
||||
- New server attributes that map to a post-creation API action (e.g. `change_*`) follow the `source_and_destination_check` / `separate_private_network_interface` pattern: read the planned value before the first state save, call the action after creation if the value differs from the server default, and handle updates in the `Update` method.
|
||||
- Run `go fmt ./...` before finishing — the CI will fail without it.
|
||||
- Don't add `Required: false` when `Optional: true` is already set; it is redundant.
|
||||
|
||||
## Testing
|
||||
|
||||
- Tests live in `internal/provider/*_test.go`.
|
||||
- A valid `BINARYLANE_API_TOKEN` is required; tests create real servers that cost money — be conscientious.
|
||||
- **Always run the sweeper after tests**, regardless of success or failure:
|
||||
```sh
|
||||
go test -v ./internal/provider/... -sweep=all
|
||||
```
|
||||
- Run a specific test with:
|
||||
```sh
|
||||
go test -v -run TestServerResource ./internal/provider/...
|
||||
```
|
||||
- New attributes on the server resource should be exercised inside an existing test step where possible (e.g. the first step of `TestServerResource` already provisions a VPC server).
|
||||
|
||||
## Workflow
|
||||
|
||||
1. `go generate` — fetch latest OpenAPI spec and regenerate code + docs.
|
||||
2. Make changes.
|
||||
3. `go fmt ./...` — format code.
|
||||
4. `go build ./...` — verify it compiles.
|
||||
5. `go vet ./...` — catch common mistakes.
|
||||
6. Run relevant tests, then sweep.
|
||||
7. `go generate` again to regenerate docs if schema or descriptions changed.
|
||||
@@ -20,9 +20,9 @@ Planned features:
|
||||
- [x] Cloud Init
|
||||
- [x] Updating server properties
|
||||
- [x] "Advanced Features"
|
||||
- [x] IPV6
|
||||
- [ ] Customised Backups
|
||||
- [ ] Software
|
||||
- [ ] IPV6
|
||||
- [ ] Alerts
|
||||
- [x] SSH Keys
|
||||
- [x] Virtual Private Cloud
|
||||
@@ -57,6 +57,7 @@ jq '[ .regions[] | .slug ] | sort' tmp/regions.json
|
||||
|
||||
```json
|
||||
[
|
||||
"adl",
|
||||
"bne",
|
||||
"mel",
|
||||
"per",
|
||||
@@ -80,21 +81,24 @@ jq '[ .images[] | .slug ] | sort' tmp/images.json
|
||||
|
||||
```json
|
||||
[
|
||||
"alma-10",
|
||||
"alma-8",
|
||||
"alma-9",
|
||||
"byo-os",
|
||||
"byo-os-uefi",
|
||||
"byo-os-virtio-disabled",
|
||||
"cpanel-plus-whm",
|
||||
"debian-11",
|
||||
"debian-12",
|
||||
"rocky-8",
|
||||
"debian-13",
|
||||
"rocky-10",
|
||||
"rocky-9",
|
||||
"ubuntu-20.04-neon-desktop",
|
||||
"ubuntu-20.04.6",
|
||||
"ubuntu-22.04",
|
||||
"ubuntu-22.04-desktop",
|
||||
"ubuntu-24.04",
|
||||
"windows-2012-r2",
|
||||
"ubuntu-24.04-neon-desktop",
|
||||
"ubuntu-26.04",
|
||||
"windows-2016",
|
||||
"windows-2016-sql-2016-web",
|
||||
"windows-2019",
|
||||
@@ -102,7 +106,8 @@ jq '[ .images[] | .slug ] | sort' tmp/images.json
|
||||
"windows-2019-sql-2017-web",
|
||||
"windows-2022",
|
||||
"windows-2022-sql-2019-std",
|
||||
"windows-2022-sql-2019-web"
|
||||
"windows-2022-sql-2019-web",
|
||||
"windows-2025"
|
||||
]
|
||||
```
|
||||
|
||||
@@ -125,14 +130,16 @@ jq '[ .sizes[] | .slug ] | sort' tmp/sizes.json
|
||||
"cpu-4thr",
|
||||
"cpu-6thr",
|
||||
"cpu-8thr",
|
||||
"ded-3900x-1600gb",
|
||||
"ded-7900x-800gb",
|
||||
"ded-e2136-400gb",
|
||||
"ded-e2136-800gb",
|
||||
"ded-e2288g-400gb",
|
||||
"ded-e2288g-800gb",
|
||||
"hdd-1000gb",
|
||||
"hdd-2000gb",
|
||||
"hdd-4000gb",
|
||||
"hdd-500gb",
|
||||
"hdd-6000gb",
|
||||
"hdd-8000gb",
|
||||
"std-1vcpu",
|
||||
"std-2vcpu",
|
||||
"std-4vcpu",
|
||||
|
||||
@@ -29,16 +29,22 @@ data "binarylane_load_balancer" "example" {
|
||||
|
||||
- `forwarding_rules` (Attributes List) The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule. (see [below for nested schema](#nestedatt--forwarding_rules))
|
||||
- `health_check` (Object) The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules. (see [below for nested schema](#nestedatt--health_check))
|
||||
- `ip` (String) The IPv4 address of the load balancer.
|
||||
- `name` (String) The hostname of the load balancer.
|
||||
- `region` (String) Leave null to create an anycast load balancer.
|
||||
- `server_ids` (List of Number) A list of server IDs to assign to this load balancer.
|
||||
- `server_ids` (Set of Number) A list of server IDs to assign to this load balancer.
|
||||
|
||||
<a id="nestedatt--forwarding_rules"></a>
|
||||
### Nested Schema for `forwarding_rules`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
|
||||
- `entry_protocol` (String) The protocol that traffic must match for this load balancer to forward traffic according to this rule.
|
||||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| http | The load balancer will forward HTTP traffic that matches this rule. |
|
||||
| https | The load balancer will forward HTTPS traffic that matches this rule. |
|
||||
|
||||
|
||||
<a id="nestedatt--health_check"></a>
|
||||
|
||||
@@ -29,11 +29,13 @@ data "binarylane_server" "example" {
|
||||
|
||||
- `advanced_features` (Object) (see [below for nested schema](#nestedatt--advanced_features))
|
||||
- `backups` (Boolean) If `true` this will enable two daily backups for the server. By default, backups are disabled.
|
||||
- `disk` (Number) The total storage in GB for this server. Leave null to accept the default for the size Valid values:
|
||||
- `disk` (Number) The total storage in GB for this server. Leave null to accept the default for the size. Valid values:
|
||||
- must be a multiple of 5
|
||||
- \> 60 GB must be a multiple of 10
|
||||
- \> 200 GB must be a multiple of 100
|
||||
- `disks` (Attributes List) The disks attached to this server. Leave null to let Binary Lane manage the server's disks. If specified, every disk must be listed, including the primary: exactly one entry must have `primary = true`, and its `id` and `description` are assigned by Binary Lane. (see [below for nested schema](#nestedatt--disks))
|
||||
- `image` (String) The slug of the selected operating system, such as `debian-12`. You can fetch a full list of images from the BinaryLane API.
|
||||
- `ipv6` (Boolean) If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled.
|
||||
- `memory` (Number) The total memory in MB for this server. Leave null to accept the default size. Valid values:
|
||||
- must be a multiple of 128
|
||||
- \> 2048 MB must be a multiple of 1024
|
||||
@@ -41,15 +43,19 @@ data "binarylane_server" "example" {
|
||||
- \> 24576 MB must be a multiple of 4096
|
||||
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
|
||||
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
|
||||
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
|
||||
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to verified accounts.
|
||||
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
|
||||
- `private_ipv6_addresses` (List of String) The private IPv6 addresses assigned to the server.
|
||||
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
|
||||
- `public_ipv6_addresses` (List of String) The public IPv6 addresses assigned to the server.
|
||||
- `region` (String) The slug of the selected region.
|
||||
- `separate_private_network_interface` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled, a separate private network interface is provided for the server's VPC traffic.
|
||||
- `size` (String) The slug of the selected size.
|
||||
- `source_and_destination_check` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled (which is `true` by default), your server will only be able to send or receive packets that are directly addressed to one of the IP addresses associated with the Cloud Server. Generally, this is desirable behaviour because it prevents IP conflicts and other hard-to-diagnose networking faults due to incorrect network configuration. When `source_and_destination_check` is `false`, your Cloud Server will be able to send and receive packets addressed to any server. This is typically used when you want to use your Cloud Server as a VPN endpoint, a NAT server to provide internet access, or IP forwarding.
|
||||
- `ssh_keys` (List of Number) This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.
|
||||
- `user_data` (String) A script or cloud-config YAML file to configure the server. Can only be specified if the OS image supports UserData (i.e. not Windows). See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
- `vpc_ipv4_address` (String) If provided this will be the IPv4 address for the server's private VPC network adapter. If this is unspecified, then an unused IPv4 address will be assigned. This field is only valid when `vpc_id` is provided.
|
||||
|
||||
<a id="nestedatt--advanced_features"></a>
|
||||
### Nested Schema for `advanced_features`
|
||||
@@ -66,3 +72,14 @@ Read-Only:
|
||||
- `qemu_guest_agent` (Boolean)
|
||||
- `uefi_boot` (Boolean)
|
||||
- `unset_uuid` (Boolean)
|
||||
|
||||
|
||||
<a id="nestedatt--disks"></a>
|
||||
### Nested Schema for `disks`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String) A label for this disk. Assigned by Binary Lane for the primary disk, so must be left null when `primary = true`. Changing this will destroy the disk and create a new one, losing any data on it. Disks that share a description are matched in the order they are listed.
|
||||
- `id` (Number) The ID of this disk.
|
||||
- `primary` (Boolean) If `true` this is the primary disk, which the operating system is installed on. Exactly one disk must be the primary.
|
||||
- `size_gigabytes` (Number) The size of the disk in GB. The sum across all disks cannot exceed `disk`, and any remainder is left unallocated.
|
||||
|
||||
@@ -74,7 +74,7 @@ Read-Only:
|
||||
- `offsite_backups_cost_per_gigabyte` (Number) The additional cost per GB of storage for enabling offsite backups. See the API support document for how to calculate the final cost of backups based on the options selected.
|
||||
- `restricted_disk_values` (List of Number) If this is null the normal valid values in the documentation for SizeOptionsRequest are used, otherwise only these values (in GB) are permitted.
|
||||
- `transfer_cost_per_additional_gigabyte` (Number) The additional cost per GB per month for additional included transfer.
|
||||
- `transfer_max` (Number) The maximum transfer in TB permitted for this size.
|
||||
- `transfer_max` (Number) The maximum transfer in TB permitted for this size. If this is the same as `transfer` no additional transfer is supported.
|
||||
- `weekly_backups` (Number) The number of weekly backups included in the base size cost.
|
||||
|
||||
<a id="nestedatt--sizes--options--offsite_backup_frequency_cost"></a>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane Provider"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
---
|
||||
|
||||
@@ -40,19 +40,25 @@ resource "binarylane_load_balancer" "example" {
|
||||
- `forwarding_rules` (Attributes List) The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule. (see [below for nested schema](#nestedatt--forwarding_rules))
|
||||
- `health_check` (Attributes) The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules. (see [below for nested schema](#nestedatt--health_check))
|
||||
- `region` (String) Leave null to create an anycast load balancer.
|
||||
- `server_ids` (List of Number) A list of server IDs to assign to this load balancer.
|
||||
- `server_ids` (Set of Number) A list of server IDs to assign to this load balancer.
|
||||
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (Number) The ID of the load balancer to fetch.
|
||||
- `ip` (String) The IPv4 address of the load balancer.
|
||||
|
||||
<a id="nestedatt--forwarding_rules"></a>
|
||||
### Nested Schema for `forwarding_rules`
|
||||
|
||||
Required:
|
||||
|
||||
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
|
||||
- `entry_protocol` (String) The protocol that traffic must match for this load balancer to forward traffic according to this rule.
|
||||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| http | The load balancer will forward HTTP traffic that matches this rule. |
|
||||
| https | The load balancer will forward HTTPS traffic that matches this rule. |
|
||||
|
||||
|
||||
<a id="nestedatt--health_check"></a>
|
||||
@@ -76,11 +82,14 @@ Optional:
|
||||
Optional:
|
||||
|
||||
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_load_balancer.example "<name or id>"
|
||||
```
|
||||
|
||||
@@ -20,6 +20,14 @@ resource "binarylane_server" "example" {
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
public_ipv4_count = 1
|
||||
|
||||
# To manage multiple disks, every disk must be listed, including the primary
|
||||
# disk = 40
|
||||
# disks = [
|
||||
# { primary = true, size_gigabytes = 20 },
|
||||
# { description = "data1", size_gigabytes = 10 },
|
||||
# { description = "data2", size_gigabytes = 10 },
|
||||
# ]
|
||||
|
||||
# Accepts a cloud-init script or cloud-config YAML file to configure the server
|
||||
# See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
user_data = file("./init.sh")
|
||||
@@ -40,10 +48,12 @@ resource "binarylane_server" "example" {
|
||||
|
||||
- `advanced_features` (Attributes) (see [below for nested schema](#nestedatt--advanced_features))
|
||||
- `backups` (Boolean) If `true` this will enable two daily backups for the server. By default, backups are disabled.
|
||||
- `disk` (Number) The total storage in GB for this server. Leave null to accept the default for the size Valid values:
|
||||
- `disk` (Number) The total storage in GB for this server. Leave null to accept the default for the size. Valid values:
|
||||
- must be a multiple of 5
|
||||
- \> 60 GB must be a multiple of 10
|
||||
- \> 200 GB must be a multiple of 100
|
||||
- `disks` (Attributes List) The disks attached to this server. Leave null to let Binary Lane manage the server's disks. If specified, every disk must be listed, including the primary: exactly one entry must have `primary = true`, and its `id` and `description` are assigned by Binary Lane. (see [below for nested schema](#nestedatt--disks))
|
||||
- `ipv6` (Boolean) If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled.
|
||||
- `memory` (Number) The total memory in MB for this server. Leave null to accept the default size. Valid values:
|
||||
- must be a multiple of 128
|
||||
- \> 2048 MB must be a multiple of 1024
|
||||
@@ -51,12 +61,14 @@ resource "binarylane_server" "example" {
|
||||
- \> 24576 MB must be a multiple of 4096
|
||||
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
|
||||
- `password` (String, Sensitive) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions. If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
|
||||
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
|
||||
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to verified accounts.
|
||||
- `separate_private_network_interface` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled, a separate private network interface is provided for the server's VPC traffic.
|
||||
- `source_and_destination_check` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled (which is `true` by default), your server will only be able to send or receive packets that are directly addressed to one of the IP addresses associated with the Cloud Server. Generally, this is desirable behaviour because it prevents IP conflicts and other hard-to-diagnose networking faults due to incorrect network configuration. When `source_and_destination_check` is `false`, your Cloud Server will be able to send and receive packets addressed to any server. This is typically used when you want to use your Cloud Server as a VPN endpoint, a NAT server to provide internet access, or IP forwarding.
|
||||
- `ssh_keys` (List of Number) This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.
|
||||
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
|
||||
- `user_data` (String) A script or cloud-config YAML file to configure the server. Can only be specified if the OS image supports UserData (i.e. not Windows). See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
- `vpc_ipv4_address` (String) If provided this will be the IPv4 address for the server's private VPC network adapter. If this is unspecified, then an unused IPv4 address will be assigned. This field is only valid when `vpc_id` is provided.
|
||||
|
||||
### Read-Only
|
||||
|
||||
@@ -64,7 +76,9 @@ resource "binarylane_server" "example" {
|
||||
- `password_change_supported` (Boolean) If this is true then the `password` attribute can be changed with Terraform. If this is false then the `password` attribute can only be replaced with a null/empty value, which will clear the root/administrator password allowing the password to be changed via the web console.
|
||||
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
|
||||
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
|
||||
- `private_ipv6_addresses` (List of String) The private IPv6 addresses assigned to the server.
|
||||
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
|
||||
- `public_ipv6_addresses` (List of String) The public IPv6 addresses assigned to the server.
|
||||
|
||||
<a id="nestedatt--advanced_features"></a>
|
||||
### Nested Schema for `advanced_features`
|
||||
@@ -86,6 +100,23 @@ Read-Only:
|
||||
- `uefi_boot` (Boolean) When this option is enabled the Cloud Server will use UEFI instead of legacy PC BIOS.
|
||||
|
||||
|
||||
<a id="nestedatt--disks"></a>
|
||||
### Nested Schema for `disks`
|
||||
|
||||
Required:
|
||||
|
||||
- `size_gigabytes` (Number) The size of the disk in GB. The sum across all disks cannot exceed `disk`, and any remainder is left unallocated.
|
||||
|
||||
Optional:
|
||||
|
||||
- `description` (String) A label for this disk. Assigned by Binary Lane for the primary disk, so must be left null when `primary = true`. Changing this will destroy the disk and create a new one, losing any data on it. Disks that share a description are matched in the order they are listed.
|
||||
- `primary` (Boolean) If `true` this is the primary disk, which the operating system is installed on. Exactly one disk must be the primary.
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `id` (Number) The ID of this disk.
|
||||
|
||||
|
||||
<a id="nestedatt--timeouts"></a>
|
||||
### Nested Schema for `timeouts`
|
||||
|
||||
@@ -98,6 +129,8 @@ Optional:
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_server.example "<server name or id>"
|
||||
```
|
||||
|
||||
@@ -71,6 +71,8 @@ Optional:
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_server_firewall_rules.example "<server name or id>"
|
||||
```
|
||||
|
||||
@@ -47,6 +47,8 @@ resource "binarylane_server" "example" {
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_ssh_key.example "<SSH Key ID or fingerprint>"
|
||||
```
|
||||
|
||||
@@ -43,6 +43,8 @@ resource "binarylane_server" "example" {
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_vpc.example "<vpc_id>"
|
||||
```
|
||||
|
||||
@@ -67,6 +67,8 @@ Optional:
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
terraform import binarylane_vpc_route_entries.example "<vpc_id>"
|
||||
```
|
||||
|
||||
@@ -3,10 +3,10 @@ bootcmd:
|
||||
# If you have additional installation steps that need to be awaited, define
|
||||
# these in the `bootcmd` section and they will also be awaited.
|
||||
#
|
||||
- [cloud-init-per, once, 'example-installing', sh, -c, 'sleep 60'] # pretend this is a slow installation step
|
||||
- [cloud-init-per, instance, 'example-installing', sh, -c, 'sleep 60'] # pretend this is a slow installation step
|
||||
- [
|
||||
cloud-init-per,
|
||||
once,
|
||||
instance,
|
||||
'example-done',
|
||||
sh,
|
||||
-c,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# Ignore SSH keys
|
||||
.id_ed25519*
|
||||
.kube
|
||||
@@ -5,6 +5,14 @@ resource "binarylane_server" "example" {
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
public_ipv4_count = 1
|
||||
|
||||
# To manage multiple disks, every disk must be listed, including the primary
|
||||
# disk = 40
|
||||
# disks = [
|
||||
# { primary = true, size_gigabytes = 20 },
|
||||
# { description = "data1", size_gigabytes = 10 },
|
||||
# { description = "data2", size_gigabytes = 10 },
|
||||
# ]
|
||||
|
||||
# Accepts a cloud-init script or cloud-config YAML file to configure the server
|
||||
# See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
user_data = file("./init.sh")
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
module terraform-provider-binarylane
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.1
|
||||
go 1.25.8
|
||||
|
||||
require (
|
||||
github.com/deepmap/oapi-codegen v1.16.3
|
||||
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.1
|
||||
github.com/hashicorp/terraform-plugin-codegen-openapi v0.3.0
|
||||
github.com/hashicorp/terraform-plugin-docs v0.21.0
|
||||
github.com/hashicorp/terraform-plugin-framework v1.15.0
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.28.0
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.2
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1
|
||||
github.com/oapi-codegen/runtime v1.1.2
|
||||
github.com/hashicorp/terraform-plugin-docs v0.24.0
|
||||
github.com/hashicorp/terraform-plugin-framework v1.19.0
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.19.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.31.0
|
||||
github.com/hashicorp/terraform-plugin-log v0.11.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.16.0
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.6.0
|
||||
github.com/oapi-codegen/runtime v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -25,21 +23,21 @@ require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/agext/levenshtein v1.2.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fatih/color v1.19.0 // indirect
|
||||
github.com/frankban/quicktest v1.14.6 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/getkin/kin-openapi v0.131.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
@@ -52,30 +50,30 @@ require (
|
||||
github.com/hashicorp/go-cty v1.5.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.3 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-plugin v1.8.0 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.9.2 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 // indirect
|
||||
github.com/hashicorp/go-version v1.9.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.9.4 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.23.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.25.0 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.25.1 // indirect
|
||||
github.com/hashicorp/terraform-json v0.27.2 // indirect
|
||||
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.3.0 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.1 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.5.0 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.2.1 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
@@ -88,32 +86,34 @@ require (
|
||||
github.com/posener/complete v1.2.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
|
||||
github.com/speakeasy-api/jsonpath v0.6.0 // indirect
|
||||
github.com/speakeasy-api/openapi-overlay v0.10.2 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/woodsbury/decimal128 v1.3.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.7 // indirect
|
||||
github.com/yuin/goldmark-meta v1.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.16.3 // indirect
|
||||
github.com/zclconf/go-cty v1.18.1 // indirect
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/crypto v0.54.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.org/x/mod v0.37.0 // indirect
|
||||
golang.org/x/net v0.57.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
golang.org/x/tools v0.47.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 // indirect
|
||||
google.golang.org/grpc v1.83.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -13,11 +13,11 @@ github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
|
||||
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
|
||||
@@ -30,23 +30,26 @@ github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xW
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deepmap/oapi-codegen v1.16.3 h1:GT9G86SbQtT1r8ZB+4Cybi9VGdu1P5ieNvNdEoCSbrA=
|
||||
github.com/deepmap/oapi-codegen v1.16.3/go.mod h1:JD6ErqeX0nYnhdciLc61Konj3NBASREMlkHOgHn8WAM=
|
||||
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
|
||||
@@ -55,24 +58,24 @@ github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5ql
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE=
|
||||
github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
|
||||
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
|
||||
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
|
||||
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
|
||||
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
|
||||
github.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM=
|
||||
github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
@@ -124,51 +127,51 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
|
||||
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-plugin v1.8.0 h1:ie8S6RRY8RvB2usYZv+AAZ/wBvx2AU5p5QeP5j/FORs=
|
||||
github.com/hashicorp/go-plugin v1.8.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hc-install v0.9.2 h1:v80EtNX4fCVHqzL9Lg/2xkp62bbvQMnvPQ0G+OmtO24=
|
||||
github.com/hashicorp/hc-install v0.9.2/go.mod h1:XUqBQNnuT4RsxoxiM9ZaUk0NX8hi2h+Lb6/c0OZnC/I=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
|
||||
github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hc-install v0.9.4 h1:KKWOpUG0EqIV63Qk2GGFrZ0s275NVs5lKf9N5vjBNoc=
|
||||
github.com/hashicorp/hc-install v0.9.4/go.mod h1:4LRYeEN2bMIFfIv57ldMWt9awfuZhvpbRt0vWmv51WU=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
|
||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/terraform-exec v0.23.0 h1:MUiBM1s0CNlRFsCLJuM5wXZrzA3MnPYEsiXmzATMW/I=
|
||||
github.com/hashicorp/terraform-exec v0.23.0/go.mod h1:mA+qnx1R8eePycfwKkCRk3Wy65mwInvlpAeOwmA7vlY=
|
||||
github.com/hashicorp/terraform-json v0.25.0 h1:rmNqc/CIfcWawGiwXmRuiXJKEiJu1ntGoxseG1hLhoQ=
|
||||
github.com/hashicorp/terraform-json v0.25.0/go.mod h1:sMKS8fiRDX4rVlR6EJUMudg1WcanxCMoWwTLkgZP/vc=
|
||||
github.com/hashicorp/terraform-exec v0.25.1 h1:PRutYRGM8pixV3B8812NYoBK5O+yuf3qcB/70KFKGiU=
|
||||
github.com/hashicorp/terraform-exec v0.25.1/go.mod h1:+izOYrs9sKMQK4OYvGDnrSSJHY/pm4e4eXFqSL2Q5mA=
|
||||
github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU=
|
||||
github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE=
|
||||
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.1 h1:eaI/3dsu2T5QAXbA+7N+B+UBj20GdtYnsRuYypKh3S4=
|
||||
github.com/hashicorp/terraform-plugin-codegen-framework v0.4.1/go.mod h1:kpYM23L7NtcfaQdWAN0QFkV/lU0w16qJ2ddAPCI4zAg=
|
||||
github.com/hashicorp/terraform-plugin-codegen-openapi v0.3.0 h1:IKpc337XKk50QyQPSxLrHwdqSo1E2XqCMxFkWsZcTvc=
|
||||
github.com/hashicorp/terraform-plugin-codegen-openapi v0.3.0/go.mod h1:tT6wl80h7nsMBw+1yZRgJXi+Ys85PUai11weDqysvp4=
|
||||
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 h1:91dQG1A/DxP6vRz9GiytDTrZTXDbhHPvmpYnAyWA/Vw=
|
||||
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0/go.mod h1:fywrEKpordQypmAjz/HIfm2LuNVmyJ6KDe8XT9GdJxQ=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.21.0 h1:yoyA/Y719z9WdFJAhpUkI1jRbKP/nteVNBaI3hW7iQ8=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.21.0/go.mod h1:J4Wott1J2XBKZPp/NkQv7LMShJYOcrqhQ2myXBcu64s=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.15.0 h1:LQ2rsOfmDLxcn5EeIwdXFtr03FVsNktbbBci8cOKdb4=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.15.0/go.mod h1:hxrNI/GY32KPISpWqlCoTLM9JZsGH3CyYlir09bD/fI=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0 h1:I/N0g/eLZ1ZkLZXUQ0oRSXa8YG/EF0CEuQP1wXdrzKw=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0/go.mod h1:t339KhmxnaF4SzdpxmqW8HnQBHVGYazwtfxU0qCs4eE=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0 h1:OQnlOt98ua//rCw+QhBbSqfW3QbwtVrcdWeQN5gI3Hw=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0/go.mod h1:lZvZvagw5hsJwuY7mAY6KUz45/U6fiDR0CzQAwWD0CA=
|
||||
github.com/hashicorp/terraform-plugin-go v0.28.0 h1:zJmu2UDwhVN0J+J20RE5huiF3XXlTYVIleaevHZgKPA=
|
||||
github.com/hashicorp/terraform-plugin-go v0.28.0/go.mod h1:FDa2Bb3uumkTGSkTFpWSOwWJDwA7bf3vdP3ltLDTH6o=
|
||||
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-sdk/v2 v2.37.0 h1:NFPMacTrY/IdcIcnUB+7hsore1ZaRWU9cnB6jFoBnIM=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0/go.mod h1:QYmYnLfsosrxjCnGY1p9c7Zj6n9thnEE+7RObeYs3fA=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.2 h1:mSotG4Odl020vRjIenA3rggwo6Kg6XCKIwtRhYgp+/M=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.2/go.mod h1:WHQ9FDdiLoneey2/QHpGM/6SAYf4A7AZazVg7230pLE=
|
||||
github.com/hashicorp/terraform-registry-address v0.3.0 h1:HMpK3nqaGFPS9VmgRXrJL/dzHNdheGVKk5k7VlFxzCo=
|
||||
github.com/hashicorp/terraform-registry-address v0.3.0/go.mod h1:jRGCMiLaY9zii3GLC7hqpSnwhfnCN5yzvY0hh4iCGbM=
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
|
||||
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.24.0 h1:YNZYd+8cpYclQyXbl1EEngbld8w7/LPOm99GD5nikIU=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.24.0/go.mod h1:YLg+7LEwVmRuJc0EuCw0SPLxuQXw5mW8iJ5ml/kvi+o=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.19.0 h1:q0bwyhxAOR3vfdgbk9iplv3MlTv/dhBHTXjQOtQDoBA=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.19.0/go.mod h1:YRXOBu0jvs7xp4AThBbX4mAzYaMJ1JgtFH//oGKxwLc=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0 h1:jblRy1PkLfPm5hb5XeMa3tezusnMRziUGqtT5epSYoI=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.7.0/go.mod h1:5jm2XK8uqrdiSRfD5O47OoxyGMCnwTcl8eoiDgSa+tc=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.19.0 h1:Zz3iGgzxe/1XBkooZCewS0nJAaCFPFPHdNJd8FgE4Ow=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.19.0/go.mod h1:GBKTNGbGVJohU03dZ7U8wHqc2zYnMUawgCN+gC0itLc=
|
||||
github.com/hashicorp/terraform-plugin-go v0.31.0 h1:0Fz2r9DQ+kNNl6bx8HRxFd1TfMKUvnrOtvJPmp3Z0q8=
|
||||
github.com/hashicorp/terraform-plugin-go v0.31.0/go.mod h1:A88bDhd/cW7FnwqxQRz3slT+QY6yzbHKc6AOTtmdeS8=
|
||||
github.com/hashicorp/terraform-plugin-log v0.11.0 h1:WjhcpZIVqP8YRe83+dIZXncwSgtu4vh27i23G33PUQY=
|
||||
github.com/hashicorp/terraform-plugin-log v0.11.0/go.mod h1:XygBz8+m5kgwTb73MMyrnUjeNQeVWECEfg+h2opMsj0=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.1 h1:2yPUd7esMOpuTaG3y1iEla1iw+tla+3ZEkkBnmOAre4=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.1/go.mod h1:sq8qsxh+PwdvTQFcd17kfCoBgQo46ADNMvCpKE7t/gY=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.16.0 h1:GB97nGnJ1hESpDrCjqZig38RodSF0gdRzxlDupLXP38=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.16.0/go.mod h1:eQPYAy9xFMV7xtIFX8Y+wJGtUB++HBl329zCF6PBMZk=
|
||||
github.com/hashicorp/terraform-registry-address v0.5.0 h1:FAlhWOLFgMvo/4f5DPhCTwRYfHYdF1DjiOtgxfGr4p0=
|
||||
github.com/hashicorp/terraform-registry-address v0.5.0/go.mod h1:wOJYCN60i/gSQGPCcGdamatjxn65EZBMFVt7c/Suzis=
|
||||
github.com/hashicorp/terraform-svchost v0.2.1 h1:ubvrTFw3Q7CsoEaX7V06PtCTKG3wu7GyyobAoN4eF3Q=
|
||||
github.com/hashicorp/terraform-svchost v0.2.1/go.mod h1:zDMheBLvNzu7Q6o9TBvPqiZToJcSuCLXjAXxBslSky4=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
@@ -181,8 +184,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
|
||||
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
|
||||
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
@@ -199,12 +202,12 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
@@ -212,8 +215,8 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
@@ -224,10 +227,12 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q=
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8=
|
||||
github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI=
|
||||
github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/oapi-codegen/nullable v1.1.0 h1:eAh8JVc5430VtYVnq00Hrbpag9PFRGWLjxR1/3KntMs=
|
||||
github.com/oapi-codegen/nullable v1.1.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY=
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.6.0 h1:4i+F2cvwBFZeplxCssNdLy3MhNzUD87mI3HnayHZkAU=
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.6.0/go.mod h1:eWHeJSohQJIINJZzzQriVynfGsnlQVh0UkN2UYYcw4Q=
|
||||
github.com/oapi-codegen/runtime v1.6.0 h1:7Xx+GlueD6nRuyKoCPzL434Jfi3BetbiJOrzCHp/VPU=
|
||||
github.com/oapi-codegen/runtime v1.6.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||
@@ -254,15 +259,16 @@ github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0V
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
@@ -271,8 +277,10 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54VRhy1aSutx0PQg=
|
||||
github.com/speakeasy-api/openapi-overlay v0.9.0/go.mod h1:f5FloQrHA7MsxYg9djzMD5h6dxrHjVVByWKh7an8TRc=
|
||||
github.com/speakeasy-api/jsonpath v0.6.0 h1:IhtFOV9EbXplhyRqsVhHoBmmYjblIRh5D1/g8DHMXJ8=
|
||||
github.com/speakeasy-api/jsonpath v0.6.0/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw=
|
||||
github.com/speakeasy-api/openapi-overlay v0.10.2 h1:VOdQ03eGKeiHnpb1boZCGm7x8Haj6gST0P3SGTX95GU=
|
||||
github.com/speakeasy-api/openapi-overlay v0.10.2/go.mod h1:n0iOU7AqKpNFfEt6tq7qYITC4f0yzVVdFw0S7hukemg=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
@@ -283,10 +291,10 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
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=
|
||||
@@ -298,6 +306,8 @@ github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9N
|
||||
github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
|
||||
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -313,37 +323,37 @@ github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
|
||||
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
||||
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
|
||||
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
|
||||
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty v1.18.1 h1:yEGE8M4iIZlyKQURZNb2SnEyZlZHUcBCnx6KF81KuwM=
|
||||
github.com/zclconf/go-cty v1.18.1/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw=
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -354,14 +364,14 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -385,9 +395,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
@@ -397,25 +406,27 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 h1:mJiOtnGp0k/BcSgdu03G2NwnscCfCH+h2QKUBZr18KI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
|
||||
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -424,8 +435,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
+167
-151
File diff suppressed because it is too large
Load Diff
+1393
-520
File diff suppressed because it is too large
Load Diff
@@ -165,8 +165,8 @@ func SizesDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
},
|
||||
"transfer_max": schema.Float64Attribute{
|
||||
Computed: true,
|
||||
Description: "The maximum transfer in TB permitted for this size.",
|
||||
MarkdownDescription: "The maximum transfer in TB permitted for this size.",
|
||||
Description: "The maximum transfer in TB permitted for this size. If this is the same as `transfer` no additional transfer is supported.",
|
||||
MarkdownDescription: "The maximum transfer in TB permitted for this size. If this is the same as `transfer` no additional transfer is supported.",
|
||||
},
|
||||
"weekly_backups": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
|
||||
@@ -82,9 +82,9 @@ func (d *imagesDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
resp.Diagnostics.AddError("Unexpected status code listing images", string(listResp.Body))
|
||||
return
|
||||
}
|
||||
imgResults = append(imgResults, *listResp.JSON200.Images...)
|
||||
imgResults = append(imgResults, listResp.JSON200.Images...)
|
||||
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -22,8 +23,9 @@ type loadBalancerDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type loadBalancerDataSourceModel struct {
|
||||
type loadBalancerDataModel struct {
|
||||
resources.LoadBalancerModel
|
||||
Ip types.String `tfsdk:"ip"`
|
||||
}
|
||||
|
||||
func (d *loadBalancerDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
@@ -52,7 +54,7 @@ func (d *loadBalancerDataSource) Configure(
|
||||
|
||||
func (d *loadBalancerDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(
|
||||
resources.LoadBalancerResourceSchema(ctx),
|
||||
loadBalancerSchema(ctx),
|
||||
AttributeConfig{
|
||||
RequiredAttributes: &[]string{"id"},
|
||||
},
|
||||
@@ -63,10 +65,11 @@ func (d *loadBalancerDataSource) Schema(ctx context.Context, req datasource.Sche
|
||||
}
|
||||
resp.Schema = *ds
|
||||
resp.Schema.Description = "Retrieve details about a BinaryLane Load Balancer."
|
||||
resp.Schema.MarkdownDescription = resp.Schema.Description
|
||||
}
|
||||
|
||||
func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data loadBalancerDataSourceModel
|
||||
var data loadBalancerDataModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
@@ -93,7 +96,7 @@ func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRe
|
||||
return
|
||||
}
|
||||
|
||||
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
diags := setLoadBalancerModelState(ctx, &data, &lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
||||
@@ -10,14 +10,17 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
"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/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
@@ -37,7 +40,7 @@ type loadBalancerResource struct {
|
||||
}
|
||||
|
||||
type loadBalancerResourceModel struct {
|
||||
resources.LoadBalancerModel
|
||||
loadBalancerDataModel
|
||||
Timeouts timeouts.Value `tfsdk:"timeouts"`
|
||||
}
|
||||
|
||||
@@ -60,15 +63,15 @@ func (d *loadBalancerResource) Configure(_ context.Context, req resource.Configu
|
||||
d.bc = &bc
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.LoadBalancerResourceSchema(ctx)
|
||||
resp.Schema.Description = "Provides a BinaryLane Load Balancer resource. This can be used to create, update, and delete load balancers."
|
||||
resp.Schema.MarkdownDescription = resp.Schema.Description +
|
||||
func loadBalancerSchema(ctx context.Context) schema.Schema {
|
||||
s := resources.LoadBalancerResourceSchema(ctx)
|
||||
s.Description = "Provides a BinaryLane Load Balancer resource. This can be used to create, update, and delete load balancers."
|
||||
s.MarkdownDescription = s.Description +
|
||||
" See [the docs](https://support.binarylane.com.au/support/solutions/articles/1000025661-load-balancer) for more information."
|
||||
|
||||
// Overrides
|
||||
id := resp.Schema.Attributes["id"]
|
||||
resp.Schema.Attributes["id"] = &schema.Int64Attribute{
|
||||
id := s.Attributes["id"]
|
||||
s.Attributes["id"] = schema.Int64Attribute{
|
||||
Description: id.GetDescription(),
|
||||
MarkdownDescription: id.GetMarkdownDescription(),
|
||||
// read only
|
||||
@@ -80,8 +83,8 @@ func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRe
|
||||
},
|
||||
}
|
||||
|
||||
region := resp.Schema.Attributes["region"]
|
||||
resp.Schema.Attributes["region"] = &schema.StringAttribute{
|
||||
region := s.Attributes["region"]
|
||||
s.Attributes["region"] = schema.StringAttribute{
|
||||
Description: region.GetDescription(),
|
||||
MarkdownDescription: region.GetMarkdownDescription(),
|
||||
Optional: true,
|
||||
@@ -91,10 +94,56 @@ func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRe
|
||||
},
|
||||
}
|
||||
|
||||
resp.Schema.Attributes["timeouts"] =
|
||||
timeouts.Attributes(ctx, timeouts.Opts{
|
||||
Create: true,
|
||||
})
|
||||
forwardingRules := s.Attributes["forwarding_rules"].(schema.ListNestedAttribute)
|
||||
s.Attributes["forwarding_rules"] = schema.ListNestedAttribute{
|
||||
NestedObject: forwardingRules.NestedObject,
|
||||
Description: forwardingRules.GetDescription(),
|
||||
MarkdownDescription: forwardingRules.GetMarkdownDescription(),
|
||||
Required: false,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Default: listdefault.StaticValue(types.ListValueMust(
|
||||
types.ObjectType{
|
||||
AttrTypes: resources.ForwardingRulesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
[]attr.Value{
|
||||
types.ObjectValueMust(
|
||||
map[string]attr.Type{
|
||||
"entry_protocol": types.StringType,
|
||||
},
|
||||
map[string]attr.Value{
|
||||
"entry_protocol": types.StringValue("http"),
|
||||
},
|
||||
),
|
||||
},
|
||||
)),
|
||||
Validators: []validator.List{
|
||||
listvalidator.SizeAtLeast(1),
|
||||
},
|
||||
}
|
||||
|
||||
// Additional attributes
|
||||
s.Attributes["ip"] = schema.StringAttribute{
|
||||
Description: "The IPv4 address of the load balancer.",
|
||||
MarkdownDescription: "The IPv4 address of the load balancer.",
|
||||
Required: false,
|
||||
Optional: false,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = loadBalancerSchema(ctx)
|
||||
|
||||
resp.Schema.Attributes["timeouts"] = timeouts.Attributes(ctx, timeouts.Opts{
|
||||
Create: true,
|
||||
Update: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
@@ -107,9 +156,14 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
|
||||
return
|
||||
}
|
||||
|
||||
timeout, diags := data.Timeouts.Create(ctx, 20*time.Minute)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
// Create API call logic
|
||||
forwardingRules := []binarylane.ForwardingRule{}
|
||||
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, &forwardingRules, false)
|
||||
forwardingRules := []binarylane.ForwardingRuleRequest{}
|
||||
diags = data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, &forwardingRules, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -129,7 +183,7 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
|
||||
}
|
||||
|
||||
if data.HealthCheck.Path.ValueStringPointer() != nil || data.HealthCheck.Protocol.ValueStringPointer() != nil {
|
||||
body.HealthCheck = &binarylane.HealthCheck{
|
||||
body.HealthCheck = &binarylane.HealthCheckRequest{
|
||||
Path: data.HealthCheck.Path.ValueStringPointer(),
|
||||
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
|
||||
}
|
||||
@@ -154,7 +208,7 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
|
||||
return
|
||||
}
|
||||
|
||||
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
diags = setLoadBalancerModelState(ctx, &data.loadBalancerDataModel, &lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -163,60 +217,20 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
// Wait for server to be ready
|
||||
var createActionId int64
|
||||
for _, action := range *lbResp.JSON200.Links.Actions {
|
||||
if *action.Rel == "create_load_balancer" {
|
||||
createActionId = *action.Id
|
||||
// Wait for load balancer to be ready
|
||||
var actionId int64
|
||||
for _, action := range lbResp.JSON200.Links.Actions {
|
||||
if action.Rel == "create_load_balancer" {
|
||||
actionId = action.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
if createActionId == 0 {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to wait for load balancer to be created, links.actions with rel=create_load_balancer missing from response",
|
||||
fmt.Sprintf("Received %s creating new load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body))
|
||||
|
||||
err = r.waitForAction(ctx, actionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for load balancer to be created", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
createTimeout, diags := data.Timeouts.Create(ctx, 20*time.Minute)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
ctx, cancel := context.WithTimeout(ctx, createTimeout)
|
||||
defer cancel()
|
||||
var lastReadyResp *binarylane.GetActionsActionIdResponse
|
||||
|
||||
retryLoop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if lastReadyResp == nil {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(
|
||||
"Timed out waiting for load balancer to be created",
|
||||
fmt.Sprintf(
|
||||
"Timed out waiting for load balancer %s to be created, last response was status=%s, body: %s",
|
||||
data.Name.ValueString(),
|
||||
lastReadyResp.Status(),
|
||||
lastReadyResp.Body,
|
||||
),
|
||||
)
|
||||
return
|
||||
default:
|
||||
readyResp, err := r.bc.client.GetActionsActionIdWithResponse(ctx, createActionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for load balancer to be created", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
|
||||
break retryLoop
|
||||
}
|
||||
|
||||
lastReadyResp = readyResp
|
||||
tflog.Debug(ctx, fmt.Sprintf("Waiting for load balancer to be created: name=%s, status=%s, details: %s", data.Name.ValueString(), readyResp.Status(), readyResp.Body))
|
||||
}
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
@@ -253,7 +267,7 @@ func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadReques
|
||||
return
|
||||
}
|
||||
|
||||
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
diags := setLoadBalancerModelState(ctx, &data.loadBalancerDataModel, &lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -274,7 +288,7 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
forwardingRules := &[]binarylane.ForwardingRule{}
|
||||
forwardingRules := &[]binarylane.ForwardingRuleRequest{}
|
||||
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, forwardingRules, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
@@ -293,13 +307,18 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
|
||||
body := binarylane.UpdateLoadBalancerRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
ForwardingRules: forwardingRules,
|
||||
HealthCheck: &binarylane.HealthCheck{
|
||||
HealthCheck: &binarylane.HealthCheckRequest{
|
||||
Path: data.HealthCheck.Path.ValueStringPointer(),
|
||||
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
|
||||
},
|
||||
ServerIds: serverIds,
|
||||
}
|
||||
|
||||
timeout, diags := data.Timeouts.Update(ctx, 20*time.Minute)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
lbResp, err := r.bc.client.PutLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64(), body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
@@ -317,12 +336,27 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
|
||||
return
|
||||
}
|
||||
|
||||
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
diags = setLoadBalancerModelState(ctx, &data.loadBalancerDataModel, &lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for load balancer to be ready
|
||||
var actionId int64
|
||||
for _, action := range lbResp.JSON200.Links.Actions {
|
||||
if action.Rel == "update_load_balancer" {
|
||||
actionId = action.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = r.waitForAction(ctx, actionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for load balancer to be updated", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
@@ -369,85 +403,60 @@ func (r *loadBalancerResource) ImportState(
|
||||
}
|
||||
|
||||
// Import by name
|
||||
var page int32 = 1
|
||||
perPage := int32(200)
|
||||
var loadBalancer binarylane.LoadBalancer
|
||||
var nextPage bool = true
|
||||
|
||||
for nextPage { // Need to paginate because the API does not support filtering by name
|
||||
params := binarylane.GetLoadBalancersParams{
|
||||
Page: &page,
|
||||
PerPage: &perPage,
|
||||
}
|
||||
|
||||
lbResp, err := r.bc.client.GetLoadBalancersWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Error getting load balancer for import: name=%s", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code getting load balancer for import",
|
||||
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), req.ID,
|
||||
lbResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
loadBalancers := *lbResp.JSON200.LoadBalancers
|
||||
for _, lb := range loadBalancers {
|
||||
if *lb.Name == req.ID {
|
||||
loadBalancer = lb
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if lbResp.JSON200.Links == nil || lbResp.JSON200.Links.Pages == nil || lbResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
params := binarylane.GetLoadBalancersParams{
|
||||
Name: &req.ID,
|
||||
}
|
||||
|
||||
if loadBalancer.Id == nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Could not find load balancer by name",
|
||||
fmt.Sprintf("Error finding load balancer: name=%s", req.ID),
|
||||
)
|
||||
lbResp, err := r.bc.client.GetLoadBalancersWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(fmt.Sprintf("Error getting load balancer for import: name=%s", req.ID), err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("id"), *loadBalancer.Id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code getting load balancer for import",
|
||||
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), req.ID,
|
||||
lbResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
for _, lb := range lbResp.JSON200.LoadBalancers {
|
||||
if lb.Name == req.ID {
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), lb.Id)...)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resp.Diagnostics.AddError(
|
||||
"Load balancer not found",
|
||||
fmt.Sprintf("No load balancer named %q was found to import.", req.ID),
|
||||
)
|
||||
}
|
||||
|
||||
func setLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancerModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
|
||||
func setLoadBalancerModelState(ctx context.Context, data *loadBalancerDataModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
|
||||
var diags, diag diag.Diagnostics
|
||||
|
||||
data.Id = types.Int64Value(*lb.Id)
|
||||
data.Name = types.StringValue(*lb.Name)
|
||||
data.Id = types.Int64Value(lb.Id)
|
||||
data.Name = types.StringValue(lb.Name)
|
||||
data.Ip = types.StringValue(lb.Ip)
|
||||
|
||||
if lb.Region == nil {
|
||||
data.Region = types.StringNull()
|
||||
} else {
|
||||
data.Region = types.StringValue(*lb.Region.Slug)
|
||||
data.Region = types.StringValue(lb.Region.Slug)
|
||||
}
|
||||
|
||||
data.ServerIds, diags = types.ListValueFrom(ctx, types.Int64Type, lb.ServerIds)
|
||||
data.ServerIds, diags = types.SetValueFrom(ctx, types.Int64Type, lb.ServerIds)
|
||||
|
||||
if lb.HealthCheck == nil {
|
||||
data.HealthCheck = resources.NewHealthCheckValueNull()
|
||||
} else {
|
||||
data.HealthCheck, diag = resources.NewHealthCheckValue(
|
||||
resources.HealthCheckValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"path": types.StringValue(*lb.HealthCheck.Path),
|
||||
"protocol": types.StringValue(*lb.HealthCheck.Protocol),
|
||||
},
|
||||
)
|
||||
diags.Append(diag...)
|
||||
}
|
||||
data.HealthCheck, diag = resources.NewHealthCheckValue(
|
||||
resources.HealthCheckValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"path": types.StringValue(lb.HealthCheck.Path),
|
||||
"protocol": types.StringValue(lb.HealthCheck.Protocol),
|
||||
},
|
||||
)
|
||||
diags.Append(diag...)
|
||||
|
||||
data.ForwardingRules, diag = types.ListValueFrom(ctx,
|
||||
types.ObjectType{AttrTypes: resources.ForwardingRulesValue{}.AttributeTypes(ctx)},
|
||||
@@ -457,3 +466,37 @@ func setLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancer
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) waitForAction(ctx context.Context, actionId int64) error {
|
||||
var lastReadyResp *binarylane.GetActionsActionIdResponse
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if lastReadyResp == nil {
|
||||
return fmt.Errorf("timed out waiting for action: action_id=%d", actionId)
|
||||
} else {
|
||||
return fmt.Errorf("timed out waiting for action: action_id=%d, last response was status=%s, body: %s",
|
||||
actionId, lastReadyResp.Status(), lastReadyResp.Body)
|
||||
}
|
||||
default:
|
||||
readyResp, err := r.bc.client.GetActionsActionIdWithResponse(ctx, actionId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected error waiting for action: action_id=%d, error: %w", actionId, err)
|
||||
}
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.Status == binarylane.Errored {
|
||||
return fmt.Errorf("action failed with error: action_id=%d, error: %s", actionId, readyResp.Body)
|
||||
}
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
|
||||
return nil
|
||||
}
|
||||
lastReadyResp = readyResp
|
||||
tflog.Debug(ctx,
|
||||
fmt.Sprintf("waiting for action for action_id=%d: last response was status=%s, details: %s",
|
||||
actionId, readyResp.Status(), readyResp.Body,
|
||||
),
|
||||
)
|
||||
}
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,15 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"maps"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
)
|
||||
|
||||
func TestLoadBalancerResource(t *testing.T) {
|
||||
@@ -25,7 +28,7 @@ func TestLoadBalancerResource(t *testing.T) {
|
||||
resource "binarylane_server" "test" {
|
||||
count = 2
|
||||
name = "tf-test-lb-server-${count.index}"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
@@ -35,7 +38,7 @@ resource "binarylane_server" "test" {
|
||||
resource "binarylane_load_balancer" "test" {
|
||||
name = "tf-test-lb"
|
||||
server_ids = [binarylane_server.test.0.id, binarylane_server.test.1.id]
|
||||
forwarding_rules = [{ entry_protocol = "http" }]
|
||||
# initially, skip defining forwarding_rules, health_check, to test default values
|
||||
}
|
||||
|
||||
data "binarylane_load_balancer" "test" {
|
||||
@@ -50,20 +53,26 @@ data "binarylane_load_balancer" "test" {
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "name", "tf-test-lb"),
|
||||
resource.TestCheckNoResourceAttr("binarylane_load_balancer.test", "region"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "server_ids.#", "2"),
|
||||
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
|
||||
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
|
||||
testCheckIfResourceAttrContainsAttr("binarylane_load_balancer.test", "server_ids", "binarylane_server.test.0", "id"),
|
||||
testCheckIfResourceAttrContainsAttr("binarylane_load_balancer.test", "server_ids", "binarylane_server.test.1", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.path", "/"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.protocol", "http"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "ip"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "id", "binarylane_load_balancer.test", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "name", "tf-test-lb"),
|
||||
resource.TestCheckNoResourceAttr("data.binarylane_load_balancer.test", "region"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "server_ids.#", "2"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
|
||||
testCheckIfResourceAttrContainsAttr("data.binarylane_load_balancer.test", "server_ids", "binarylane_server.test.0", "id"),
|
||||
testCheckIfResourceAttrContainsAttr("data.binarylane_load_balancer.test", "server_ids", "binarylane_server.test.1", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.path", "/"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.protocol", "http"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_load_balancer.test", "ip"),
|
||||
),
|
||||
},
|
||||
// Test import by ID
|
||||
@@ -79,6 +88,64 @@ data "binarylane_load_balancer" "test" {
|
||||
ImportStateId: "tf-test-lb",
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
{
|
||||
Taint: []string{"binarylane_server.test[0]"},
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
count = 2
|
||||
name = "tf-test-lb-server-${count.index}"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
resource "binarylane_load_balancer" "test" {
|
||||
name = "tf-test-lb"
|
||||
server_ids = [binarylane_server.test.0.id, binarylane_server.test.1.id]
|
||||
# updated
|
||||
forwarding_rules = [{ entry_protocol = "https" }]
|
||||
health_check = {
|
||||
path = "/test-health"
|
||||
protocol = "https"
|
||||
}
|
||||
}
|
||||
|
||||
data "binarylane_load_balancer" "test" {
|
||||
depends_on = [binarylane_load_balancer.test]
|
||||
|
||||
id = binarylane_load_balancer.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "name", "tf-test-lb"),
|
||||
resource.TestCheckNoResourceAttr("binarylane_load_balancer.test", "region"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "server_ids.#", "2"),
|
||||
testCheckIfResourceAttrContainsAttr("binarylane_load_balancer.test", "server_ids", "binarylane_server.test.0", "id"),
|
||||
testCheckIfResourceAttrContainsAttr("binarylane_load_balancer.test", "server_ids", "binarylane_server.test.1", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "https"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.path", "/test-health"),
|
||||
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.protocol", "https"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "ip"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "id", "binarylane_load_balancer.test", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "name", "tf-test-lb"),
|
||||
resource.TestCheckNoResourceAttr("data.binarylane_load_balancer.test", "region"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "server_ids.#", "2"),
|
||||
testCheckIfResourceAttrContainsAttr("data.binarylane_load_balancer.test", "server_ids", "binarylane_server.test.0", "id"),
|
||||
testCheckIfResourceAttrContainsAttr("data.binarylane_load_balancer.test", "server_ids", "binarylane_server.test.1", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "https"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.path", "/test-health"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.protocol", "https"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_load_balancer.test", "ip"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -115,20 +182,20 @@ func init() {
|
||||
return fmt.Errorf("Unexpected status code getting load balancers for test sweep: %s", listResp.Body)
|
||||
}
|
||||
|
||||
loadBalancers := *listResp.JSON200.LoadBalancers
|
||||
loadBalancers := listResp.JSON200.LoadBalancers
|
||||
for _, lb := range loadBalancers {
|
||||
if strings.HasPrefix(*lb.Name, "tf-test-") {
|
||||
deleteResp, err := client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, *lb.Id)
|
||||
if strings.HasPrefix(lb.Name, "tf-test-") {
|
||||
deleteResp, err := client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, lb.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting load balancer %d for test sweep: %w", *lb.Id, err)
|
||||
return fmt.Errorf("Error deleting load balancer %d for test sweep: %w", lb.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting load balancer %d in test sweep: %s", deleteResp.StatusCode(), *lb.Id, deleteResp.Body)
|
||||
return fmt.Errorf("Unexpected status %d deleting load balancer %d in test sweep: %s", deleteResp.StatusCode(), lb.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted load balancer during test sweep:", *lb.Id)
|
||||
log.Println("Deleted load balancer during test sweep:", lb.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
@@ -139,3 +206,65 @@ func init() {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) {
|
||||
ms := s.RootModule()
|
||||
|
||||
rs, ok := ms.Resources[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Not found: %s in %s, resources: %v", name, ms.Path, slices.Collect(maps.Keys(ms.Resources)))
|
||||
}
|
||||
is := rs.Primary
|
||||
if is == nil {
|
||||
return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path)
|
||||
}
|
||||
return is, nil
|
||||
}
|
||||
|
||||
func testCheckIfResourceAttrContainsAttr(collectionKey, collectionPath, otherResourceKey, otherAttrPath string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
collectionState, err := primaryInstanceState(s, collectionKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elementState, err := primaryInstanceState(s, otherResourceKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if collectionKey == otherResourceKey && collectionPath == otherAttrPath {
|
||||
return fmt.Errorf(
|
||||
"comparing self: resource %s attribute %s",
|
||||
collectionKey,
|
||||
collectionPath,
|
||||
)
|
||||
}
|
||||
|
||||
vElement, okElement := elementState.Attributes[otherAttrPath]
|
||||
if !okElement {
|
||||
return fmt.Errorf("%s: Attribute %q not set, cannot be contained in %q from %s", otherResourceKey, otherAttrPath, collectionPath, collectionKey)
|
||||
}
|
||||
|
||||
vCollection, okCollection := collectionState.Attributes[collectionPath+".#"]
|
||||
if !okCollection {
|
||||
return fmt.Errorf("%s: Attribute %q not a collection, cannot contain %q from %s", collectionKey, collectionPath, otherAttrPath, otherResourceKey)
|
||||
}
|
||||
|
||||
for attrKey, attrValue := range collectionState.Attributes {
|
||||
if strings.HasPrefix(attrKey, collectionPath+".") &&
|
||||
!(strings.HasSuffix(attrKey, ".%")) &&
|
||||
!strings.HasSuffix(attrKey, ".#") &&
|
||||
attrValue == vElement {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf(
|
||||
"%s: Attribute '%s' expected to contain %#v, got %#v",
|
||||
collectionKey,
|
||||
collectionPath,
|
||||
vElement,
|
||||
vCollection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,24 @@ provider "binarylane" {
|
||||
# End of provider config
|
||||
|
||||
`
|
||||
|
||||
// planOnlyProviderConfig configures the provider with a placeholder token, for tests that
|
||||
// only ever plan and so never reach the API. providerConfig leaves the token to
|
||||
// BINARYLANE_API_TOKEN, which those tests would otherwise need set to run at all.
|
||||
planOnlyProviderConfig = `
|
||||
provider "binarylane" {
|
||||
api_token = "placeholder-never-used"
|
||||
}
|
||||
|
||||
# End of provider config
|
||||
|
||||
`
|
||||
|
||||
// testRegion is the BinaryLane region acceptance tests create servers in.
|
||||
// TEMPORARY: set to "mel" because "per" is intermittently returning "Unable to find a
|
||||
// suitable host for the requested server configuration". Revert to "per" once that's
|
||||
// resolved.
|
||||
testRegion = "mel"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -83,7 +83,7 @@ func (d *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest
|
||||
}
|
||||
regResults = append(regResults, listResp.JSON200.Regions...)
|
||||
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
@@ -31,6 +32,8 @@ type serverDataModel struct {
|
||||
resources.ServerModel
|
||||
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
|
||||
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
|
||||
PublicIpv6Addresses types.List `tfsdk:"public_ipv6_addresses"`
|
||||
PrivateIpv6Addresses types.List `tfsdk:"private_ipv6_addresses"`
|
||||
Permalink types.String `tfsdk:"permalink"`
|
||||
Memory types.Int32 `tfsdk:"memory"`
|
||||
Disk types.Int32 `tfsdk:"disk"`
|
||||
@@ -103,20 +106,36 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
return
|
||||
}
|
||||
|
||||
data.Id = types.Int64Value(*serverResp.JSON200.Server.Id)
|
||||
data.Name = types.StringValue(*serverResp.JSON200.Server.Name)
|
||||
data.Id = types.Int64Value(serverResp.JSON200.Server.Id)
|
||||
data.Name = types.StringValue(serverResp.JSON200.Server.Name)
|
||||
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
|
||||
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
|
||||
data.Region = types.StringValue(serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(serverResp.JSON200.Server.Size.Slug)
|
||||
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
data.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
|
||||
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
|
||||
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
|
||||
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
|
||||
data.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
|
||||
data.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
|
||||
data.Memory = types.Int32Value(serverResp.JSON200.Server.Memory)
|
||||
data.Disk = types.Int32Value(serverResp.JSON200.Server.Disk)
|
||||
data.Disks, diag = serverDisksToList(ctx, serverResp.JSON200.Server.Disks, data.Disks)
|
||||
diags.Append(diag...)
|
||||
data.SourceAndDestinationCheck = types.BoolPointerValue(serverResp.JSON200.Server.Networks.SourceAndDestinationCheck)
|
||||
data.SeparatePrivateNetworkInterface = types.BoolPointerValue(serverResp.JSON200.Server.Networks.SeparatePrivateNetworkInterface)
|
||||
|
||||
advFeat := *serverResp.JSON200.Server.AdvancedFeatures.EnabledAdvancedFeatures
|
||||
if serverResp.JSON200.Server.VpcId == nil {
|
||||
data.VpcIpv4Address = types.StringNull()
|
||||
} else if len(serverResp.JSON200.Server.Networks.V4) > 0 {
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
// Skip addresses in 172.21.0.0/16, these are BL internal addresses that are not part of the user's VPC
|
||||
if v4address.Type == "private" && !strings.HasPrefix(v4address.IpAddress, "172.21.") {
|
||||
data.VpcIpv4Address = types.StringValue(v4address.IpAddress)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
advFeat := serverResp.JSON200.Server.AdvancedFeatures.EnabledAdvancedFeatures
|
||||
data.AdvancedFeatures, diags = resources.NewAdvancedFeaturesValue(
|
||||
resources.AdvancedFeaturesValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
@@ -162,6 +181,33 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
data.PrivateIPv4Addresses = tfPrivateIpv4Addresses
|
||||
}
|
||||
|
||||
publicIpv6Addresses := []string{}
|
||||
privateIpv6Addresses := []string{}
|
||||
|
||||
for _, v6address := range serverResp.JSON200.Server.Networks.V6 {
|
||||
if v6address.Type == "public" {
|
||||
publicIpv6Addresses = append(publicIpv6Addresses, v6address.IpAddress)
|
||||
} else {
|
||||
privateIpv6Addresses = append(privateIpv6Addresses, v6address.IpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
tfPublicIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv6Addresses)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() {
|
||||
data.PublicIpv6Addresses = types.ListUnknown(data.PublicIpv6Addresses.ElementType(ctx))
|
||||
} else {
|
||||
data.PublicIpv6Addresses = tfPublicIpv6Addresses
|
||||
}
|
||||
|
||||
tfPrivateIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv6Addresses)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() {
|
||||
data.PrivateIpv6Addresses = types.ListUnknown(data.PrivateIpv6Addresses.ElementType(ctx))
|
||||
} else {
|
||||
data.PrivateIpv6Addresses = tfPrivateIpv6Addresses
|
||||
}
|
||||
|
||||
// Get user data script
|
||||
userDataResp, err := d.bc.client.GetServersServerIdUserDataWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
|
||||
@@ -63,7 +63,7 @@ func (r *serverFirewallRulesResource) Schema(ctx context.Context, req resource.S
|
||||
|
||||
// Overrides
|
||||
serverId := resp.Schema.Attributes["server_id"]
|
||||
resp.Schema.Attributes["server_id"] = &schema.Int64Attribute{
|
||||
resp.Schema.Attributes["server_id"] = schema.Int64Attribute{
|
||||
Description: serverId.GetDescription(),
|
||||
MarkdownDescription: serverId.GetMarkdownDescription(),
|
||||
Required: true, // Server ID is required to define the firewall rules
|
||||
@@ -251,7 +251,7 @@ func GetFirewallRulesState(ctx context.Context, firewallRules *[]binarylane.Adva
|
||||
firewallRulesValue := resources.FirewallRulesValue{}
|
||||
var firewallRulesValues []resources.FirewallRulesValue
|
||||
|
||||
if *firewallRules == nil || len(*firewallRules) == 0 {
|
||||
if len(*firewallRules) == 0 {
|
||||
firewallRulesValues = []resources.FirewallRulesValue{}
|
||||
} else {
|
||||
for _, rule := range *firewallRules {
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestServerFirewallRulesResource(t *testing.T) {
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
@@ -90,7 +90,7 @@ data "binarylane_server_firewall_rules" "test" {
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
@@ -22,6 +23,10 @@ func TestServerResource(t *testing.T) {
|
||||
sshPublicKeyInitial := GenerateTestPublicKey(t)
|
||||
sshPublicKeyUpdated := GenerateTestPublicKey(t)
|
||||
|
||||
// Captured while "zeta" still exists, then checked once it is gone and the disks after it
|
||||
// have shifted down a position.
|
||||
var alphaDiskId, betaDiskId string
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
@@ -48,7 +53,7 @@ resource "binarylane_ssh_key" "updated" {
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
memory = 1152
|
||||
@@ -57,6 +62,7 @@ resource "binarylane_server" "test" {
|
||||
public_ipv4_count = 1
|
||||
ssh_keys = [binarylane_ssh_key.initial.id]
|
||||
source_and_destination_check = false
|
||||
separate_private_network_interface = true
|
||||
backups = true
|
||||
port_blocking = false
|
||||
user_data = <<EOT
|
||||
@@ -75,12 +81,18 @@ data "binarylane_server" "test" {
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "region", "per"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "region", testRegion),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-11"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "1152"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "20"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "20"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_ipv4_address"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password1),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", `#cloud-config
|
||||
@@ -93,6 +105,7 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.initial", "id"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "permalink"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "source_and_destination_check", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "separate_private_network_interface", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.emulated_hyperv", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.emulated_devices", "false"),
|
||||
@@ -104,22 +117,32 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.cloud_init", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.qemu_guest_agent", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.uefi_boot", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ipv6", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv6_addresses.#", "0"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "name", "tf-test-server-resource"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "region", "per"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "region", testRegion),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "debian-11"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "vpc_ipv4_address"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "permalink", "binarylane_server.test", "permalink"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "user_data", `#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
`),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "memory", "1152"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disk", "20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.size_gigabytes", "20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "backups", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "port_blocking", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "source_and_destination_check", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "separate_private_network_interface", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.emulated_hyperv", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.emulated_devices", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.nested_virt", "false"),
|
||||
@@ -130,6 +153,8 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.cloud_init", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.qemu_guest_agent", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.uefi_boot", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "ipv6", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "public_ipv6_addresses.#", "0"),
|
||||
),
|
||||
},
|
||||
// Test import by ID
|
||||
@@ -169,10 +194,16 @@ resource "binarylane_ssh_key" "updated" {
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource-2"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-1vcpu"
|
||||
disk = "45"
|
||||
size = "std-min"
|
||||
disk = 40
|
||||
disks = [
|
||||
{ primary = true, size_gigabytes = 25 },
|
||||
{ description = "zeta", size_gigabytes = 5 },
|
||||
{ description = "alpha", size_gigabytes = 5 },
|
||||
{ description = "beta", size_gigabytes = 5 },
|
||||
]
|
||||
password = "` + password1 + `"
|
||||
vpc_id = null
|
||||
public_ipv4_count = 0
|
||||
@@ -180,6 +211,7 @@ resource "binarylane_server" "test" {
|
||||
advanced_features = {
|
||||
emulated_hyperv = true
|
||||
}
|
||||
ipv6 = true
|
||||
|
||||
# source_and_destination_check = null # defaults to null
|
||||
# backups = false # defaults to false
|
||||
@@ -195,9 +227,34 @@ EOT
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource-2"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-1vcpu"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "2048"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "45"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
|
||||
// `memory` is unset here and `size` is unchanged, so it carries forward
|
||||
// the 1152 set in the previous step.
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "1152"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "40"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "4"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "25"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.1.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.description", "zeta"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.size_gigabytes", "5"),
|
||||
resource.TestCheckResourceAttrWith("binarylane_server.test", "disks.2.id", func(value string) error {
|
||||
alphaDiskId = value
|
||||
return nil
|
||||
}),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.description", "alpha"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.size_gigabytes", "5"),
|
||||
resource.TestCheckResourceAttrWith("binarylane_server.test", "disks.3.id", func(value string) error {
|
||||
betaDiskId = value
|
||||
return nil
|
||||
}),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.description", "beta"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.size_gigabytes", "5"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-12"),
|
||||
@@ -206,6 +263,7 @@ EOT
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.updated", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.emulated_hyperv", "true"),
|
||||
resource.TestCheckNoResourceAttr("binarylane_server.test", "source_and_destination_check"),
|
||||
resource.TestCheckNoResourceAttr("binarylane_server.test", "separate_private_network_interface"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "port_blocking", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", // test extra whitespace
|
||||
@@ -214,6 +272,9 @@ echo "Hello Whitespace" > /var/tmp/output.txt
|
||||
|
||||
|
||||
`),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ipv6", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv6_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv6_addresses.#", "1"),
|
||||
),
|
||||
},
|
||||
// Change password testing (Cannot run at same time as Rebuild operation, so it has it's own test)
|
||||
@@ -238,10 +299,16 @@ resource "binarylane_ssh_key" "updated" {
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource-2"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-1vcpu"
|
||||
disk = "45"
|
||||
size = "std-min"
|
||||
disk = 35
|
||||
disks = [
|
||||
{ primary = true, size_gigabytes = 15 },
|
||||
{ description = "alpha", size_gigabytes = 10 },
|
||||
{ description = "beta", size_gigabytes = 5 },
|
||||
{ description = "delta", size_gigabytes = 5 },
|
||||
]
|
||||
password = "` + password2 + `"
|
||||
vpc_id = null
|
||||
public_ipv4_count = 0
|
||||
@@ -262,6 +329,288 @@ EOT
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource-2"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password2),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "35"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "4"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "15"),
|
||||
// Removing "zeta" shifts these down a position, but each keeps the disk it
|
||||
// had: they are matched to prior state by description, not by index.
|
||||
resource.TestCheckResourceAttrWith("binarylane_server.test", "disks.1.id", func(value string) error {
|
||||
if value != alphaDiskId {
|
||||
return fmt.Errorf("disk \"alpha\" has id %s, want %s", value, alphaDiskId)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.description", "alpha"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.size_gigabytes", "10"),
|
||||
resource.TestCheckResourceAttrWith("binarylane_server.test", "disks.2.id", func(value string) error {
|
||||
if value != betaDiskId {
|
||||
return fmt.Errorf("disk \"beta\" has id %s, want %s", value, betaDiskId)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.description", "beta"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.size_gigabytes", "5"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.3.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.description", "delta"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.size_gigabytes", "5"),
|
||||
),
|
||||
},
|
||||
// Dropping `disks` leaves the server's disks alone: growing the total here only
|
||||
// adds unallocated space, since the primary is not the only disk.
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_ssh_key" "updated" {
|
||||
name = "tf-test-server-resource-updated"
|
||||
public_key = "` + sshPublicKeyUpdated + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource-2"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
disk = 50
|
||||
password = "` + password2 + `"
|
||||
vpc_id = null
|
||||
public_ipv4_count = 0
|
||||
ssh_keys = [binarylane_ssh_key.updated.id]
|
||||
|
||||
user_data = <<EOT
|
||||
#cloud-config
|
||||
echo "Hello Whitespace" > /var/tmp/output.txt
|
||||
|
||||
|
||||
EOT
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "50"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "4"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "15"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.1.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.description", "alpha"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.size_gigabytes", "10"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.2.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.description", "beta"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.size_gigabytes", "5"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.3.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.description", "delta"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.3.size_gigabytes", "5"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestServerResourceDisksValidation exercises the plan-time `disks` validators. Every case only
|
||||
// plans a fresh server, so no infrastructure is created.
|
||||
func TestServerResourceDisksValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
// attributes are added to an otherwise valid server resource.
|
||||
attributes string
|
||||
// expectErr is the error the plan must fail with, or empty if it must succeed.
|
||||
expectErr string
|
||||
}{
|
||||
{
|
||||
name: "no primary disk",
|
||||
attributes: `disks = [{ description = "data1", size_gigabytes = 10 }]`,
|
||||
expectErr: "Missing primary disk",
|
||||
},
|
||||
{
|
||||
name: "empty disks list",
|
||||
attributes: `disks = []`,
|
||||
expectErr: "Missing primary disk",
|
||||
},
|
||||
{
|
||||
name: "two primary disks",
|
||||
attributes: `disks = [
|
||||
{ primary = true, size_gigabytes = 20 },
|
||||
{ primary = true, size_gigabytes = 10 },
|
||||
]`,
|
||||
expectErr: "Multiple primary disks",
|
||||
},
|
||||
{
|
||||
// The primary disk's description is assigned by Binary Lane, not by the user.
|
||||
name: "primary disk with a description",
|
||||
attributes: `disks = [{ primary = true, description = "SYSTEM", size_gigabytes = 20 }]`,
|
||||
expectErr: "Primary disk description is server-assigned",
|
||||
},
|
||||
{
|
||||
name: "disks exceeding the total",
|
||||
attributes: `disk = 45
|
||||
disks = [
|
||||
{ primary = true, size_gigabytes = 30 },
|
||||
{ description = "data1", size_gigabytes = 20 },
|
||||
]`,
|
||||
expectErr: "Disks exceed total disk allocation",
|
||||
},
|
||||
{
|
||||
name: "disk below the minimum",
|
||||
attributes: `disk = 10`,
|
||||
expectErr: "Attribute disk",
|
||||
},
|
||||
{
|
||||
// Binary Lane allows duplicate descriptions, and so does this provider.
|
||||
name: "duplicate descriptions",
|
||||
attributes: `disks = [
|
||||
{ primary = true, size_gigabytes = 20 },
|
||||
{ description = "data", size_gigabytes = 5 },
|
||||
{ description = "data", size_gigabytes = 10 },
|
||||
]`,
|
||||
},
|
||||
{
|
||||
// Also valid with room to spare, leaving the difference unallocated.
|
||||
name: "disks filling the total exactly",
|
||||
attributes: `disk = 40
|
||||
disks = [
|
||||
{ primary = true, size_gigabytes = 30 },
|
||||
{ description = "data1", size_gigabytes = 10 },
|
||||
]`,
|
||||
},
|
||||
{
|
||||
name: "disks with the total left unset",
|
||||
attributes: `disks = [
|
||||
{ primary = true, size_gigabytes = 30 },
|
||||
{ description = "data1", size_gigabytes = 10 },
|
||||
]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
step := resource.TestStep{
|
||||
Config: planOnlyProviderConfig + fmt.Sprintf(`
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-disks-validation"
|
||||
region = %q
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
%s
|
||||
}
|
||||
`, testRegion, tt.attributes),
|
||||
PlanOnly: true,
|
||||
ExpectNonEmptyPlan: true,
|
||||
}
|
||||
if tt.expectErr != "" {
|
||||
step.ExpectError = regexp.MustCompile(tt.expectErr)
|
||||
}
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{step},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerResourceCreateWithDisks(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
password := GenerateTestPassword(t)
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-create-disks"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
disk = 40
|
||||
disks = [
|
||||
{ primary = true, size_gigabytes = 20 },
|
||||
{ description = "data", size_gigabytes = 10 },
|
||||
{ description = "data", size_gigabytes = 5 },
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// 5 GB of the total is left unallocated.
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "40"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "3"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "20"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.1.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.description", "data"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.size_gigabytes", "10"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.2.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.description", "data"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.size_gigabytes", "5"),
|
||||
),
|
||||
},
|
||||
// The same disks in a different order, which is not a change.
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-create-disks"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
disk = 40
|
||||
disks = [
|
||||
{ description = "data", size_gigabytes = 10 },
|
||||
{ primary = true, size_gigabytes = 20 },
|
||||
{ description = "data", size_gigabytes = 5 },
|
||||
]
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// The resource keeps the order the configuration lists the disks in
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "3"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.description", "data"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "10"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.1.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.size_gigabytes", "20"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.2.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.description", "data"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.2.size_gigabytes", "5"),
|
||||
|
||||
// The data source has no order of its own, so it lists the primary disk
|
||||
// first and the rest by id
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.#", "3"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.description", "SYSTEM"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.size_gigabytes", "20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.1.primary", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.1.description", "data"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.1.size_gigabytes", "10"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.2.primary", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.2.description", "data"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.2.size_gigabytes", "5"),
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -281,7 +630,7 @@ func TestServerResourceRename(t *testing.T) {
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-rename-1"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
@@ -294,7 +643,7 @@ resource "binarylane_server" "test" {
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-rename-2"
|
||||
region = "per"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
@@ -303,7 +652,7 @@ resource "binarylane_server" "test" {
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-rename-2"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "region", "per"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "region", testRegion),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-11"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password),
|
||||
@@ -313,6 +662,153 @@ resource "binarylane_server" "test" {
|
||||
})
|
||||
}
|
||||
|
||||
func TestServerVpcIpv4Change(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
password := GenerateTestPassword(t)
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Plan should fail if vpc_ipv4_address is set without vpc_id
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_ipv4_address = "10.241.69.69"
|
||||
}
|
||||
`,
|
||||
PlanOnly: true,
|
||||
ExpectError: regexp.MustCompile("Attribute \"vpc_id\" must be specified when \"vpc_ipv4_address\" is specified"),
|
||||
},
|
||||
// Setup
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test1" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc" "test2" {
|
||||
name = "tf-test-server-vpcipv4-2"
|
||||
ip_range = "10.241.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test1.id
|
||||
vpc_ipv4_address = "10.240.0.69"
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "vpc_id", "binarylane_vpc.test1", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "vpc_ipv4_address", "10.240.0.69"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.0", "10.240.0.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "vpc_ipv4_address", "10.240.0.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.0", "10.240.0.69"),
|
||||
),
|
||||
},
|
||||
// Change IP without changing VPC
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test1" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc" "test2" {
|
||||
name = "tf-test-server-vpcipv4-2"
|
||||
ip_range = "10.241.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test1.id
|
||||
vpc_ipv4_address = "10.240.4.20"
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "vpc_id", "binarylane_vpc.test1", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "vpc_ipv4_address", "10.240.4.20"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.0", "10.240.4.20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "vpc_ipv4_address", "10.240.4.20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.0", "10.240.4.20"),
|
||||
),
|
||||
},
|
||||
// Change VPC (should also change IP)
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test1" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc" "test2" {
|
||||
name = "tf-test-server-vpcipv4-2"
|
||||
ip_range = "10.241.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "` + testRegion + `"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test2.id
|
||||
vpc_ipv4_address = "10.241.69.69"
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "vpc_id", "binarylane_vpc.test2", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "vpc_ipv4_address", "10.241.69.69"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.0", "10.241.69.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "vpc_ipv4_address", "10.241.69.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.0", "10.241.69.69"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func GenerateTestPassword(t *testing.T) string {
|
||||
t.Helper()
|
||||
pwBytes := make([]byte, 12)
|
||||
@@ -328,7 +824,6 @@ func init() {
|
||||
Name: "server",
|
||||
F: func(_ string) error {
|
||||
client, err := binarylane.NewClientWithDefaultConfig()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
|
||||
}
|
||||
@@ -354,25 +849,25 @@ func init() {
|
||||
return fmt.Errorf("Unexpected status code getting servers in test sweep: %s", listResp.Body)
|
||||
}
|
||||
|
||||
servers := *listResp.JSON200.Servers
|
||||
servers := listResp.JSON200.Servers
|
||||
for _, s := range servers {
|
||||
if strings.HasPrefix(*s.Name, "tf-test-") {
|
||||
if strings.HasPrefix(s.Name, "tf-test-") {
|
||||
reason := "Terraform deletion"
|
||||
params := binarylane.DeleteServersServerIdParams{
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
deleteResp, err := client.DeleteServersServerIdWithResponse(ctx, *s.Id, ¶ms)
|
||||
deleteResp, err := client.DeleteServersServerIdWithResponse(ctx, s.Id, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting server %d during test sweep: %w", *s.Id, err)
|
||||
return fmt.Errorf("Error deleting server %d during test sweep: %w", s.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting server %d in test sweep: %s", deleteResp.StatusCode(), *s.Id, deleteResp.Body)
|
||||
return fmt.Errorf("Unexpected status %d deleting server %d in test sweep: %s", deleteResp.StatusCode(), s.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted server during test sweep:", *s.Id)
|
||||
log.Println("Deleted server during test sweep:", s.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (d *sizesDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
}
|
||||
regResults = append(regResults, listResp.JSON200.Sizes...)
|
||||
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
|
||||
}
|
||||
|
||||
fingerprintDescription := "The fingerprint of the SSH key."
|
||||
resp.Schema.Attributes["fingerprint"] = &schema.StringAttribute{
|
||||
resp.Schema.Attributes["fingerprint"] = schema.StringAttribute{
|
||||
Description: fingerprintDescription,
|
||||
MarkdownDescription: fingerprintDescription,
|
||||
Optional: false,
|
||||
@@ -118,13 +118,13 @@ func (d *sshKeyDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
return
|
||||
}
|
||||
|
||||
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
|
||||
data.Default = types.BoolValue(*sshResp.JSON200.SshKey.Default)
|
||||
data.Id = types.Int64Value(sshResp.JSON200.SshKey.Id)
|
||||
data.Default = types.BoolValue(sshResp.JSON200.SshKey.Default)
|
||||
data.Name = types.StringValue(*sshResp.JSON200.SshKey.Name)
|
||||
data.PublicKey = TrimmedStringValue{
|
||||
StringValue: types.StringValue(*sshResp.JSON200.SshKey.PublicKey),
|
||||
StringValue: types.StringValue(sshResp.JSON200.SshKey.PublicKey),
|
||||
}
|
||||
data.Fingerprint = types.StringValue(*sshResp.JSON200.SshKey.Fingerprint)
|
||||
data.Fingerprint = types.StringValue(sshResp.JSON200.SshKey.Fingerprint)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
@@ -105,7 +105,7 @@ func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest,
|
||||
}
|
||||
|
||||
fingerprintDescription := "The fingerprint of the SSH key."
|
||||
resp.Schema.Attributes["fingerprint"] = &schema.StringAttribute{
|
||||
resp.Schema.Attributes["fingerprint"] = schema.StringAttribute{
|
||||
Description: fingerprintDescription,
|
||||
MarkdownDescription: fingerprintDescription,
|
||||
Optional: false,
|
||||
@@ -153,8 +153,8 @@ func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
}
|
||||
|
||||
// Set data values
|
||||
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
|
||||
data.Fingerprint = types.StringValue(*sshResp.JSON200.SshKey.Fingerprint)
|
||||
data.Id = types.Int64Value(sshResp.JSON200.SshKey.Id)
|
||||
data.Fingerprint = types.StringValue(sshResp.JSON200.SshKey.Fingerprint)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -191,13 +191,13 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
}
|
||||
|
||||
// Set data values
|
||||
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
|
||||
data.Default = types.BoolValue(*sshResp.JSON200.SshKey.Default)
|
||||
data.Id = types.Int64Value(sshResp.JSON200.SshKey.Id)
|
||||
data.Default = types.BoolValue(sshResp.JSON200.SshKey.Default)
|
||||
data.Name = types.StringValue(*sshResp.JSON200.SshKey.Name)
|
||||
data.PublicKey = TrimmedStringValue{
|
||||
StringValue: types.StringValue(*sshResp.JSON200.SshKey.PublicKey),
|
||||
StringValue: types.StringValue(sshResp.JSON200.SshKey.PublicKey),
|
||||
}
|
||||
data.Fingerprint = types.StringValue(*sshResp.JSON200.SshKey.Fingerprint)
|
||||
data.Fingerprint = types.StringValue(sshResp.JSON200.SshKey.Fingerprint)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
@@ -312,13 +312,13 @@ func (r *sshKeyResource) ImportState(
|
||||
|
||||
sshKeys := sshResp.JSON200.SshKeys
|
||||
for _, key := range sshKeys {
|
||||
if *key.Fingerprint == req.ID {
|
||||
if key.Fingerprint == req.ID {
|
||||
sshKey = key
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if sshResp.JSON200.Links == nil || sshResp.JSON200.Links.Pages == nil || sshResp.JSON200.Links.Pages.Next == nil {
|
||||
if sshResp.JSON200.Links == nil || sshResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
@@ -326,7 +326,7 @@ func (r *sshKeyResource) ImportState(
|
||||
page++
|
||||
}
|
||||
|
||||
if sshKey.Id == nil {
|
||||
if sshKey.Id == 0 {
|
||||
resp.Diagnostics.AddError(
|
||||
"Could not find SSH key by fingerprint",
|
||||
fmt.Sprintf("Error finding SSH key: fingerprint=%s", req.ID),
|
||||
@@ -334,6 +334,6 @@ func (r *sshKeyResource) ImportState(
|
||||
return
|
||||
}
|
||||
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("id"), *sshKey.Id)
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("id"), sshKey.Id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
|
||||
@@ -151,17 +151,17 @@ func init() {
|
||||
for _, k := range keys {
|
||||
if strings.HasPrefix(*k.Name, "tf-test-") {
|
||||
|
||||
deleteResp, err := client.DeleteAccountKeysKeyIdWithResponse(ctx, int(*k.Id))
|
||||
deleteResp, err := client.DeleteAccountKeysKeyIdWithResponse(ctx, int(k.Id))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting SSH key %d for test sweep: %w", *k.Id, err)
|
||||
return fmt.Errorf("Error deleting SSH key %d for test sweep: %w", k.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting SSH key %d for test sweep: %s", deleteResp.StatusCode(), *k.Id, deleteResp.Body)
|
||||
return fmt.Errorf("Unexpected status %d deleting SSH key %d for test sweep: %s", deleteResp.StatusCode(), k.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted SSH key for test sweep:", *k.Id)
|
||||
log.Println("Deleted SSH key for test sweep:", k.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -127,6 +127,45 @@ func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribu
|
||||
}, nil
|
||||
|
||||
default:
|
||||
if t, isSet := attribute.(r_schema.SetNestedAttribute); isSet {
|
||||
nestedObjectAttrs := make(map[string]d_schema.Attribute)
|
||||
for name, attribute := range t.NestedObject.Attributes {
|
||||
nestedAttribute, err := convertResourceAttrToDataSourceAttr(name, attribute, required, optional)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nestedObjectAttrs[name] = nestedAttribute
|
||||
}
|
||||
return d_schema.SetNestedAttribute{
|
||||
NestedObject: d_schema.NestedAttributeObject{
|
||||
Attributes: nestedObjectAttrs,
|
||||
CustomType: t.NestedObject.CustomType,
|
||||
Validators: t.NestedObject.Validators,
|
||||
},
|
||||
CustomType: t.CustomType,
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if t, isSet := attribute.GetType().(types.SetType); isSet {
|
||||
return d_schema.SetAttribute{
|
||||
ElementType: t.ElemType,
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
Computed: !required,
|
||||
Sensitive: attribute.IsSensitive(),
|
||||
MarkdownDescription: attribute.GetMarkdownDescription(),
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if t, isList := attribute.(r_schema.ListNestedAttribute); isList {
|
||||
nestedObjectAttrs := make(map[string]d_schema.Attribute)
|
||||
for name, attribute := range t.NestedObject.Attributes {
|
||||
@@ -136,13 +175,13 @@ func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribu
|
||||
}
|
||||
nestedObjectAttrs[name] = nestedAttribute
|
||||
}
|
||||
|
||||
return d_schema.ListNestedAttribute{
|
||||
NestedObject: d_schema.NestedAttributeObject{
|
||||
Attributes: nestedObjectAttrs,
|
||||
CustomType: t.NestedObject.CustomType,
|
||||
Validators: t.NestedObject.Validators,
|
||||
},
|
||||
CustomType: t.CustomType,
|
||||
Description: attribute.GetDescription(),
|
||||
Required: required,
|
||||
Optional: optional,
|
||||
@@ -165,6 +204,7 @@ func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribu
|
||||
DeprecationMessage: attribute.GetDeprecationMessage(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if t, isObject := attribute.(r_schema.SingleNestedAttribute); isObject {
|
||||
attributeTypes := make(map[string]attr.Type, len(t.GetAttributes()))
|
||||
for name, attribute := range t.GetAttributes() {
|
||||
@@ -183,7 +223,6 @@ func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribu
|
||||
AttributeTypes: attributeTypes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if t, isObject := attribute.GetType().(types.ObjectType); isObject {
|
||||
return d_schema.ObjectAttribute{
|
||||
AttributeTypes: t.AttrTypes,
|
||||
|
||||
@@ -90,8 +90,8 @@ func (d *vpcDataSource) Read(ctx context.Context, req datasource.ReadRequest, re
|
||||
fmt.Sprintf("Received %s reading VPC: id=%d. Details: %s", vpcResp.Status(), data.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
data.IpRange = types.StringValue(*vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
data.IpRange = types.StringValue(vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
@@ -12,6 +12,9 @@ import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
@@ -60,7 +63,7 @@ func (r *vpcResource) Schema(ctx context.Context, req resource.SchemaRequest, re
|
||||
|
||||
// Overrides
|
||||
id := resp.Schema.Attributes["id"]
|
||||
resp.Schema.Attributes["id"] = &schema.Int64Attribute{
|
||||
resp.Schema.Attributes["id"] = schema.Int64Attribute{
|
||||
Description: id.GetDescription(),
|
||||
MarkdownDescription: id.GetMarkdownDescription(),
|
||||
// read only
|
||||
@@ -68,6 +71,19 @@ func (r *vpcResource) Schema(ctx context.Context, req resource.SchemaRequest, re
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
ipRange := resp.Schema.Attributes["ip_range"]
|
||||
resp.Schema.Attributes["ip_range"] = schema.StringAttribute{
|
||||
Description: ipRange.GetDescription(),
|
||||
MarkdownDescription: ipRange.GetMarkdownDescription(),
|
||||
Required: false,
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Default: stringdefault.StaticString("10.240.0.0/16"),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *vpcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
@@ -104,8 +120,8 @@ func (r *vpcResource) Create(ctx context.Context, req resource.CreateRequest, re
|
||||
return
|
||||
}
|
||||
|
||||
data.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
data.Id = types.Int64Value(vpcResp.JSON200.Vpc.Id)
|
||||
data.Name = types.StringValue(vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
@@ -141,9 +157,9 @@ func (r *vpcResource) Read(ctx context.Context, req resource.ReadRequest, resp *
|
||||
fmt.Sprintf("Received %s reading VPC: id=%d. Details: %s", vpcResp.Status(), data.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
data.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
data.IpRange = types.StringValue(*vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
data.Id = types.Int64Value(vpcResp.JSON200.Vpc.Id)
|
||||
data.IpRange = types.StringValue(vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
@@ -187,8 +203,8 @@ func (r *vpcResource) Update(ctx context.Context, req resource.UpdateRequest, re
|
||||
return
|
||||
}
|
||||
|
||||
state.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
state.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
state.Id = types.Int64Value(vpcResp.JSON200.Vpc.Id)
|
||||
state.Name = types.StringValue(vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
|
||||
@@ -156,20 +156,20 @@ func init() {
|
||||
}
|
||||
|
||||
vpcs := listResp.JSON200.Vpcs
|
||||
for _, vpc := range *vpcs {
|
||||
if strings.HasPrefix(*vpc.Name, "tf-test-") {
|
||||
for _, vpc := range vpcs {
|
||||
if strings.HasPrefix(vpc.Name, "tf-test-") {
|
||||
|
||||
deleteResp, err := client.DeleteVpcsVpcIdWithResponse(ctx, *vpc.Id)
|
||||
deleteResp, err := client.DeleteVpcsVpcIdWithResponse(ctx, vpc.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting VPC %d in test sweep: %w", *vpc.Id, err)
|
||||
return fmt.Errorf("Error deleting VPC %d in test sweep: %w", vpc.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting VPC %d for test sweep: %s", deleteResp.StatusCode(), *vpc.Id, deleteResp.Body)
|
||||
return fmt.Errorf("Unexpected status %d deleting VPC %d for test sweep: %s", deleteResp.StatusCode(), vpc.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted VPC for test sweep:", *vpc.Id)
|
||||
log.Println("Deleted VPC for test sweep:", vpc.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.S
|
||||
|
||||
// Overrides
|
||||
vpcId := resp.Schema.Attributes["vpc_id"]
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
resp.Schema.Attributes["vpc_id"] = schema.Int64Attribute{
|
||||
Description: vpcId.GetDescription(),
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Required: true, // vpc_id is required to define the route entries
|
||||
@@ -55,7 +55,7 @@ func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.S
|
||||
|
||||
vpcRouteEntries := resp.Schema.Attributes["route_entries"].(schema.ListNestedAttribute)
|
||||
vpcRouteEntriesDescription := "The route entries that control how network traffic is directed through the VPC environment."
|
||||
resp.Schema.Attributes["route_entries"] = &schema.ListNestedAttribute{
|
||||
resp.Schema.Attributes["route_entries"] = schema.ListNestedAttribute{
|
||||
Description: vpcRouteEntriesDescription,
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
CustomType: vpcRouteEntries.NestedObject.CustomType,
|
||||
@@ -93,7 +93,7 @@ func (d *vpcRouteEntriesDataSource) Read(ctx context.Context, req datasource.Rea
|
||||
return
|
||||
}
|
||||
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, &vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(routeEntriesDiags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
||||
@@ -48,7 +48,7 @@ func (r *vpcRouteEntriesResource) Schema(ctx context.Context, req resource.Schem
|
||||
|
||||
// Overrides
|
||||
vpcId := resp.Schema.Attributes["vpc_id"]
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
resp.Schema.Attributes["vpc_id"] = schema.Int64Attribute{
|
||||
Description: vpcId.GetDescription(),
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Required: true, // vpc_id is required to define the route entries
|
||||
@@ -94,7 +94,7 @@ func (r *vpcRouteEntriesResource) Create(ctx context.Context, req resource.Creat
|
||||
return
|
||||
}
|
||||
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, &vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -136,7 +136,7 @@ func (r *vpcRouteEntriesResource) Read(ctx context.Context, req resource.ReadReq
|
||||
return
|
||||
}
|
||||
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, &vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(routeEntriesDiags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -183,7 +183,7 @@ func (r *vpcRouteEntriesResource) Update(ctx context.Context, req resource.Updat
|
||||
return
|
||||
}
|
||||
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, &vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -246,15 +246,15 @@ func GetRouteEntriesState(ctx context.Context, routeEntries *[]binarylane.RouteE
|
||||
routeEntriesValue := resources.RouteEntriesValue{}
|
||||
var routeEntriesValues []resources.RouteEntriesValue
|
||||
|
||||
if *routeEntries == nil || len(*routeEntries) == 0 {
|
||||
if len(*routeEntries) == 0 {
|
||||
routeEntriesValues = []resources.RouteEntriesValue{}
|
||||
} else {
|
||||
for _, route := range *routeEntries {
|
||||
// TODO: This panics if invalid, should be handled better
|
||||
r := resources.NewRouteEntriesValueMust(routeEntriesValue.AttributeTypes(ctx), map[string]attr.Value{
|
||||
"description": types.StringValue(*route.Description),
|
||||
"destination": types.StringValue(*route.Destination),
|
||||
"router": types.StringValue(*route.Router),
|
||||
"destination": types.StringValue(route.Destination),
|
||||
"router": types.StringValue(route.Router),
|
||||
})
|
||||
routeEntriesValues = append(routeEntriesValues, r)
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ func LoadBalancerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"entry_protocol": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
|
||||
MarkdownDescription: "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
|
||||
Description: "The protocol that traffic must match for this load balancer to forward traffic according to this rule.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The load balancer will forward HTTP traffic that matches this rule. |\n| https | The load balancer will forward HTTPS traffic that matches this rule. |\n\n",
|
||||
MarkdownDescription: "The protocol that traffic must match for this load balancer to forward traffic according to this rule.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The load balancer will forward HTTP traffic that matches this rule. |\n| https | The load balancer will forward HTTPS traffic that matches this rule. |\n\n",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"http",
|
||||
@@ -55,6 +55,7 @@ func LoadBalancerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "Leave null to accept the default '/' path.",
|
||||
MarkdownDescription: "Leave null to accept the default '/' path.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
stringvalidator.RegexMatches(regexp.MustCompile("/[A-Za-z0-9/.?=&+%_-]*"), ""),
|
||||
},
|
||||
},
|
||||
@@ -95,7 +96,7 @@ func LoadBalancerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "Leave null to create an anycast load balancer.",
|
||||
MarkdownDescription: "Leave null to create an anycast load balancer.",
|
||||
},
|
||||
"server_ids": schema.ListAttribute{
|
||||
"server_ids": schema.SetAttribute{
|
||||
ElementType: types.Int64Type,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
@@ -112,7 +113,7 @@ type LoadBalancerModel struct {
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
ServerIds types.List `tfsdk:"server_ids"`
|
||||
ServerIds types.Set `tfsdk:"server_ids"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = ForwardingRulesType{}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"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/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
// DiskKey identifies a disk across plans: the primary by being the primary, whose description
|
||||
// Binary Lane assigns, and every other disk by its description. Binary Lane allows two disks to
|
||||
// share a description, so they are also numbered within it: the second "data" disk configured is
|
||||
// the second "data" disk on the server.
|
||||
type DiskKey struct {
|
||||
primary bool
|
||||
description string
|
||||
ordinal int
|
||||
}
|
||||
|
||||
// DiskKeyer numbers disks as it keys them, so both sides of a match must be keyed in the same
|
||||
// order: id order for the server's own disks.
|
||||
type DiskKeyer map[string]int
|
||||
|
||||
func (k DiskKeyer) Of(primary bool, description string) DiskKey {
|
||||
if primary {
|
||||
return DiskKey{primary: true}
|
||||
}
|
||||
ordinal := k[description]
|
||||
k[description]++
|
||||
return DiskKey{description: description, ordinal: ordinal}
|
||||
}
|
||||
|
||||
// DiskShrinkWarning warns that shrinking a disk may corrupt the filesystem on it.
|
||||
func DiskShrinkWarning(attrPath path.Path, label string, from int64, to int64) diag.Diagnostic {
|
||||
return diag.NewAttributeWarningDiagnostic(
|
||||
attrPath,
|
||||
fmt.Sprintf("%s shrink may corrupt the filesystem", label),
|
||||
fmt.Sprintf(
|
||||
"%s is being reduced from %d GB to %d GB. Binary Lane resizes the block device in place and does not touch the "+
|
||||
"filesystem on it, so the filesystem(s) (and any partitions) on it must first be shrunk to fit within %d GB. "+
|
||||
"Back up the server before applying, and make sure there's enough free space to shrink the filesystem to that size.",
|
||||
label, from, to, to,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// disksPlanModifier is referenced by name from the "disks" attribute in
|
||||
// scripts/data/server_disks.json, the same way disksValidator is.
|
||||
//
|
||||
// Terraform builds the proposed plan for a list by merging prior state into the configuration
|
||||
// element by element, so removing a disk hands the disks after it the id of their predecessor.
|
||||
// This plans the list from the configuration instead, taking each disk's server-assigned values
|
||||
// from the disk it matches in prior state rather than from whatever sits at the same index.
|
||||
type disksPlanModifier struct{}
|
||||
|
||||
func (m disksPlanModifier) Description(ctx context.Context) string {
|
||||
return "Matches each disk with the one it corresponds to in prior state, rather than with whatever is at the same index."
|
||||
}
|
||||
|
||||
func (m disksPlanModifier) MarkdownDescription(ctx context.Context) string {
|
||||
return m.Description(ctx)
|
||||
}
|
||||
|
||||
func (m disksPlanModifier) PlanModifyList(ctx context.Context, req planmodifier.ListRequest, resp *planmodifier.ListResponse) {
|
||||
// Nothing to plan when the resource is being destroyed.
|
||||
if req.Plan.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
|
||||
// An unset `disks` is whatever Binary Lane gives the server, which isn't known until the
|
||||
// server exists. Once it does, the resource's ModifyPlan plans it instead: what happens
|
||||
// to the disks then depends on `disk`, the server's total, which it only resolves after
|
||||
// attribute plan modifiers like this one have run.
|
||||
if req.State.Raw.IsNull() {
|
||||
resp.PlanValue = types.ListUnknown(DisksValue{}.Type(ctx))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var configured, current []DisksValue
|
||||
resp.Diagnostics.Append(req.ConfigValue.ElementsAs(ctx, &configured, true)...)
|
||||
if !req.StateValue.IsNull() && !req.StateValue.IsUnknown() {
|
||||
resp.Diagnostics.Append(req.StateValue.ElementsAs(ctx, ¤t, true)...)
|
||||
}
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Key prior state in id order, the order the server's own disks are keyed in wherever they
|
||||
// are matched. serverDisksToList already leaves state in an order that agrees with it, but
|
||||
// this does not have to rely on that: get it wrong and disks that share a description are
|
||||
// paired with the wrong disk, and the plan promises a resize of one while the apply resizes
|
||||
// another.
|
||||
slices.SortStableFunc(current, func(a, b DisksValue) int {
|
||||
return cmp.Compare(a.Id.ValueInt64(), b.Id.ValueInt64())
|
||||
})
|
||||
|
||||
stateKeys := DiskKeyer{}
|
||||
priorByKey := make(map[DiskKey]DisksValue, len(current))
|
||||
for _, d := range current {
|
||||
priorByKey[stateKeys.Of(d.Primary.ValueBool(), d.Description.ValueString())] = d
|
||||
}
|
||||
|
||||
configKeys := DiskKeyer{}
|
||||
elements := make([]attr.Value, 0, len(configured))
|
||||
for _, cd := range configured {
|
||||
// Additional disks may leave `primary` unset, which means false.
|
||||
isPrimary := cd.Primary.ValueBool()
|
||||
prior, matched := priorByKey[configKeys.Of(isPrimary, cd.Description.ValueString())]
|
||||
|
||||
// `id` and the primary's `description` are assigned by Binary Lane, so both are unknown
|
||||
// until there is a disk in state to take them from.
|
||||
id, description := types.Int64Unknown(), cd.Description
|
||||
if isPrimary {
|
||||
description = types.StringUnknown()
|
||||
}
|
||||
if matched {
|
||||
if !prior.Id.IsNull() && !prior.Id.IsUnknown() {
|
||||
id = prior.Id
|
||||
}
|
||||
if isPrimary && !prior.Description.IsNull() && !prior.Description.IsUnknown() {
|
||||
description = prior.Description
|
||||
}
|
||||
if !cd.SizeGigabytes.IsUnknown() && cd.SizeGigabytes.ValueInt64() < prior.SizeGigabytes.ValueInt64() {
|
||||
label := fmt.Sprintf("Disk %q", cd.Description.ValueString())
|
||||
if isPrimary {
|
||||
label = "The primary disk"
|
||||
}
|
||||
resp.Diagnostics.Append(DiskShrinkWarning(
|
||||
req.Path, label, prior.SizeGigabytes.ValueInt64(), cd.SizeGigabytes.ValueInt64()))
|
||||
}
|
||||
}
|
||||
|
||||
disk, diskDiags := NewDisksValue(
|
||||
DisksValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"id": id,
|
||||
"description": description,
|
||||
"primary": types.BoolValue(isPrimary),
|
||||
"size_gigabytes": cd.SizeGigabytes,
|
||||
},
|
||||
)
|
||||
resp.Diagnostics.Append(diskDiags...)
|
||||
elements = append(elements, disk)
|
||||
}
|
||||
|
||||
planned, diags := types.ListValue(DisksValue{}.Type(ctx), elements)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if !diags.HasError() {
|
||||
resp.PlanValue = planned
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
// disksValidator is referenced by name from the "disks" attribute in
|
||||
// scripts/data/server_disks.json, which extend-provider.sh splices into the generated schema. It
|
||||
// lives here rather than in internal/provider, which internal/resources cannot import.
|
||||
type disksValidator struct{}
|
||||
|
||||
func (v disksValidator) Description(ctx context.Context) string {
|
||||
return "Requires exactly one `disks` entry to have `primary = true` (with no `description` set) whenever `disks` is configured, and `sum(disks[*].size_gigabytes)` to not exceed `disk`."
|
||||
}
|
||||
|
||||
func (v disksValidator) MarkdownDescription(ctx context.Context) string {
|
||||
return v.Description(ctx)
|
||||
}
|
||||
|
||||
func (v disksValidator) ValidateList(ctx context.Context, req validator.ListRequest, resp *validator.ListResponse) {
|
||||
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
|
||||
return
|
||||
}
|
||||
|
||||
var disks []DisksValue
|
||||
resp.Diagnostics.Append(req.ConfigValue.ElementsAs(ctx, &disks, false)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
primaries, primaryUnknown := 0, false
|
||||
sum, sumKnown := int64(0), true
|
||||
for i, d := range disks {
|
||||
switch {
|
||||
case d.Primary.IsUnknown():
|
||||
primaryUnknown = true
|
||||
case d.Primary.ValueBool():
|
||||
primaries++
|
||||
if !d.Description.IsNull() && !d.Description.IsUnknown() {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
req.Path.AtListIndex(i).AtName("description"),
|
||||
"Primary disk description is server-assigned",
|
||||
"The primary disk's `description` is always assigned by Binary Lane (typically \"SYSTEM\"); leave it unset on the entry with `primary = true`.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if d.SizeGigabytes.IsNull() || d.SizeGigabytes.IsUnknown() {
|
||||
sumKnown = false
|
||||
continue
|
||||
}
|
||||
sum += d.SizeGigabytes.ValueInt64()
|
||||
}
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if !primaryUnknown {
|
||||
if primaries == 0 {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
req.Path,
|
||||
"Missing primary disk",
|
||||
"When `disks` is specified, it must fully describe every disk, including the primary — add an entry with `primary = true`.",
|
||||
)
|
||||
return
|
||||
}
|
||||
if primaries > 1 {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
req.Path,
|
||||
"Multiple primary disks",
|
||||
"Only one entry in `disks` can have `primary = true`.",
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var disk types.Int32
|
||||
resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("disk"), &disk)...)
|
||||
if resp.Diagnostics.HasError() || !sumKnown || disk.IsNull() || disk.IsUnknown() {
|
||||
return
|
||||
}
|
||||
if sum > int64(disk.ValueInt32()) {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
req.Path,
|
||||
"Disks exceed total disk allocation",
|
||||
fmt.Sprintf(
|
||||
"The sum of `disks[*].size_gigabytes` is %d GB, which exceeds `disk` (%d GB total). "+
|
||||
"`sum(disks[*].size_gigabytes)` must not exceed `disk`.",
|
||||
sum, disk.ValueInt32(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ package resources
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
@@ -94,6 +96,55 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.\n",
|
||||
MarkdownDescription: "If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.\n",
|
||||
},
|
||||
"disks": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"description": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "A label for this disk. Assigned by Binary Lane for the primary disk, so must be left null when `primary = true`. Changing this will destroy the disk and create a new one, losing any data on it. Disks that share a description are matched in the order they are listed.",
|
||||
MarkdownDescription: "A label for this disk. Assigned by Binary Lane for the primary disk, so must be left null when `primary = true`. Changing this will destroy the disk and create a new one, losing any data on it. Disks that share a description are matched in the order they are listed.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The ID of this disk.",
|
||||
MarkdownDescription: "The ID of this disk.",
|
||||
},
|
||||
"primary": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If `true` this is the primary disk, which the operating system is installed on. Exactly one disk must be the primary.",
|
||||
MarkdownDescription: "If `true` this is the primary disk, which the operating system is installed on. Exactly one disk must be the primary.",
|
||||
},
|
||||
"size_gigabytes": schema.Int64Attribute{
|
||||
Required: true,
|
||||
Description: "The size of the disk in GB. The sum across all disks cannot exceed `disk`, and any remainder is left unallocated.",
|
||||
MarkdownDescription: "The size of the disk in GB. The sum across all disks cannot exceed `disk`, and any remainder is left unallocated.",
|
||||
Validators: []validator.Int64{
|
||||
int64validator.AtLeast(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
CustomType: DisksType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: DisksValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The disks attached to this server. Leave null to let Binary Lane manage the server's disks. If specified, every disk must be listed, including the primary: exactly one entry must have `primary = true`, and its `id` and `description` are assigned by Binary Lane.",
|
||||
MarkdownDescription: "The disks attached to this server. Leave null to let Binary Lane manage the server's disks. If specified, every disk must be listed, including the primary: exactly one entry must have `primary = true`, and its `id` and `description` are assigned by Binary Lane.",
|
||||
PlanModifiers: []planmodifier.List{
|
||||
disksPlanModifier{},
|
||||
},
|
||||
Validators: []validator.List{
|
||||
disksValidator{},
|
||||
},
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
@@ -105,6 +156,12 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "The slug of the selected operating system.",
|
||||
MarkdownDescription: "The slug of the selected operating system.",
|
||||
},
|
||||
"ipv6": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If true this will enable IPv6 for this server.",
|
||||
MarkdownDescription: "If true this will enable IPv6 for this server.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
@@ -114,8 +171,8 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
"port_blocking": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.",
|
||||
MarkdownDescription: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.",
|
||||
Description: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to verified accounts.",
|
||||
MarkdownDescription: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to verified accounts.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
@@ -125,6 +182,12 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"separate_private_network_interface": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If true this will enable a separate private network interface for the server. This is only available for servers in a VPC.",
|
||||
MarkdownDescription: "If true this will enable a separate private network interface for the server. This is only available for servers in a VPC.",
|
||||
},
|
||||
"size": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The slug of the selected size.",
|
||||
@@ -155,22 +218,32 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "Leave null to use default (public) network for the selected region.",
|
||||
MarkdownDescription: "Leave null to use default (public) network for the selected region.",
|
||||
},
|
||||
"vpc_ipv4_address": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided.",
|
||||
MarkdownDescription: "If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ServerModel struct {
|
||||
AdvancedFeatures AdvancedFeaturesValue `tfsdk:"advanced_features"`
|
||||
Backups types.Bool `tfsdk:"backups"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
PortBlocking types.Bool `tfsdk:"port_blocking"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Size types.String `tfsdk:"size"`
|
||||
SshKeys types.List `tfsdk:"ssh_keys"`
|
||||
UserData types.String `tfsdk:"user_data"`
|
||||
VpcId types.Int64 `tfsdk:"vpc_id"`
|
||||
AdvancedFeatures AdvancedFeaturesValue `tfsdk:"advanced_features"`
|
||||
Backups types.Bool `tfsdk:"backups"`
|
||||
Disks types.List `tfsdk:"disks"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
Ipv6 types.Bool `tfsdk:"ipv6"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
PortBlocking types.Bool `tfsdk:"port_blocking"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
SeparatePrivateNetworkInterface types.Bool `tfsdk:"separate_private_network_interface"`
|
||||
Size types.String `tfsdk:"size"`
|
||||
SshKeys types.List `tfsdk:"ssh_keys"`
|
||||
UserData types.String `tfsdk:"user_data"`
|
||||
VpcId types.Int64 `tfsdk:"vpc_id"`
|
||||
VpcIpv4Address types.String `tfsdk:"vpc_ipv4_address"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = AdvancedFeaturesType{}
|
||||
@@ -991,3 +1064,492 @@ func (v AdvancedFeaturesValue) AttributeTypes(ctx context.Context) map[string]at
|
||||
"unset_uuid": basetypes.BoolType{},
|
||||
}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = DisksType{}
|
||||
|
||||
type DisksType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t DisksType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(DisksType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t DisksType) String() string {
|
||||
return "DisksType"
|
||||
}
|
||||
|
||||
func (t DisksType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
idAttribute, ok := attributes["id"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`id is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
idVal, ok := idAttribute.(basetypes.Int64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`id expected to be basetypes.Int64Value, was: %T`, idAttribute))
|
||||
}
|
||||
|
||||
primaryAttribute, ok := attributes["primary"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`primary is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
primaryVal, ok := primaryAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`primary expected to be basetypes.BoolValue, was: %T`, primaryAttribute))
|
||||
}
|
||||
|
||||
sizeGigabytesAttribute, ok := attributes["size_gigabytes"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`size_gigabytes is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
sizeGigabytesVal, ok := sizeGigabytesAttribute.(basetypes.Int64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`size_gigabytes expected to be basetypes.Int64Value, was: %T`, sizeGigabytesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return DisksValue{
|
||||
Description: descriptionVal,
|
||||
Id: idVal,
|
||||
Primary: primaryVal,
|
||||
SizeGigabytes: sizeGigabytesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewDisksValueNull() DisksValue {
|
||||
return DisksValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDisksValueUnknown() DisksValue {
|
||||
return DisksValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDisksValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (DisksValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
|
||||
ctx := context.Background()
|
||||
|
||||
for name, attributeType := range attributeTypes {
|
||||
attribute, ok := attributes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Missing DisksValue Attribute Value",
|
||||
"While creating a DisksValue value, a missing attribute value was detected. "+
|
||||
"A DisksValue must contain values for all attributes, even if null or unknown. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("DisksValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid DisksValue Attribute Type",
|
||||
"While creating a DisksValue value, an invalid attribute value was detected. "+
|
||||
"A DisksValue must use a matching attribute type for the value. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("DisksValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("DisksValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra DisksValue Attribute Value",
|
||||
"While creating a DisksValue value, an extra attribute value was detected. "+
|
||||
"A DisksValue must not contain values beyond the expected attribute types. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("Extra DisksValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
idAttribute, ok := attributes["id"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`id is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
idVal, ok := idAttribute.(basetypes.Int64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`id expected to be basetypes.Int64Value, was: %T`, idAttribute))
|
||||
}
|
||||
|
||||
primaryAttribute, ok := attributes["primary"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`primary is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
primaryVal, ok := primaryAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`primary expected to be basetypes.BoolValue, was: %T`, primaryAttribute))
|
||||
}
|
||||
|
||||
sizeGigabytesAttribute, ok := attributes["size_gigabytes"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`size_gigabytes is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
sizeGigabytesVal, ok := sizeGigabytesAttribute.(basetypes.Int64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`size_gigabytes expected to be basetypes.Int64Value, was: %T`, sizeGigabytesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
return DisksValue{
|
||||
Description: descriptionVal,
|
||||
Id: idVal,
|
||||
Primary: primaryVal,
|
||||
SizeGigabytes: sizeGigabytesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewDisksValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) DisksValue {
|
||||
object, diags := NewDisksValue(attributeTypes, attributes)
|
||||
|
||||
if diags.HasError() {
|
||||
// This could potentially be added to the diag package.
|
||||
diagsStrings := make([]string, 0, len(diags))
|
||||
|
||||
for _, diagnostic := range diags {
|
||||
diagsStrings = append(diagsStrings, fmt.Sprintf(
|
||||
"%s | %s | %s",
|
||||
diagnostic.Severity(),
|
||||
diagnostic.Summary(),
|
||||
diagnostic.Detail()))
|
||||
}
|
||||
|
||||
panic("NewDisksValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t DisksType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewDisksValueNull(), nil
|
||||
}
|
||||
|
||||
if !in.Type().Equal(t.TerraformType(ctx)) {
|
||||
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
|
||||
}
|
||||
|
||||
if !in.IsKnown() {
|
||||
return NewDisksValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewDisksValueNull(), nil
|
||||
}
|
||||
|
||||
attributes := map[string]attr.Value{}
|
||||
|
||||
val := map[string]tftypes.Value{}
|
||||
|
||||
err := in.As(&val)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes[k] = a
|
||||
}
|
||||
|
||||
return NewDisksValueMust(DisksValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t DisksType) ValueType(ctx context.Context) attr.Value {
|
||||
return DisksValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = DisksValue{}
|
||||
|
||||
type DisksValue struct {
|
||||
Description basetypes.StringValue `tfsdk:"description"`
|
||||
Id basetypes.Int64Value `tfsdk:"id"`
|
||||
Primary basetypes.BoolValue `tfsdk:"primary"`
|
||||
SizeGigabytes basetypes.Int64Value `tfsdk:"size_gigabytes"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v DisksValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 4)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["id"] = basetypes.Int64Type{}.TerraformType(ctx)
|
||||
attrTypes["primary"] = basetypes.BoolType{}.TerraformType(ctx)
|
||||
attrTypes["size_gigabytes"] = basetypes.Int64Type{}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 4)
|
||||
|
||||
val, err = v.Description.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["description"] = val
|
||||
|
||||
val, err = v.Id.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["id"] = val
|
||||
|
||||
val, err = v.Primary.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["primary"] = val
|
||||
|
||||
val, err = v.SizeGigabytes.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["size_gigabytes"] = val
|
||||
|
||||
if err := tftypes.ValidateValue(objectType, vals); err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
return tftypes.NewValue(objectType, vals), nil
|
||||
case attr.ValueStateNull:
|
||||
return tftypes.NewValue(objectType, nil), nil
|
||||
case attr.ValueStateUnknown:
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
|
||||
}
|
||||
}
|
||||
|
||||
func (v DisksValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v DisksValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v DisksValue) String() string {
|
||||
return "DisksValue"
|
||||
}
|
||||
|
||||
func (v DisksValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"description": basetypes.StringType{},
|
||||
"id": basetypes.Int64Type{},
|
||||
"primary": basetypes.BoolType{},
|
||||
"size_gigabytes": basetypes.Int64Type{},
|
||||
}
|
||||
|
||||
if v.IsNull() {
|
||||
return types.ObjectNull(attributeTypes), diags
|
||||
}
|
||||
|
||||
if v.IsUnknown() {
|
||||
return types.ObjectUnknown(attributeTypes), diags
|
||||
}
|
||||
|
||||
objVal, diags := types.ObjectValue(
|
||||
attributeTypes,
|
||||
map[string]attr.Value{
|
||||
"description": v.Description,
|
||||
"id": v.Id,
|
||||
"primary": v.Primary,
|
||||
"size_gigabytes": v.SizeGigabytes,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v DisksValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(DisksValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.Description.Equal(other.Description) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Id.Equal(other.Id) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Primary.Equal(other.Primary) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.SizeGigabytes.Equal(other.SizeGigabytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v DisksValue) Type(ctx context.Context) attr.Type {
|
||||
return DisksType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v DisksValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"description": basetypes.StringType{},
|
||||
"id": basetypes.Int64Type{},
|
||||
"primary": basetypes.BoolType{},
|
||||
"size_gigabytes": basetypes.Int64Type{},
|
||||
}
|
||||
}
|
||||
|
||||
+597
-759
File diff suppressed because it is too large
Load Diff
+119
-5
@@ -573,7 +573,7 @@
|
||||
"name": "transfer_max",
|
||||
"float64": {
|
||||
"computed_optional_required": "computed",
|
||||
"description": "The maximum transfer in TB permitted for this size."
|
||||
"description": "The maximum transfer in TB permitted for this size. If this is the same as `transfer` no additional transfer is supported."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -712,7 +712,7 @@
|
||||
"name": "entry_protocol",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
|
||||
"description": "The protocol that traffic must match for this load balancer to forward traffic according to this rule.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The load balancer will forward HTTP traffic that matches this rule. |\n| https | The load balancer will forward HTTPS traffic that matches this rule. |\n\n",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
@@ -743,6 +743,16 @@
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "Leave null to accept the default '/' path.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtLeast(1)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
@@ -798,7 +808,7 @@
|
||||
},
|
||||
{
|
||||
"name": "server_ids",
|
||||
"list": {
|
||||
"set": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"element_type": {
|
||||
"int64": {}
|
||||
@@ -834,6 +844,13 @@
|
||||
"description": "The slug of the selected operating system."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ipv6",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If true this will enable IPv6 for this server."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"string": {
|
||||
@@ -845,7 +862,7 @@
|
||||
"name": "port_blocking",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts."
|
||||
"description": "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to verified accounts."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -867,6 +884,13 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "separate_private_network_interface",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If true this will enable a separate private network interface for the server. This is only available for servers in a VPC."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"string": {
|
||||
@@ -922,6 +946,13 @@
|
||||
"description": "Leave null to use default (public) network for the selected region."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vpc_ipv4_address",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
@@ -1006,6 +1037,88 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "disks",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "The disks attached to this server. Leave null to let Binary Lane manage the server's disks. If specified, every disk must be listed, including the primary: exactly one entry must have `primary = true`, and its `id` and `description` are assigned by Binary Lane.",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed",
|
||||
"description": "The ID of this disk."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "A label for this disk. Assigned by Binary Lane for the primary disk, so must be left null when `primary = true`. Changing this will destroy the disk and create a new one, losing any data on it. Disks that share a description are matched in the order they are listed.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "primary",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If `true` this is the primary disk, which the operating system is installed on. Exactly one disk must be the primary."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size_gigabytes",
|
||||
"int64": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The size of the disk in GB. The sum across all disks cannot exceed `disk`, and any remainder is left unallocated.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "int64validator.AtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"schema_definition": "disksValidator{}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"plan_modifiers": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
}
|
||||
],
|
||||
"schema_definition": "disksPlanModifier{}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1309,5 +1422,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "0.1"
|
||||
"version": "0.1",
|
||||
"$schema": "https://raw.githubusercontent.com/hashicorp/terraform-plugin-codegen-spec/main/spec/v0.1/schema.json"
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ resources:
|
||||
default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to
|
||||
disable deployment of default keys.
|
||||
ignores:
|
||||
- ipv6
|
||||
- licenses
|
||||
- links
|
||||
- server
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "disks",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "The disks attached to this server. Leave null to let Binary Lane manage the server's disks. If specified, every disk must be listed, including the primary: exactly one entry must have `primary = true`, and its `id` and `description` are assigned by Binary Lane.",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed",
|
||||
"description": "The ID of this disk."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "A label for this disk. Assigned by Binary Lane for the primary disk, so must be left null when `primary = true`. Changing this will destroy the disk and create a new one, losing any data on it. Disks that share a description are matched in the order they are listed.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "primary",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If `true` this is the primary disk, which the operating system is installed on. Exactly one disk must be the primary."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size_gigabytes",
|
||||
"int64": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The size of the disk in GB. The sum across all disks cannot exceed `disk`, and any remainder is left unallocated.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "int64validator.AtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"schema_definition": "disksValidator{}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"plan_modifiers": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
}
|
||||
],
|
||||
"schema_definition": "disksPlanModifier{}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,27 @@
|
||||
|
||||
GENERATOR_CONFIG=$(dirname "$0")/../provider_code_spec.json
|
||||
|
||||
# Advanced features configuration
|
||||
ADVANCED_FEATURES_CONFIG=$(dirname "$0")/data/advanced_features.json
|
||||
# Add JSON schema
|
||||
cat <<<$(jq --tab '. + {"$schema": "https://raw.githubusercontent.com/hashicorp/terraform-plugin-codegen-spec/main/spec/v0.1/schema.json"}' $GENERATOR_CONFIG) >$GENERATOR_CONFIG
|
||||
|
||||
# Server configuration
|
||||
ADVANCED_FEATURES_CONFIG=$(dirname "$0")/data/server_advanced_features.json
|
||||
cat <<<$(jq --tab --slurpfile adv_feat_cfg $ADVANCED_FEATURES_CONFIG '.resources[1].schema.attributes |= . + $adv_feat_cfg' $GENERATOR_CONFIG) >$GENERATOR_CONFIG
|
||||
|
||||
DISKS_CONFIG=$(dirname "$0")/data/server_disks.json
|
||||
cat <<<$(jq --tab --slurpfile disks_cfg $DISKS_CONFIG '.resources[1].schema.attributes |= . + $disks_cfg' $GENERATOR_CONFIG) >$GENERATOR_CONFIG
|
||||
|
||||
# Use set instead of list for server_ids in load_balancer resource
|
||||
jq --tab '
|
||||
.resources |= map(
|
||||
if .name == "load_balancer" then
|
||||
.schema.attributes |= map(
|
||||
if .name == "server_ids" then
|
||||
with_entries(.key |= gsub("list"; "set"))
|
||||
else .
|
||||
end
|
||||
)
|
||||
else .
|
||||
end
|
||||
)
|
||||
' "$GENERATOR_CONFIG" >"$GENERATOR_CONFIG.tmp" && mv "$GENERATOR_CONFIG.tmp" "$GENERATOR_CONFIG"
|
||||
|
||||
@@ -37,6 +37,12 @@ cat <<<$(jq 'del(.paths."/images/{image_id}")' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
# Add x-oapi-codegen-extra-tags so structs can be reflected
|
||||
|
||||
## Disk
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.id += {"x-oapi-codegen-extra-tags": {"tfsdk": "id"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.primary += {"x-oapi-codegen-extra-tags": {"tfsdk": "primary"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.size_gigabytes += {"x-oapi-codegen-extra-tags": {"tfsdk": "size_gigabytes"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
## RouteEntryRequest
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.destination += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
@@ -59,6 +65,7 @@ cat <<<$(jq '.components.schemas.AdvancedFirewallRuleRequest.properties.source_a
|
||||
## Load Balancer
|
||||
cat <<<$(jq '.components.schemas.CreateLoadBalancerRequest.properties.forwarding_rules += {"x-oapi-codegen-extra-tags": {"tfsdk": "forwarding_rules"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "entry_protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.ForwardingRuleRequest.properties.entry_protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "entry_protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.LoadBalancer.properties.health_check += {"x-oapi-codegen-extra-tags": {"tfsdk": "health_check"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.HealthCheckProtocol |= del(.enum)' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
@@ -143,3 +150,4 @@ cat <<<$(jq '.components.schemas.SizeType.properties.description += {"x-oapi-cod
|
||||
|
||||
# Edit description here because it's hard to override nested schema properties
|
||||
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol.description = "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\"."' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.SizeOptions.properties.transfer_max.description = "The maximum transfer in TB permitted for this size. If this is the same as `transfer` no additional transfer is supported."' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
Reference in New Issue
Block a user