forked from mirrors/terraform-provider-binarylane
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bc281d177 | |||
| 2a6ba27340 | |||
| fecd42d4a9 | |||
| c5a44b7694 | |||
| 2c639837ef | |||
| 200b2ee3a1 | |||
| a135bceee8 | |||
| 103f4cc2b0 | |||
| db948a103c | |||
| e18d2424d0 | |||
| e1aac7ec1f | |||
| 6c3520bbda | |||
| 22ea042e33 | |||
| b63f052a9f | |||
| a5731e0a12 | |||
| e5450d9f27 | |||
| 53b1643f30 |
@@ -1,2 +1,8 @@
|
||||
TF_ACC=1
|
||||
BINARYLANE_API_TOKEN=
|
||||
|
||||
# Uncomment to enable debug logging (will dump request bodies)
|
||||
# TF_LOG=DEBUG
|
||||
|
||||
# Uncomment to disable the Go cache when testing, useful for flaky tests
|
||||
# GOCACHE=off
|
||||
|
||||
@@ -32,13 +32,13 @@ jobs:
|
||||
go-version-file: 'go.mod'
|
||||
cache: true
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
|
||||
uses: crazy-max/ghaction-import-gpg@cb9bde2e2525e640591a934b1fd28eef1dcaf5e5 # v6.2.0
|
||||
id: import_gpg
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
with:
|
||||
args: release --clean
|
||||
env:
|
||||
|
||||
@@ -23,18 +23,18 @@ 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@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- uses: hashicorp/setup-terraform@v2
|
||||
- uses: hashicorp/setup-terraform@v3
|
||||
with:
|
||||
terraform_version: '1.5.*'
|
||||
terraform_wrapper: false
|
||||
- name: Check if generated files are up to date
|
||||
run: |
|
||||
go generate
|
||||
git diff --color --exit-code
|
||||
git diff --color --exit-code --ignore-matching-lines='//'
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "generated files are not up to date, please run 'go generate' and commit the changes" >&2
|
||||
exit 1
|
||||
@@ -44,11 +44,17 @@ jobs:
|
||||
env:
|
||||
TF_ACC: '1'
|
||||
BINARYLANE_API_TOKEN: ${{ secrets.BINARYLANE_API_TOKEN }}
|
||||
- name: Run sweepers
|
||||
if: failure()
|
||||
run: go test -v ./internal/provider/... -sweep=all
|
||||
env:
|
||||
TF_ACC: '1'
|
||||
BINARYLANE_API_TOKEN: ${{ secrets.BINARYLANE_API_TOKEN }}
|
||||
# unit:
|
||||
# name: Unit Tests
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: actions/checkout@v4
|
||||
# - uses: actions/setup-go@v5
|
||||
# with:
|
||||
# go-version-file: 'go.mod'
|
||||
|
||||
@@ -41,6 +41,12 @@ eval export $(cat .env)
|
||||
go test -v ./internal/provider/...
|
||||
```
|
||||
|
||||
To run Sweepers:
|
||||
|
||||
```sh
|
||||
go test -v ./internal/provider/... -sweep=all
|
||||
```
|
||||
|
||||
### Update modules
|
||||
|
||||
```sh
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# terraform-provider-binarylane
|
||||
|
||||
See the examples in the [examples directory](./examples/basic/README.md).
|
||||
See the [documentation on the Terraform Registry](https://registry.terraform.io/providers/oscarhermoso/binarylane/latest), or see [examples in the `examples` directory](./examples/README.md).
|
||||
|
||||
```terraform
|
||||
resource "binarylane_server" "example" {
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ description: |-
|
||||
- `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.
|
||||
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
|
||||
|
||||
### Read-Only
|
||||
|
||||
@@ -45,3 +46,11 @@ Optional:
|
||||
|
||||
- `path` (String) Leave null to accept the default '/' path.
|
||||
- `protocol` (String) Leave null to accept the default HTTP protocol.
|
||||
|
||||
|
||||
<a id="nestedatt--timeouts"></a>
|
||||
### Nested Schema for `timeouts`
|
||||
|
||||
Optional:
|
||||
|
||||
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
|
||||
@@ -18,6 +18,7 @@ Provides a Binary Lane Server resource. This can be used to create and delete se
|
||||
### Required
|
||||
|
||||
- `image` (String) The slug of the selected operating system.
|
||||
- `public_ipv4_count` (Number) The number of public IPv4 addresses to assign to the server. If this is not provided, the server will be created with the default number of public IPv4 addresses.
|
||||
- `region` (String) The slug of the selected region.
|
||||
- `size` (String) The slug of the selected size.
|
||||
|
||||
@@ -27,15 +28,23 @@ Provides a Binary Lane Server resource. This can be used to create and delete se
|
||||
- `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.
|
||||
- `public_ipv4_count` (Number) The number of public IPv4 addresses to assign to the server. If this is not provided, the server will be created with the default number of public IPv4 addresses.
|
||||
- `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) If provided this will be used to initialise the new server. This must be left null if the Image does not support UserData, see DistributionInfo.Features for more information.
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
- `wait_for_create` (Number) The number of seconds to wait for the server to be created, after which, a timeout error will be reported. If `wait_seconds` is left empty or set to 0, Terraform will succeed without waiting for the server creation to complete.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (Number) The ID of the server to fetch.
|
||||
- `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.
|
||||
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
|
||||
|
||||
<a id="nestedatt--timeouts"></a>
|
||||
### Nested Schema for `timeouts`
|
||||
|
||||
Optional:
|
||||
|
||||
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
|
||||
|
||||
@@ -11,10 +11,10 @@ provider "binarylane" {
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example-basic"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
|
||||
name = "tf-example-basic"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
public_ipv4_count = 1
|
||||
# Password will be generated automatically and mailed to the account holder
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#cloud-config
|
||||
bootcmd:
|
||||
# By default, the provider won't wait for the server installation to complete.
|
||||
#
|
||||
# If you want Terraform to wait until the server has fully provisioned, set
|
||||
# the `wait_for_create` attribute on the `binarylane_server` resource.
|
||||
#
|
||||
# If you have additional installation steps that need to be awaited, define
|
||||
# these in the `bootcmd` section and they will also be awaited.
|
||||
#
|
||||
|
||||
@@ -9,10 +9,10 @@ terraform {
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-cloud-init-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
wait_for_create = 300 # 5 mins
|
||||
name = "tf-cloud-init-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ resource "binarylane_server" "server" {
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
user_data = sensitive(data.cloudinit_config.server.rendered)
|
||||
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
|
||||
}
|
||||
|
||||
data "external" "kubeconfig" {
|
||||
@@ -107,7 +106,6 @@ resource "binarylane_server" "agent" {
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
user_data = sensitive(data.cloudinit_config.agent.rendered)
|
||||
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
|
||||
}
|
||||
|
||||
# Virtual Private Cloud Routing
|
||||
|
||||
@@ -9,10 +9,10 @@ terraform {
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-nix-example"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
wait_for_create = 300 # 5 mins
|
||||
name = "tf-nix-example"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
@@ -28,11 +28,12 @@ resource "binarylane_ssh_key" "example" {
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example-ssh"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
ssh_keys = [binarylane_ssh_key.example.id]
|
||||
name = "tf-example-ssh"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
ssh_keys = [binarylane_ssh_key.example.id]
|
||||
public_ipv4_count = 1
|
||||
|
||||
# If you are using a global default SSH key, you need to explicitly wait for
|
||||
# it to be created before creating the server.
|
||||
|
||||
@@ -6,13 +6,14 @@ 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.19.4
|
||||
github.com/hashicorp/terraform-plugin-framework v1.12.0
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.24.0
|
||||
github.com/hashicorp/terraform-plugin-docs v0.20.0
|
||||
github.com/hashicorp/terraform-plugin-framework v1.13.0
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.25.0
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.10.0
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
)
|
||||
|
||||
@@ -29,16 +30,16 @@ require (
|
||||
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.6.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/frankban/quicktest v1.14.6 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/getkin/kin-openapi v0.124.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.8 // indirect
|
||||
github.com/getkin/kin-openapi v0.127.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
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
@@ -49,15 +50,15 @@ require (
|
||||
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // 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.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.8.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.9.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.21.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.22.1 // indirect
|
||||
github.com/hashicorp/terraform-json v0.23.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
|
||||
@@ -65,7 +66,7 @@ require (
|
||||
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/invopop/yaml v0.2.0 // indirect
|
||||
github.com/invopop/yaml v0.3.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
@@ -84,6 +85,7 @@ 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/spf13/cast v1.5.1 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
@@ -93,22 +95,22 @@ require (
|
||||
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.1 // indirect
|
||||
github.com/yuin/goldmark v1.7.7 // indirect
|
||||
github.com/yuin/goldmark-meta v1.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.15.0 // indirect
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect
|
||||
google.golang.org/grpc v1.67.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -30,8 +30,8 @@ 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.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.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/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
@@ -55,26 +55,26 @@ 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.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/frankban/quicktest v1.14.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.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
|
||||
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
||||
github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY=
|
||||
github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
|
||||
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.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
|
||||
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
@@ -120,8 +120,8 @@ 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.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
|
||||
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
|
||||
github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog=
|
||||
github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q=
|
||||
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-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
@@ -129,30 +129,32 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C
|
||||
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.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI=
|
||||
github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU=
|
||||
github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE=
|
||||
github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
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.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
|
||||
github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
|
||||
github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec=
|
||||
github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A=
|
||||
github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI=
|
||||
github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
|
||||
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.19.4 h1:G3Bgo7J22OMtegIgn8Cd/CaSeyEljqjH3G39w28JK4c=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.19.4/go.mod h1:4pLASsatTmRynVzsjEhbXZ6s7xBlUw/2Kt0zfrq8HxA=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.12.0 h1:7HKaueHPaikX5/7cbC1r9d1m12iYHY+FlNZEGxQ42CQ=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.12.0/go.mod h1:N/IOQ2uYjW60Jp39Cp3mw7I/OpC/GfZ0385R0YibmkE=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 h1:bxZfGo9DIUoLLtHMElsu+zwqI4IsMZQBRRy4iLzZJ8E=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0/go.mod h1:wGeI02gEhj9nPANU62F2jCaHjXulejm/X+af4PdZaNo=
|
||||
github.com/hashicorp/terraform-plugin-go v0.24.0 h1:2WpHhginCdVhFIrWHxDEg6RBn3YaWzR2o6qUeIEat2U=
|
||||
github.com/hashicorp/terraform-plugin-go v0.24.0/go.mod h1:tUQ53lAsOyYSckFGEefGC5C8BAaO0ENqzFd3bQeuYQg=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.20.0 h1:ox7rm1FN0dVZaJBUzkVVh10R1r3+FeMQWL0QopQ9d7o=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.20.0/go.mod h1:A/+4SVMdAkQYtIBtaxV0H7AU862TxVZk/hhKaMDQB6Y=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaKFhm4h2TgvMUlNzFAtUqlcOWnWPm+9E=
|
||||
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1/go.mod h1:MsjL1sQ9L7wGwzJ5RjcI6FzEMdyoBnw+XK8ZnOvQOLY=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0 h1:RXMmu7JgpFjnI1a5QjMCBb11usrW2OtAG+iOTIj5c9Y=
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY=
|
||||
github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks=
|
||||
github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw=
|
||||
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.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE=
|
||||
@@ -173,8 +175,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
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/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
|
||||
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
|
||||
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
|
||||
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=
|
||||
@@ -221,8 +223,8 @@ 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.3.0 h1:rICjNsHbPP1LttefanBPnwsSwl09SqhCO7Ee623qR84=
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0/go.mod h1:4k+cJeSq5ntkwlcpQSxLxICCxQzCL772o30PxdibRt4=
|
||||
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.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
@@ -254,8 +256,8 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg
|
||||
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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
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/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=
|
||||
@@ -264,6 +266,8 @@ 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.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
|
||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
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/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=
|
||||
@@ -300,8 +304,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
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.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
|
||||
@@ -315,14 +319,14 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
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=
|
||||
@@ -333,8 +337,8 @@ 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
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=
|
||||
@@ -366,8 +370,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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=
|
||||
@@ -378,8 +382,8 @@ 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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
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=
|
||||
@@ -393,10 +397,10 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||
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-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
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=
|
||||
@@ -405,8 +409,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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
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=
|
||||
@@ -425,6 +429,5 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package binarylane
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
func NewClientWithAuth(endpoint string, token string) (*ClientWithResponses, error) {
|
||||
if token == "" {
|
||||
return nil, errors.New("missing or empty value for the Binary Lane API " +
|
||||
"token. Set the `api_token` value in the configuration or use the " +
|
||||
"BINARYLANE_API_TOKEN environment variable. If either is already set, " +
|
||||
"ensure the value is not empty")
|
||||
}
|
||||
|
||||
auth, err := securityprovider.NewSecurityProviderBearerToken(token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create API client with supplied API token: %w", err)
|
||||
}
|
||||
|
||||
if endpoint == "" {
|
||||
endpoint = "https://api.binarylane.com.au/v2"
|
||||
}
|
||||
|
||||
client, err := NewClientWithResponses(
|
||||
endpoint,
|
||||
WithRequestEditorFn(func(ctx context.Context, req *http.Request) error {
|
||||
dump, err := httputil.DumpRequestOut(req, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("%q\n", dump))
|
||||
return nil
|
||||
}),
|
||||
WithRequestEditorFn(auth.Intercept), // include auth AFTER the request logger
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Binary Lane API client: %w", err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package binarylane provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT.
|
||||
package binarylane
|
||||
|
||||
import (
|
||||
|
||||
@@ -39,4 +39,5 @@ cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.source_addresse
|
||||
## 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.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
|
||||
|
||||
@@ -11845,7 +11845,10 @@
|
||||
}
|
||||
],
|
||||
"description": "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer.",
|
||||
"readOnly": true
|
||||
"readOnly": true,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "health_check"
|
||||
}
|
||||
},
|
||||
"region": {
|
||||
"allOf": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package binarylane provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT.
|
||||
package binarylane
|
||||
|
||||
import (
|
||||
@@ -1860,7 +1860,7 @@ type LoadBalancer struct {
|
||||
ForwardingRules *[]ForwardingRule `json:"forwarding_rules,omitempty"`
|
||||
|
||||
// HealthCheck The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer.
|
||||
HealthCheck *HealthCheck `json:"health_check,omitempty"`
|
||||
HealthCheck *HealthCheck `json:"health_check,omitempty" tfsdk:"health_check"`
|
||||
|
||||
// Id The ID of the load balancer.
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
|
||||
@@ -95,7 +95,7 @@ func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRe
|
||||
|
||||
diags := SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if diags.HasError() {
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
@@ -37,6 +38,7 @@ type loadBalancerResource struct {
|
||||
|
||||
type loadBalancerResourceModel struct {
|
||||
resources.LoadBalancerModel
|
||||
Timeouts timeouts.Value `tfsdk:"timeouts"`
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
@@ -86,6 +88,11 @@ func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRe
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
}
|
||||
|
||||
resp.Schema.Attributes["timeouts"] =
|
||||
timeouts.Attributes(ctx, timeouts.Opts{
|
||||
Create: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
@@ -137,18 +144,17 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
tflog.Info(ctx, fmt.Sprintf("Attempted to create new load balancer: request=%+v", body))
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating load balancer",
|
||||
fmt.Sprintf("Received %s creating new load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body))
|
||||
"Unexpected status code creating load balancer",
|
||||
fmt.Sprintf("Received %s creating new load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags = SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if diags.HasError() {
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -170,31 +176,43 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
|
||||
return
|
||||
}
|
||||
|
||||
timeLimit := time.Now().Add(time.Duration(60 * time.Second))
|
||||
for {
|
||||
tflog.Info(ctx, "Waiting for load balancer to be ready...")
|
||||
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
|
||||
|
||||
readyResp, err := r.bc.client.GetActionsActionIdWithResponse(ctx, createActionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for load balancer to be ready", err.Error())
|
||||
return
|
||||
}
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
|
||||
tflog.Info(ctx, "Load balancer is ready")
|
||||
break
|
||||
}
|
||||
if time.Now().After(timeLimit) {
|
||||
retryLoop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if lastReadyResp == nil {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(
|
||||
"Timed out waiting for load balancer to be ready",
|
||||
"Timed out waiting for load balancer to be created",
|
||||
fmt.Sprintf(
|
||||
"Timed out waiting for load balancer to be created, as `wait_for_create` was surpassed without "+
|
||||
"recieving a `completed_at` in response: name=%s, status=%s, details: %s",
|
||||
data.Name.ValueString(), readyResp.Status(), readyResp.Body,
|
||||
"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))
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("Waiting for load balancer to be ready: name=%s, status=%s, details: %s", data.Name.ValueString(), readyResp.Status(), readyResp.Body))
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
@@ -229,7 +247,7 @@ func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadReques
|
||||
|
||||
diags := SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if diags.HasError() {
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -293,7 +311,7 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
|
||||
|
||||
diags = SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if diags.HasError() {
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
@@ -25,25 +32,25 @@ func TestLoadBalancerResource(t *testing.T) {
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
count = 2
|
||||
name = "tf-test-lb-server-${count.index}"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
wait_for_create = 60
|
||||
count = 2
|
||||
name = "tf-test-lb-server-${count.index}"
|
||||
region = "per"
|
||||
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]
|
||||
forwarding_rules = [{ entry_protocol = "http" }]
|
||||
name = "tf-test-lb"
|
||||
server_ids = [binarylane_server.test.0.id, binarylane_server.test.1.id]
|
||||
forwarding_rules = [{ entry_protocol = "http" }]
|
||||
}
|
||||
|
||||
data "binarylane_load_balancer" "test" {
|
||||
depends_on = [binarylane_load_balancer.test]
|
||||
|
||||
id = binarylane_load_balancer.test.id
|
||||
id = binarylane_load_balancer.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
@@ -84,3 +91,69 @@ data "binarylane_load_balancer" "test" {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("load_balancer", &resource.Sweeper{
|
||||
Name: "load_balancer",
|
||||
Dependencies: []string{"server"},
|
||||
F: func(_ string) error {
|
||||
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
endpoint = "https://api.binarylane.com.au/v2"
|
||||
}
|
||||
token := os.Getenv("BINARYLANE_API_TOKEN")
|
||||
|
||||
client, err := binarylane.NewClientWithAuth(
|
||||
endpoint,
|
||||
token,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var page int32 = 1
|
||||
perPage := int32(200)
|
||||
var nextPage bool = true
|
||||
|
||||
for nextPage {
|
||||
params := binarylane.GetLoadBalancersParams{
|
||||
Page: &page,
|
||||
PerPage: &perPage,
|
||||
}
|
||||
|
||||
lbResp, err := client.GetLoadBalancersWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting load balancers for test sweep: %w", err)
|
||||
}
|
||||
|
||||
if lbResp.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("Unexpected status code getting load balancers for test sweep: %s", lbResp.Body)
|
||||
}
|
||||
|
||||
loadBalancers := *lbResp.JSON200.LoadBalancers
|
||||
for _, lb := range loadBalancers {
|
||||
if strings.HasPrefix(*lb.Name, "tf-test-") {
|
||||
lbResp, err := client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, *lb.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting load balancer %d for test sweep: %w", *lb.Id, err)
|
||||
}
|
||||
if lbResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting load balancer %d in test sweep: %s", lbResp.StatusCode(), *lb.Id, lbResp.Body)
|
||||
}
|
||||
log.Println("Deleted load balancer during test sweep:", *lb.Id)
|
||||
}
|
||||
}
|
||||
if lbResp.JSON200.Links == nil || lbResp.JSON200.Links.Pages == nil || lbResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ import (
|
||||
"os"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
@@ -67,60 +65,24 @@ func (p *binarylaneProvider) Configure(ctx context.Context, req provider.Configu
|
||||
return
|
||||
}
|
||||
|
||||
if config.Endpoint.IsUnknown() {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
path.Root("api_endpoint"),
|
||||
"Unknown Binary Lane API endpoint",
|
||||
"The provider cannot create the Binary Lane API client as there is an unknown configuration value for the "+
|
||||
"Binary Lane API endpoint. Either target apply the source of the value first, set the value statically in "+
|
||||
"the configuration, or use the BINARYLANE_API_ENDPOINT environment variable.",
|
||||
)
|
||||
}
|
||||
if config.Token.IsUnknown() {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
path.Root("api_token"),
|
||||
"Unknown Binary Lane token",
|
||||
"The provider cannot create the Binary Lane API client as there is an unknown configuration value for the "+
|
||||
"Binary Lane API token. Either target apply the source of the value first, set the value statically in the "+
|
||||
"configuration, or use the BINARYLANE_API_TOKEN environment variable.",
|
||||
)
|
||||
}
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Default values to environment variables, but override
|
||||
// with Terraform configuration value if set.
|
||||
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
|
||||
token := os.Getenv("BINARYLANE_API_TOKEN")
|
||||
if !config.Endpoint.IsNull() {
|
||||
endpoint = config.Endpoint.ValueString()
|
||||
}
|
||||
if !config.Token.IsNull() {
|
||||
token = config.Token.ValueString()
|
||||
}
|
||||
endpoint := config.Endpoint.ValueString()
|
||||
if endpoint == "" {
|
||||
endpoint = "https://api.binarylane.com.au/v2"
|
||||
}
|
||||
if token == "" {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
path.Root("api_token"),
|
||||
"Missing Binary Lane API token",
|
||||
"The provider cannot create the Binary Lane API client as there is a missing or empty value for the Binary "+
|
||||
"Lane API token. Set the token value in the configuration or use the BINARYLANE_API_TOKEN environment "+
|
||||
"variable. If either is already set, ensure the value is not empty.",
|
||||
)
|
||||
}
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
auth, err := securityprovider.NewSecurityProviderBearerToken(token)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to create security provider with supplied token", err.Error())
|
||||
return
|
||||
endpoint = os.Getenv("BINARYLANE_API_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
endpoint = "https://api.binarylane.com.au/v2"
|
||||
}
|
||||
}
|
||||
|
||||
client, err := binarylane.NewClientWithResponses(endpoint, binarylane.WithRequestEditorFn(auth.Intercept))
|
||||
token := config.Token.ValueString()
|
||||
if token == "" {
|
||||
token = os.Getenv("BINARYLANE_API_TOKEN")
|
||||
}
|
||||
|
||||
client, err := binarylane.NewClientWithAuth(
|
||||
endpoint,
|
||||
token,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to create Binary Lane API client", err.Error())
|
||||
return
|
||||
|
||||
@@ -11,6 +11,9 @@ provider "binarylane" {
|
||||
# api_token = "test123"
|
||||
# api_endpoint = "https://test.binarylane.internal.au/v2"
|
||||
}
|
||||
|
||||
# End of provider config
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -21,27 +21,28 @@ func TestServerFirewallRulesResource(t *testing.T) {
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 0
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server.test.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow SSH"
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["22"]
|
||||
action = "accept"
|
||||
}
|
||||
]
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow SSH"
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["22"]
|
||||
action = "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
data "binarylane_server_firewall_rules" "test" {
|
||||
@@ -95,27 +96,28 @@ data "binarylane_server_firewall_rules" "test" {
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 0
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server.test.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow HTTP" # Updated description
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["80"] # Updated port
|
||||
action = "accept"
|
||||
},
|
||||
]
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow HTTP" # Updated description
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["80"] # Updated port
|
||||
action = "accept"
|
||||
},
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
|
||||
@@ -10,14 +10,16 @@ import (
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int32validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int32default"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
@@ -32,6 +34,7 @@ var (
|
||||
_ resource.Resource = &serverResource{}
|
||||
_ resource.ResourceWithConfigure = &serverResource{}
|
||||
_ resource.ResourceWithImportState = &serverResource{}
|
||||
_ resource.ResourceWithModifyPlan = &serverResource{}
|
||||
)
|
||||
|
||||
// Helper function to simplify the provider implementation.
|
||||
@@ -45,12 +48,13 @@ type serverResource struct {
|
||||
|
||||
type serverModel struct {
|
||||
resources.ServerModel
|
||||
WaitForCreateSeconds types.Int32 `tfsdk:"wait_for_create"`
|
||||
PublicIpv4Count types.Int32 `tfsdk:"public_ipv4_count"`
|
||||
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
|
||||
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
|
||||
Permalink types.String `tfsdk:"permalink"`
|
||||
Password types.String `tfsdk:"password"`
|
||||
PublicIpv4Count types.Int32 `tfsdk:"public_ipv4_count"`
|
||||
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
|
||||
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
|
||||
Permalink types.String `tfsdk:"permalink"`
|
||||
Password types.String `tfsdk:"password"`
|
||||
PasswordChangeSupported types.Bool `tfsdk:"password_change_supported"`
|
||||
Timeouts timeouts.Value `tfsdk:"timeouts"`
|
||||
}
|
||||
|
||||
func (d *serverResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
@@ -93,13 +97,27 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
},
|
||||
}
|
||||
|
||||
image := resp.Schema.Attributes["image"]
|
||||
resp.Schema.Attributes["image"] = &schema.StringAttribute{
|
||||
Description: image.GetDescription(),
|
||||
MarkdownDescription: image.GetMarkdownDescription(),
|
||||
Required: image.IsRequired(),
|
||||
Optional: image.IsOptional(),
|
||||
Computed: image.IsComputed(),
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, // Requires replacement if the image changes, TODO (#37)
|
||||
}
|
||||
|
||||
backups := resp.Schema.Attributes["backups"]
|
||||
resp.Schema.Attributes["backups"] = &schema.BoolAttribute{
|
||||
Description: backups.GetDescription(),
|
||||
MarkdownDescription: backups.GetMarkdownDescription(),
|
||||
Optional: backups.IsOptional(),
|
||||
Computed: backups.IsComputed(),
|
||||
Default: booldefault.StaticBool(false), // Add default to backups
|
||||
Default: booldefault.StaticBool(false), // Add default to backups
|
||||
PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()}, // TODO (#37)
|
||||
}
|
||||
|
||||
user_data := resp.Schema.Attributes["user_data"]
|
||||
@@ -119,6 +137,7 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Optional: vpcId.IsOptional(),
|
||||
Computed: false, // VPC ID is not computed, defined at creation
|
||||
PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()},
|
||||
}
|
||||
|
||||
portBlocking := resp.Schema.Attributes["port_blocking"]
|
||||
@@ -128,6 +147,7 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
Optional: portBlocking.IsOptional(),
|
||||
Computed: portBlocking.IsComputed(),
|
||||
Default: booldefault.StaticBool(true), // Add default to port_blocking
|
||||
PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()},
|
||||
}
|
||||
|
||||
sshKeys := resp.Schema.Attributes["ssh_keys"]
|
||||
@@ -154,16 +174,7 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
Optional: true, // Password optional, if not set will be emailed to user
|
||||
Computed: false, // Computed must be false to allow server to be created without password
|
||||
Sensitive: true, // Mark password as sensitive
|
||||
}
|
||||
|
||||
waitDescription := "The number of seconds to wait for the server to be created, after which, a timeout error will " +
|
||||
"be reported. If `wait_seconds` is left empty or set to 0, Terraform will succeed without waiting for the " +
|
||||
"server creation to complete."
|
||||
resp.Schema.Attributes["wait_for_create"] = &schema.Int32Attribute{
|
||||
Description: waitDescription,
|
||||
MarkdownDescription: waitDescription,
|
||||
Optional: true,
|
||||
Computed: false,
|
||||
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
|
||||
}
|
||||
|
||||
publicIpv4CountDescription := "The number of public IPv4 addresses to assign to the server. If this is not provided, the " +
|
||||
@@ -171,9 +182,10 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
resp.Schema.Attributes["public_ipv4_count"] = &schema.Int32Attribute{
|
||||
Description: publicIpv4CountDescription,
|
||||
MarkdownDescription: publicIpv4CountDescription,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Default: int32default.StaticInt32(1),
|
||||
Required: true,
|
||||
Optional: false,
|
||||
Computed: false,
|
||||
// Default: int32default.StaticInt32(0), // TODO: Uncomment with 1.0 release (see issue #30)
|
||||
Validators: []validator.Int32{
|
||||
int32validator.AtLeast(0),
|
||||
},
|
||||
@@ -188,9 +200,6 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.List{
|
||||
listplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
}
|
||||
|
||||
privateIpv4AddressesDescription := "The private IPv4 addresses assigned to the server."
|
||||
@@ -214,7 +223,67 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
}
|
||||
|
||||
pwChangeDescription := "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."
|
||||
resp.Schema.Attributes["password_change_supported"] = &schema.BoolAttribute{
|
||||
Description: pwChangeDescription,
|
||||
MarkdownDescription: pwChangeDescription,
|
||||
// read only
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Bool{
|
||||
boolplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
}
|
||||
|
||||
resp.Schema.Attributes["timeouts"] =
|
||||
timeouts.Attributes(ctx, timeouts.Opts{
|
||||
Create: true,
|
||||
Update: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
|
||||
// Creation or destruction plan
|
||||
if req.Plan.Raw.IsNull() || req.State.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
var plan, state serverModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// When IP count is changed, plan should show addition/removal of public IPs
|
||||
ipv4CountPlan := plan.PublicIpv4Count.ValueInt32()
|
||||
ipv4CountState := state.PublicIpv4Count.ValueInt32()
|
||||
if ipv4CountPlan != ipv4CountState {
|
||||
currentIps := []*string{}
|
||||
diags := state.PublicIpv4Addresses.ElementsAs(ctx, ¤tIps, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
if ipv4CountPlan < ipv4CountState {
|
||||
plan.PublicIpv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, currentIps[:ipv4CountPlan+1])
|
||||
resp.Diagnostics.Append(diags...)
|
||||
} else {
|
||||
plan.PublicIpv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, append(currentIps, make([]*string, ipv4CountPlan-ipv4CountState)...))
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
@@ -255,6 +324,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
data.Password = types.StringNull()
|
||||
} else {
|
||||
body.Password = data.Password.ValueStringPointer()
|
||||
ctx = tflog.MaskMessageStrings(ctx, data.Password.String())
|
||||
}
|
||||
|
||||
serverResp, err := r.bc.client.PostServersWithResponse(ctx, body)
|
||||
@@ -265,11 +335,11 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server",
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), serverResp.Body))
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), serverResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -282,6 +352,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
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.PasswordChangeSupported = types.BoolValue(*serverResp.JSON200.Server.PasswordChangeSupported)
|
||||
|
||||
var publicIpv4Addresses []string
|
||||
var privateIpv4Addresses []string
|
||||
@@ -301,10 +372,6 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
if data.WaitForCreateSeconds.ValueInt32() <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for server to be ready
|
||||
var createActionId int64
|
||||
for _, action := range *serverResp.JSON200.Links.Actions {
|
||||
@@ -320,31 +387,43 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
return
|
||||
}
|
||||
|
||||
timeLimit := time.Now().Add(time.Duration(data.WaitForCreateSeconds.ValueInt32()) * time.Second)
|
||||
for {
|
||||
tflog.Info(ctx, "Waiting for server to be ready...")
|
||||
createTimeout, diags := data.Timeouts.Create(ctx, 20*time.Minute)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
ctx, cancel := context.WithTimeout(ctx, createTimeout)
|
||||
defer cancel()
|
||||
var lastReadyResp *binarylane.GetServersServerIdActionsActionIdResponse
|
||||
|
||||
readyResp, err := r.bc.client.GetServersServerIdActionsActionIdWithResponse(ctx, data.Id.ValueInt64(), createActionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for server to be ready", err.Error())
|
||||
return
|
||||
}
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
|
||||
tflog.Info(ctx, "Server is ready")
|
||||
break
|
||||
}
|
||||
if time.Now().After(timeLimit) {
|
||||
retryLoop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if lastReadyResp == nil {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(
|
||||
"Timed out waiting for server to be ready",
|
||||
"Timed out waiting for server to be created",
|
||||
fmt.Sprintf(
|
||||
"Timed out waiting for server to be created, as `wait_for_create` was surpassed without "+
|
||||
"recieving a `completed_at` in response: name=%s, status=%s, details: %s",
|
||||
data.Name.ValueString(), readyResp.Status(), readyResp.Body,
|
||||
"Timed out waiting for server %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.GetServersServerIdActionsActionIdWithResponse(ctx, data.Id.ValueInt64(), createActionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for server 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 server to to be created: name=%s, status=%s, details: %s", data.Name.ValueString(), readyResp.Status(), readyResp.Body))
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("Waiting for server to be ready: name=%s, status=%s, details: %s", data.Name.ValueString(), readyResp.Status(), readyResp.Body))
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
@@ -388,6 +467,7 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
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.PasswordChangeSupported = types.BoolValue(*serverResp.JSON200.Server.PasswordChangeSupported)
|
||||
|
||||
var publicIpv4Addresses []string
|
||||
var privateIpv4Addresses []string
|
||||
@@ -399,6 +479,7 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
|
||||
}
|
||||
}
|
||||
data.PublicIpv4Count = types.Int32Value(int32(len(publicIpv4Addresses)))
|
||||
data.PublicIpv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
data.PrivateIPv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
|
||||
@@ -413,20 +494,180 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
}
|
||||
|
||||
func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data serverModel
|
||||
var plan, state serverModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
// TODO
|
||||
createTimeout, diags := plan.Timeouts.Update(ctx, 20*time.Minute)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
ctx, cancel := context.WithTimeout(ctx, createTimeout)
|
||||
defer cancel()
|
||||
|
||||
// Rename operation
|
||||
if !plan.Name.Equal(state.Name) {
|
||||
renameResp, err := r.bc.client.PostServersServerIdActionsRenameWithResponse(
|
||||
ctx,
|
||||
state.Id.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsRenameJSONRequestBody{
|
||||
Type: "rename",
|
||||
Name: plan.Name.ValueString(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error renaming server: server_id=%s", state.Id.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if renameResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code renaming server",
|
||||
fmt.Sprintf("Received %s renaming server: server_id=%s. Details: %s", renameResp.Status(), state.Id.String(), renameResp.Body))
|
||||
return
|
||||
}
|
||||
if *renameResp.JSON200.Action.Status == "errored" {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected response with \"errored\" status when renaming server",
|
||||
fmt.Sprintf("Received %s renaming server: server_id=%s. Details: %s", renameResp.Status(), state.Id.String(), renameResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO - Currently, the API does not support polling for the rename to complete, because the action ID returns a 404 response (see #13)
|
||||
|
||||
state.Name = types.StringValue(plan.Name.ValueString())
|
||||
}
|
||||
|
||||
// Resize operation
|
||||
var resizeReq *binarylane.PostServersServerIdActionsResizeJSONRequestBody
|
||||
|
||||
if !plan.Size.Equal(state.Size) {
|
||||
resizeReq = &binarylane.PostServersServerIdActionsResizeJSONRequestBody{
|
||||
Type: "resize",
|
||||
}
|
||||
resizeReq.Size = plan.Size.ValueStringPointer()
|
||||
}
|
||||
|
||||
if !plan.PublicIpv4Count.Equal(state.PublicIpv4Count) {
|
||||
if resizeReq == nil {
|
||||
resizeReq = &binarylane.PostServersServerIdActionsResizeJSONRequestBody{
|
||||
Type: "resize",
|
||||
}
|
||||
}
|
||||
|
||||
resizeReq.Options = &binarylane.ChangeSizeOptionsRequest{
|
||||
Ipv4Addresses: plan.PublicIpv4Count.ValueInt32Pointer(),
|
||||
}
|
||||
|
||||
if plan.PublicIpv4Count.ValueInt32() < state.PublicIpv4Count.ValueInt32() {
|
||||
currentIps := []string{}
|
||||
diags := state.PublicIpv4Addresses.ElementsAs(ctx, ¤tIps, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
removedIps := currentIps[plan.PublicIpv4Count.ValueInt32():state.PublicIpv4Count.ValueInt32()]
|
||||
resizeReq.Options.Ipv4AddressesToRemove = &removedIps
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if resizeReq != nil {
|
||||
tflog.Info(ctx, fmt.Sprintf("Resizing server: server_id=%s", state.Id.String()))
|
||||
resizeResp, err := r.bc.client.PostServersServerIdActionsResizeWithResponse(
|
||||
ctx,
|
||||
state.Id.ValueInt64(),
|
||||
*resizeReq,
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error resizing server: server_id=%s", state.Id.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if resizeResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code resizing server",
|
||||
fmt.Sprintf("Received %s resizing server: server_id=%s. Details: %s", resizeResp.Status(), state.Id.String(), resizeResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
var lastReadyResp *binarylane.GetServersServerIdActionsActionIdResponse
|
||||
retryLoop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if lastReadyResp == nil {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError(
|
||||
"Timed out waiting for server to be resized",
|
||||
fmt.Sprintf(
|
||||
"Timed out waiting for server %s to be resized, last response was status=%s, body: %s",
|
||||
plan.Name.ValueString(),
|
||||
lastReadyResp.Status(),
|
||||
lastReadyResp.Body,
|
||||
),
|
||||
)
|
||||
return
|
||||
default:
|
||||
readyResp, err := r.bc.client.GetServersServerIdActionsActionIdWithResponse(ctx, state.Id.ValueInt64(), *resizeResp.JSON200.Action.Id)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for server to be resized", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
|
||||
// success
|
||||
state.Size = types.StringValue(plan.Size.ValueString())
|
||||
state.PublicIpv4Count = types.Int32Value(plan.PublicIpv4Count.ValueInt32())
|
||||
state.PublicIpv4Addresses, diags = plan.PublicIpv4Addresses.ToListValue(ctx)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
if state.PublicIpv4Addresses.IsUnknown() {
|
||||
// New IPs may have been allocated, so we need to check the server again
|
||||
serverResp, err := r.bc.client.GetServersServerIdWithResponse(ctx, state.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error checking IPs for server after resize", err.Error())
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError("Unexpected HTTP status code checking IPs for server after resize",
|
||||
fmt.Sprintf("Received %s checking IPs for server after resize: name=%s. Details: %s", serverResp.Status(), state.Name.ValueString(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
var publicIpv4Addresses []string
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
if v4address.Type == "public" {
|
||||
publicIpv4Addresses = append(publicIpv4Addresses, v4address.IpAddress)
|
||||
}
|
||||
}
|
||||
state.PublicIpv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
break retryLoop
|
||||
}
|
||||
|
||||
lastReadyResp = readyResp
|
||||
tflog.Debug(ctx, fmt.Sprintf("Waiting for server to be resized: name=%s, status=%s, details: %s", state.Name.ValueString(), readyResp.Status(), readyResp.Body))
|
||||
}
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
@@ -24,9 +31,10 @@ func TestServerResource(t *testing.T) {
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "test" {
|
||||
@@ -42,24 +50,24 @@ resource "binarylane_ssh_key" "unused" {
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
public_ipv4_count = 0
|
||||
user_data = <<EOT
|
||||
name = "tf-test-server-resource"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
public_ipv4_count = 1
|
||||
ssh_keys = [binarylane_ssh_key.test.id]
|
||||
user_data = <<EOT
|
||||
#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
EOT
|
||||
ssh_keys = [binarylane_ssh_key.test.id]
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
@@ -70,12 +78,12 @@ data "binarylane_server" "test" {
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-12"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", `#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
`),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "private_ipv4_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "port_blocking", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"), // Only the test SSH key should be registered
|
||||
@@ -96,7 +104,7 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
ResourceName: "binarylane_server.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "user_data", "wait_for_create", "public_ipv4_count"},
|
||||
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "user_data", "timeouts"},
|
||||
},
|
||||
// Test import by name
|
||||
{
|
||||
@@ -104,8 +112,120 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
ImportState: true,
|
||||
ImportStateId: "tf-test-server-resource",
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "user_data", "wait_for_create", "public_ipv4_count"},
|
||||
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "user_data", "timeouts"},
|
||||
},
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
public_key = "` + GeneratePublicKey(t) + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "unused" {
|
||||
name = "tf-test-server-resource-unused"
|
||||
public_key = "` + GeneratePublicKey(t) + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource-2"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-1vcpu"
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
public_ipv4_count = 0
|
||||
ssh_keys = [binarylane_ssh_key.test.id]
|
||||
user_data = <<EOT
|
||||
#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
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", "public_ipv4_count", "0"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("server", &resource.Sweeper{
|
||||
Name: "server",
|
||||
F: func(_ string) error {
|
||||
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
endpoint = "https://api.binarylane.com.au/v2"
|
||||
}
|
||||
token := os.Getenv("BINARYLANE_API_TOKEN")
|
||||
|
||||
client, err := binarylane.NewClientWithAuth(
|
||||
endpoint,
|
||||
token,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var page int32 = 1
|
||||
perPage := int32(200)
|
||||
nextPage := true
|
||||
|
||||
for nextPage {
|
||||
params := binarylane.GetServersParams{
|
||||
Page: &page,
|
||||
PerPage: &perPage,
|
||||
}
|
||||
|
||||
serverResp, err := client.GetServersWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting servers for test sweep: %w", err)
|
||||
}
|
||||
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("Unexpected status code getting servers in test sweep: %s", serverResp.Body)
|
||||
}
|
||||
|
||||
servers := *serverResp.JSON200.Servers
|
||||
for _, s := range servers {
|
||||
if strings.HasPrefix(*s.Name, "tf-test-") {
|
||||
reason := "Terraform deletion"
|
||||
params := binarylane.DeleteServersServerIdParams{
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
serverResp, err := client.DeleteServersServerIdWithResponse(ctx, *s.Id, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting server %d during test sweep: %w", *s.Id, err)
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting server %d in test sweep: %s", serverResp.StatusCode(), *s.Id, serverResp.Body)
|
||||
}
|
||||
log.Println("Deleted server during test sweep:", *s.Id)
|
||||
}
|
||||
}
|
||||
if serverResp.JSON200.Links == nil || serverResp.JSON200.Links.Pages == nil || serverResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
var (
|
||||
_ resource.Resource = &sshKeyResource{}
|
||||
_ resource.ResourceWithConfigure = &sshKeyResource{}
|
||||
_ resource.ResourceWithImportState = &serverResource{}
|
||||
_ resource.ResourceWithImportState = &sshKeyResource{}
|
||||
)
|
||||
|
||||
func NewSshKeyResource() resource.Resource {
|
||||
@@ -102,7 +102,6 @@ func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
sshResp, err := r.bc.client.PostAccountKeysWithResponse(ctx, binarylane.SshKeyRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
Default: data.Default.ValueBoolPointer(),
|
||||
@@ -117,8 +116,8 @@ func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
}
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server",
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
"Unexpected HTTP status code creating SSH key",
|
||||
fmt.Sprintf("Received %s creating SSH key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -158,8 +157,8 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
}
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server",
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
"Unexpected HTTP status code getting SSH key",
|
||||
fmt.Sprintf("Received %s getting SSH key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -268,7 +267,7 @@ func (r *sshKeyResource) ImportState(
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code getting SSH key for import",
|
||||
fmt.Sprintf("Received %s reading SSH key: fingerprint=%s. Details: %s", sshResp.Status(), req.ID,
|
||||
fmt.Sprintf("Received %s getting SSH key for import: fingerprint=%s. Details: %s", sshResp.Status(), req.ID,
|
||||
sshResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
@@ -19,26 +25,27 @@ func TestSshKeyResource(t *testing.T) {
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_ssh_key" "test" {
|
||||
name = "tf_ssh_key_resource_test"
|
||||
public_key = "` + publicKey + `"
|
||||
name = "tf-test-key-resource-test"
|
||||
public_key = "` + publicKey + `"
|
||||
}
|
||||
|
||||
data "binarylane_ssh_key" "test" {
|
||||
depends_on = [binarylane_ssh_key.test]
|
||||
|
||||
id = binarylane_ssh_key.test.id
|
||||
id = binarylane_ssh_key.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf-test-key-resource-test"),
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "public_key", publicKey),
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "default", "false"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_ssh_key.test", "id"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "name", "tf-test-key-resource-test"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_ssh_key.test", "public_key"), // Ideally would check this is identical, but whitespace is not preserved
|
||||
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "false"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_ssh_key.test", "id"),
|
||||
@@ -62,14 +69,14 @@ data "binarylane_ssh_key" "test" {
|
||||
// {
|
||||
// Config: providerConfig + `
|
||||
// resource "binarylane_ssh_key" "test" {
|
||||
// name = "tf_ssh_key_resource_test"
|
||||
// name = "tf-test-key-resource-test"
|
||||
// public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"
|
||||
// default = true
|
||||
// }
|
||||
// `,
|
||||
// Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// // Verify resource values
|
||||
// resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
// resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf-test-key-resource-test"),
|
||||
// resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "true"),
|
||||
// ),
|
||||
// },
|
||||
@@ -106,3 +113,70 @@ func ImportByFingerprint(state *terraform.State) (fingerprint string, err error)
|
||||
|
||||
return rawState["fingerprint"], nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("ssh_key", &resource.Sweeper{
|
||||
Name: "ssh_key",
|
||||
F: func(_ string) error {
|
||||
endpoint := os.Getenv("BINARYLANE_API_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
endpoint = "https://api.binarylane.com.au/v2"
|
||||
}
|
||||
token := os.Getenv("BINARYLANE_API_TOKEN")
|
||||
|
||||
client, err := binarylane.NewClientWithAuth(
|
||||
endpoint,
|
||||
token,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var page int32 = 1
|
||||
perPage := int32(200)
|
||||
nextPage := true
|
||||
|
||||
for nextPage {
|
||||
params := binarylane.GetAccountKeysParams{
|
||||
Page: &page,
|
||||
PerPage: &perPage,
|
||||
}
|
||||
|
||||
keyResp, err := client.GetAccountKeysWithResponse(ctx, ¶ms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting SSH keys for test sweep: %w", err)
|
||||
}
|
||||
|
||||
if keyResp.StatusCode() != http.StatusOK {
|
||||
return fmt.Errorf("Unexpected status code getting SSH keys for test sweep: %s", keyResp.Body)
|
||||
}
|
||||
|
||||
keys := keyResp.JSON200.SshKeys
|
||||
for _, k := range keys {
|
||||
if strings.HasPrefix(*k.Name, "tf-test-") {
|
||||
|
||||
keyResp, 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)
|
||||
}
|
||||
if keyResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting SSH key %d for test sweep: %s", keyResp.StatusCode(), *k.Id, keyResp.Body)
|
||||
}
|
||||
log.Println("Deleted SSH key for test sweep:", *k.Id)
|
||||
}
|
||||
}
|
||||
if keyResp.JSON200.Links == nil || keyResp.JSON200.Links.Pages == nil || keyResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
page++
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
resource.TestMain(m)
|
||||
}
|
||||
@@ -14,30 +14,31 @@ func TestVpcResource(t *testing.T) {
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
name = "tf-test-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
data "binarylane_vpc" "test" {
|
||||
depends_on = [binarylane_vpc.test]
|
||||
|
||||
id = binarylane_vpc.test.id
|
||||
id = binarylane_vpc.test.id
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.1"
|
||||
}
|
||||
]
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
data "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc_route_entries.test.vpc_id
|
||||
vpc_id = binarylane_vpc_route_entries.test.vpc_id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
@@ -82,21 +83,23 @@ data "binarylane_vpc_route_entries" "test" {
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-vpc-renamed"
|
||||
ip_range = "10.240.0.0/16"
|
||||
name = "tf-test-vpc-renamed"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test-renamed"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.2"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test-renamed"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify binarylane_vpc resource values updated
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "name", "tf-test-vpc-renamed"), // Updated name
|
||||
|
||||
Reference in New Issue
Block a user