Compare commits

..

9 Commits

Author SHA1 Message Date
Oscar Hermoso a0a227c88c Merge pull request #102 from an9wer/server-ipv6
6️⃣ Add "ipv6" attribute to servers
2025-09-29 09:54:22 +08:00
Oscar Hermoso f8edcd0e06 Add tests, update docs, bump OpenAPI schema version 2025-09-29 09:48:25 +08:00
an9wer 04552e0fcf Add 'ipv6' attribute to servers 2025-09-28 12:53:15 +09:30
dependabot[bot] 66fcb6f85e 🐹 Bump github.com/hashicorp/terraform-plugin-testing (#99)
Bumps the minor-patch group with 1 update: [github.com/hashicorp/terraform-plugin-testing](https://github.com/hashicorp/terraform-plugin-testing).


Updates `github.com/hashicorp/terraform-plugin-testing` from 1.13.2 to 1.13.3
- [Release notes](https://github.com/hashicorp/terraform-plugin-testing/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-testing/blob/v1.13.3/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-testing/compare/v1.13.2...v1.13.3)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-testing
  dependency-version: 1.13.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 12:50:05 +08:00
dependabot[bot] 806bdebaf8 🐈 Bump goreleaser/goreleaser-action in the minor-patch group (#94)
Bumps the minor-patch group with 1 update: [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action).


Updates `goreleaser/goreleaser-action` from 6.3.0 to 6.4.0
- [Release notes](https://github.com/goreleaser/goreleaser-action/releases)
- [Commits](https://github.com/goreleaser/goreleaser-action/compare/9c156ee8a17a598857849441385a2041ef570552...e435ccd777264be153ace6237001ef4d979d3a7a)

---
updated-dependencies:
- dependency-name: goreleaser/goreleaser-action
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 12:49:33 +08:00
dependabot[bot] dd06380c63 🐈 Bump actions/checkout from 4 to 5 (#95)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 12:49:03 +08:00
Oscar Hermoso 322a8fda80 👆 Update OpenAPI schema to 0.32.1 (#97) 2025-08-18 11:21:23 +08:00
Oscar Hermoso a50daac8e9 🧨 Fix unspecified attribute for load balancer (#93)
* 🧨 Fix unspecified attribute for load balancer

* remove duplicated `timeouts` in schema, test to ensure `ip` is set
2025-08-04 12:22:24 +08:00
dependabot[bot] 424828123d 🐹 Bump github.com/hashicorp/terraform-plugin-framework (#92)
Bumps the minor-patch group with 1 update: [github.com/hashicorp/terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework).


Updates `github.com/hashicorp/terraform-plugin-framework` from 1.15.0 to 1.15.1
- [Release notes](https://github.com/hashicorp/terraform-plugin-framework/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-framework/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-framework/compare/v1.15.0...v1.15.1)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-framework
  dependency-version: 1.15.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-02 16:12:23 +08:00
32 changed files with 539 additions and 175 deletions
+7 -2
View File
@@ -23,7 +23,7 @@ jobs:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
# Allow goreleaser to access older tag information.
fetch-depth: 0
@@ -38,10 +38,15 @@ jobs:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.PASSPHRASE }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
with:
args: release --clean
env:
# GitHub sets the GITHUB_TOKEN secret automatically.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
- name: Publish Release
if: startsWith(github.ref, 'refs/tags/v')
run: gh release edit "${{ github.ref }}" --draft false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
group: test
cancel-in-progress: true # would be nice if GH offered cancel-pending instead, https://github.com/orgs/community/discussions/63136
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
@@ -54,7 +54,7 @@ jobs:
# name: Unit Tests
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: actions/checkout@v5
# - uses: actions/setup-go@v5
# with:
# go-version-file: 'go.mod'
+1 -1
View File
@@ -20,9 +20,9 @@ Planned features:
- [x] Cloud Init
- [x] Updating server properties
- [x] "Advanced Features"
- [x] IPV6
- [ ] Customised Backups
- [ ] Software
- [ ] IPV6
- [ ] Alerts
- [x] SSH Keys
- [x] Virtual Private Cloud
+7 -1
View File
@@ -29,6 +29,7 @@ data "binarylane_load_balancer" "example" {
- `forwarding_rules` (Attributes List) The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule. (see [below for nested schema](#nestedatt--forwarding_rules))
- `health_check` (Object) The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules. (see [below for nested schema](#nestedatt--health_check))
- `ip` (String) The IPv4 address of the load balancer.
- `name` (String) The hostname of the load balancer.
- `region` (String) Leave null to create an anycast load balancer.
- `server_ids` (List of Number) A list of server IDs to assign to this load balancer.
@@ -38,7 +39,12 @@ data "binarylane_load_balancer" "example" {
Read-Only:
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
- `entry_protocol` (String) The protocol that traffic must match for this load balancer to forward traffic according to this rule.
| Value | Description |
| ----- | ----------- |
| http | The load balancer will forward HTTP traffic that matches this rule. |
| https | The load balancer will forward HTTPS traffic that matches this rule. |
<a id="nestedatt--health_check"></a>
+3
View File
@@ -34,6 +34,7 @@ data "binarylane_server" "example" {
- \> 60 GB must be a multiple of 10
- \> 200 GB must be a multiple of 100
- `image` (String) The slug of the selected operating system, such as `debian-12`. You can fetch a full list of images from the BinaryLane API.
- `ipv6` (Boolean) If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled.
- `memory` (Number) The total memory in MB for this server. Leave null to accept the default size. Valid values:
- must be a multiple of 128
- \> 2048 MB must be a multiple of 1024
@@ -43,7 +44,9 @@ data "binarylane_server" "example" {
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
- `private_ipv6_addresses` (List of String) The private IPv6 addresses assigned to the server.
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
- `public_ipv6_addresses` (List of String) The public IPv6 addresses assigned to the server.
- `region` (String) The slug of the selected region.
- `size` (String) The slug of the selected size.
- `source_and_destination_check` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled (which is `true` by default), your server will only be able to send or receive packets that are directly addressed to one of the IP addresses associated with the Cloud Server. Generally, this is desirable behaviour because it prevents IP conflicts and other hard-to-diagnose networking faults due to incorrect network configuration. When `source_and_destination_check` is `false`, your Cloud Server will be able to send and receive packets addressed to any server. This is typically used when you want to use your Cloud Server as a VPN endpoint, a NAT server to provide internet access, or IP forwarding.
+7 -1
View File
@@ -46,13 +46,19 @@ resource "binarylane_load_balancer" "example" {
### Read-Only
- `id` (Number) The ID of the load balancer to fetch.
- `ip` (String) The IPv4 address of the load balancer.
<a id="nestedatt--forwarding_rules"></a>
### Nested Schema for `forwarding_rules`
Required:
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
- `entry_protocol` (String) The protocol that traffic must match for this load balancer to forward traffic according to this rule.
| Value | Description |
| ----- | ----------- |
| http | The load balancer will forward HTTP traffic that matches this rule. |
| https | The load balancer will forward HTTPS traffic that matches this rule. |
<a id="nestedatt--health_check"></a>
+3
View File
@@ -44,6 +44,7 @@ resource "binarylane_server" "example" {
- must be a multiple of 5
- \> 60 GB must be a multiple of 10
- \> 200 GB must be a multiple of 100
- `ipv6` (Boolean) If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled.
- `memory` (Number) The total memory in MB for this server. Leave null to accept the default size. Valid values:
- must be a multiple of 128
- \> 2048 MB must be a multiple of 1024
@@ -64,7 +65,9 @@ resource "binarylane_server" "example" {
- `password_change_supported` (Boolean) If this is true then the `password` attribute can be changed with Terraform. If this is false then the `password` attribute can only be replaced with a null/empty value, which will clear the root/administrator password allowing the password to be changed via the web console.
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
- `private_ipv6_addresses` (List of String) The private IPv6 addresses assigned to the server.
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
- `public_ipv6_addresses` (List of String) The public IPv6 addresses assigned to the server.
<a id="nestedatt--advanced_features"></a>
### Nested Schema for `advanced_features`
+2 -2
View File
@@ -3,10 +3,10 @@ bootcmd:
# If you have additional installation steps that need to be awaited, define
# these in the `bootcmd` section and they will also be awaited.
#
- [cloud-init-per, once, 'example-installing', sh, -c, 'sleep 60'] # pretend this is a slow installation step
- [cloud-init-per, instance, 'example-installing', sh, -c, 'sleep 60'] # pretend this is a slow installation step
- [
cloud-init-per,
once,
instance,
'example-done',
sh,
-c,
+4 -4
View File
@@ -9,12 +9,12 @@ require (
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.22.0
github.com/hashicorp/terraform-plugin-framework v1.15.0
github.com/hashicorp/terraform-plugin-framework v1.15.1
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0
github.com/hashicorp/terraform-plugin-go v0.28.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.13.2
github.com/hashicorp/terraform-plugin-testing v1.13.3
github.com/oapi-codegen/oapi-codegen/v2 v2.5.0
github.com/oapi-codegen/runtime v1.1.2
)
@@ -112,8 +112,8 @@ require (
golang.org/x/text v0.27.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 // indirect
google.golang.org/grpc v1.74.2 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+20 -20
View File
@@ -71,8 +71,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
@@ -151,8 +151,8 @@ github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 h1:91dQG1A/DxP6vRz9Giy
github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0/go.mod h1:fywrEKpordQypmAjz/HIfm2LuNVmyJ6KDe8XT9GdJxQ=
github.com/hashicorp/terraform-plugin-docs v0.22.0 h1:fwIDStbFel1PPNkM+mDPnpB4efHZBdGoMz/zt5FbTDw=
github.com/hashicorp/terraform-plugin-docs v0.22.0/go.mod h1:55DJVyZ7BNK4t/lANcQ1YpemRuS6KsvIO1BbGA+xzGE=
github.com/hashicorp/terraform-plugin-framework v1.15.0 h1:LQ2rsOfmDLxcn5EeIwdXFtr03FVsNktbbBci8cOKdb4=
github.com/hashicorp/terraform-plugin-framework v1.15.0/go.mod h1:hxrNI/GY32KPISpWqlCoTLM9JZsGH3CyYlir09bD/fI=
github.com/hashicorp/terraform-plugin-framework v1.15.1 h1:2mKDkwb8rlx/tvJTlIcpw0ykcmvdWv+4gY3SIgk8Pq8=
github.com/hashicorp/terraform-plugin-framework v1.15.1/go.mod h1:hxrNI/GY32KPISpWqlCoTLM9JZsGH3CyYlir09bD/fI=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0 h1:I/N0g/eLZ1ZkLZXUQ0oRSXa8YG/EF0CEuQP1wXdrzKw=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.5.0/go.mod h1:t339KhmxnaF4SzdpxmqW8HnQBHVGYazwtfxU0qCs4eE=
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0 h1:OQnlOt98ua//rCw+QhBbSqfW3QbwtVrcdWeQN5gI3Hw=
@@ -163,8 +163,8 @@ github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9T
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 h1:NFPMacTrY/IdcIcnUB+7hsore1ZaRWU9cnB6jFoBnIM=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0/go.mod h1:QYmYnLfsosrxjCnGY1p9c7Zj6n9thnEE+7RObeYs3fA=
github.com/hashicorp/terraform-plugin-testing v1.13.2 h1:mSotG4Odl020vRjIenA3rggwo6Kg6XCKIwtRhYgp+/M=
github.com/hashicorp/terraform-plugin-testing v1.13.2/go.mod h1:WHQ9FDdiLoneey2/QHpGM/6SAYf4A7AZazVg7230pLE=
github.com/hashicorp/terraform-plugin-testing v1.13.3 h1:QLi/khB8Z0a5L54AfPrHukFpnwsGL8cwwswj4RZduCo=
github.com/hashicorp/terraform-plugin-testing v1.13.3/go.mod h1:WHQ9FDdiLoneey2/QHpGM/6SAYf4A7AZazVg7230pLE=
github.com/hashicorp/terraform-registry-address v0.3.0 h1:HMpK3nqaGFPS9VmgRXrJL/dzHNdheGVKk5k7VlFxzCo=
github.com/hashicorp/terraform-registry-address v0.3.0/go.mod h1:jRGCMiLaY9zii3GLC7hqpSnwhfnCN5yzvY0hh4iCGbM=
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
@@ -323,16 +323,16 @@ go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ
go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -414,10 +414,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-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
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=
+65 -23
View File
@@ -1189,10 +1189,10 @@ type ConsoleResponse struct {
// CreateLoadBalancerRequest defines model for CreateLoadBalancerRequest.
type CreateLoadBalancerRequest struct {
// ForwardingRules The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule.
ForwardingRules *[]ForwardingRule `json:"forwarding_rules" tfsdk:"forwarding_rules"`
ForwardingRules *[]ForwardingRuleRequest `json:"forwarding_rules" tfsdk:"forwarding_rules"`
// HealthCheck 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.
HealthCheck *HealthCheck `json:"health_check"`
HealthCheck *HealthCheckRequest `json:"health_check"`
// Name The hostname of the load balancer.
Name string `json:"name"`
@@ -1603,14 +1603,50 @@ type ForwardingRule struct {
EntryProtocol LoadBalancerRuleProtocol `json:"entry_protocol" tfsdk:"entry_protocol"`
}
// ForwardingRuleRequest defines model for ForwardingRuleRequest.
type ForwardingRuleRequest struct {
// EntryProtocol The protocol that traffic must match for this load balancer to forward traffic according to this rule.
//
// | Value | Description |
// | ----- | ----------- |
// | http | The load balancer will forward HTTP traffic that matches this rule. |
// | https | The load balancer will forward HTTPS traffic that matches this rule. |
//
EntryProtocol LoadBalancerRuleProtocol `json:"entry_protocol" tfsdk:"entry_protocol"`
}
// ForwardingRulesRequest defines model for ForwardingRulesRequest.
type ForwardingRulesRequest struct {
// ForwardingRules The rules that control which traffic the load balancer will forward to servers in the pool.
ForwardingRules []ForwardingRule `json:"forwarding_rules"`
ForwardingRules []ForwardingRuleRequest `json:"forwarding_rules"`
}
// HealthCheck defines model for HealthCheck.
type HealthCheck struct {
// Path The path to the health check endpoint.
Path string `json:"path"`
// Protocol The protocol used for the health check.
//
// | Value | Description |
// | ----- | ----------- |
// | http | The health check will be performed via HTTP. |
// | https | The health check will be performed via HTTPS. |
// | both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |
//
Protocol HealthCheckProtocol `json:"protocol"`
}
// HealthCheckProtocol
// | Value | Description |
// | ----- | ----------- |
// | http | The health check will be performed via HTTP. |
// | https | The health check will be performed via HTTPS. |
// | both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |
type HealthCheckProtocol = string
// HealthCheckRequest defines model for HealthCheckRequest.
type HealthCheckRequest struct {
// Path Leave null to accept the default '/' path.
Path *string `json:"path"`
@@ -1625,14 +1661,6 @@ type HealthCheck struct {
Protocol *HealthCheckProtocol `json:"protocol"`
}
// HealthCheckProtocol
// | Value | Description |
// | ----- | ----------- |
// | http | The health check will be performed via HTTP. |
// | https | The health check will be performed via HTTPS. |
// | both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |
type HealthCheckProtocol = string
// Host defines model for Host.
type Host struct {
// DisplayName The name for this host. If this is a dedicated host this property will be empty.
@@ -1887,6 +1915,11 @@ type License struct {
// LicensedSoftware defines model for LicensedSoftware.
type LicensedSoftware struct {
// Incompatible Software that is incompatible with the server will be automatically removed at the next plan change.
// Servers may have incompatible software due to changes made by support. Software is not incompatible merely because it is disabled;
// disabled software may be retained by servers that already have it, incompatible software will be removed.
Incompatible bool `json:"incompatible"`
// LicenceCount The current licence count for the software.
LicenceCount int32 `json:"licence_count"`
@@ -1912,29 +1945,38 @@ type Links struct {
// LoadBalancer defines model for LoadBalancer.
type LoadBalancer struct {
// CreatedAt The date and time in ISO8601 format of the creation of the load balancer.
CreatedAt *time.Time `json:"created_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
// ForwardingRules The rules that control which traffic the load balancer will forward to servers in the pool.
ForwardingRules *[]ForwardingRule `json:"forwarding_rules,omitempty"`
ForwardingRules []ForwardingRule `json:"forwarding_rules"`
// 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" tfsdk:"health_check"`
HealthCheck HealthCheck `json:"health_check" tfsdk:"health_check"`
// Id The ID of the load balancer.
Id *int64 `json:"id,omitempty"`
Id int64 `json:"id"`
// Ip The IPv4 address of the load balancer.
Ip *string `json:"ip,omitempty"`
Ip string `json:"ip"`
// Name The hostname of the load balancer.
Name *string `json:"name,omitempty"`
Name string `json:"name"`
// Region The region the load balancer is located in. If this value is null the load balancer is an 'AnyCast' load balancer.
Region *Region `json:"region"`
// ServerIds The server IDs of the servers that are currently in the load balancer pool (regardless of their current 'health').
ServerIds *[]int64 `json:"server_ids,omitempty"`
Status *LoadBalancerStatus `json:"status,omitempty"`
ServerIds []int64 `json:"server_ids"`
// Status The current status of the load balancer.
//
// | Value | Description |
// | ----- | ----------- |
// | new | The load balancer is currently being built and is not ready to accept connections. |
// | active | The load balancer is available. |
// | errored | The load balancer is in an errored state. |
//
Status LoadBalancerStatus `json:"status"`
}
// LoadBalancerAvailabilityOption defines model for LoadBalancerAvailabilityOption.
@@ -1946,7 +1988,7 @@ type LoadBalancerAvailabilityOption struct {
PriceHourly *float64 `json:"price_hourly,omitempty"`
// PriceMonthly Monthly Price in AU$.
PriceMonthly *float64 `json:"price_monthly,omitempty"`
PriceMonthly float64 `json:"price_monthly"`
// Regions The slugs of regions where this load balancer option is available. If this is an Anycast load balancer option this will be null.
Regions *[]string `json:"regions"`
@@ -2279,7 +2321,7 @@ type Resize struct {
// ChangeImage This may be left null to keep the current base image for the server. If this is provided the server disks will be destroyed and the server will be rebuilt from the selected image.
ChangeImage *ChangeImage `json:"change_image"`
// ChangeLicenses This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed.
// ChangeLicenses This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed. Any licences that are invalid will be removed regardless, see the documentation for `servers/{server_id}/software` for more information.
ChangeLicenses *ChangeLicenses `json:"change_licenses"`
// Options If this is null and the server has no selected size options the defaults for the size will be used. If this is null and the server has currently selected size options those will be preserved. If this is provided any option fields that are not included will be removed from the existing server.
@@ -3048,10 +3090,10 @@ type UpdateDomainRecordRequest struct {
// UpdateLoadBalancerRequest defines model for UpdateLoadBalancerRequest.
type UpdateLoadBalancerRequest struct {
// ForwardingRules The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule.
ForwardingRules *[]ForwardingRule `json:"forwarding_rules"`
ForwardingRules *[]ForwardingRuleRequest `json:"forwarding_rules"`
// HealthCheck 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.
HealthCheck *HealthCheck `json:"health_check"`
HealthCheck *HealthCheckRequest `json:"health_check"`
// Name The hostname of the load balancer.
Name string `json:"name"`
@@ -7,6 +7,7 @@ import (
"terraform-provider-binarylane/internal/resources"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/types"
)
var (
@@ -22,8 +23,9 @@ type loadBalancerDataSource struct {
bc *BinarylaneClient
}
type loadBalancerDataSourceModel struct {
type loadBalancerDataModel struct {
resources.LoadBalancerModel
Ip types.String `tfsdk:"ip"`
}
func (d *loadBalancerDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
@@ -52,7 +54,7 @@ func (d *loadBalancerDataSource) Configure(
func (d *loadBalancerDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
ds, err := convertResourceSchemaToDataSourceSchema(
resources.LoadBalancerResourceSchema(ctx),
loadBalancerSchema(ctx),
AttributeConfig{
RequiredAttributes: &[]string{"id"},
},
@@ -63,10 +65,11 @@ func (d *loadBalancerDataSource) Schema(ctx context.Context, req datasource.Sche
}
resp.Schema = *ds
resp.Schema.Description = "Retrieve details about a BinaryLane Load Balancer."
resp.Schema.MarkdownDescription = resp.Schema.Description
}
func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data loadBalancerDataSourceModel
var data loadBalancerDataModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
@@ -93,7 +96,7 @@ func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRe
return
}
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags := setLoadBalancerModelState(ctx, &data, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
+76 -41
View File
@@ -10,14 +10,17 @@ import (
"time"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
@@ -37,7 +40,7 @@ type loadBalancerResource struct {
}
type loadBalancerResourceModel struct {
resources.LoadBalancerModel
loadBalancerDataModel
Timeouts timeouts.Value `tfsdk:"timeouts"`
}
@@ -60,15 +63,15 @@ func (d *loadBalancerResource) Configure(_ context.Context, req resource.Configu
d.bc = &bc
}
func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.LoadBalancerResourceSchema(ctx)
resp.Schema.Description = "Provides a BinaryLane Load Balancer resource. This can be used to create, update, and delete load balancers."
resp.Schema.MarkdownDescription = resp.Schema.Description +
func loadBalancerSchema(ctx context.Context) schema.Schema {
s := resources.LoadBalancerResourceSchema(ctx)
s.Description = "Provides a BinaryLane Load Balancer resource. This can be used to create, update, and delete load balancers."
s.MarkdownDescription = s.Description +
" See [the docs](https://support.binarylane.com.au/support/solutions/articles/1000025661-load-balancer) for more information."
// Overrides
id := resp.Schema.Attributes["id"]
resp.Schema.Attributes["id"] = &schema.Int64Attribute{
id := s.Attributes["id"]
s.Attributes["id"] = schema.Int64Attribute{
Description: id.GetDescription(),
MarkdownDescription: id.GetMarkdownDescription(),
// read only
@@ -80,8 +83,8 @@ func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRe
},
}
region := resp.Schema.Attributes["region"]
resp.Schema.Attributes["region"] = &schema.StringAttribute{
region := s.Attributes["region"]
s.Attributes["region"] = schema.StringAttribute{
Description: region.GetDescription(),
MarkdownDescription: region.GetMarkdownDescription(),
Optional: true,
@@ -91,6 +94,49 @@ func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRe
},
}
forwardingRules := s.Attributes["forwarding_rules"].(schema.ListNestedAttribute)
s.Attributes["forwarding_rules"] = schema.ListNestedAttribute{
NestedObject: forwardingRules.NestedObject,
Description: forwardingRules.GetDescription(),
MarkdownDescription: forwardingRules.GetMarkdownDescription(),
Required: false,
Optional: true,
Computed: true,
Default: listdefault.StaticValue(types.ListValueMust(
types.ObjectType{
AttrTypes: resources.ForwardingRulesValue{}.AttributeTypes(ctx),
},
[]attr.Value{
types.ObjectValueMust(
map[string]attr.Type{
"entry_protocol": types.StringType,
},
map[string]attr.Value{
"entry_protocol": types.StringValue("http"),
},
),
},
)),
Validators: []validator.List{
listvalidator.SizeAtLeast(1),
},
}
// Additional attributes
s.Attributes["ip"] = schema.StringAttribute{
Description: "The IPv4 address of the load balancer.",
MarkdownDescription: "The IPv4 address of the load balancer.",
Required: false,
Optional: false,
Computed: true,
}
return s
}
func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = loadBalancerSchema(ctx)
resp.Schema.Attributes["timeouts"] =
timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
@@ -108,7 +154,7 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
}
// Create API call logic
forwardingRules := []binarylane.ForwardingRule{}
forwardingRules := []binarylane.ForwardingRuleRequest{}
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, &forwardingRules, false)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
@@ -129,7 +175,7 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
}
if data.HealthCheck.Path.ValueStringPointer() != nil || data.HealthCheck.Protocol.ValueStringPointer() != nil {
body.HealthCheck = &binarylane.HealthCheck{
body.HealthCheck = &binarylane.HealthCheckRequest{
Path: data.HealthCheck.Path.ValueStringPointer(),
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
}
@@ -154,7 +200,7 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
return
}
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags = setLoadBalancerModelState(ctx, &data.loadBalancerDataModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
@@ -253,7 +299,7 @@ func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadReques
return
}
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags := setLoadBalancerModelState(ctx, &data.loadBalancerDataModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
@@ -274,7 +320,7 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
}
// Update API call logic
forwardingRules := &[]binarylane.ForwardingRule{}
forwardingRules := &[]binarylane.ForwardingRuleRequest{}
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, forwardingRules, true)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
@@ -293,7 +339,7 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
body := binarylane.UpdateLoadBalancerRequest{
Name: data.Name.ValueString(),
ForwardingRules: forwardingRules,
HealthCheck: &binarylane.HealthCheck{
HealthCheck: &binarylane.HealthCheckRequest{
Path: data.HealthCheck.Path.ValueStringPointer(),
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
},
@@ -317,7 +363,7 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
return
}
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags = setLoadBalancerModelState(ctx, &data.loadBalancerDataModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
@@ -396,7 +442,7 @@ func (r *loadBalancerResource) ImportState(
loadBalancers := *lbResp.JSON200.LoadBalancers
for _, lb := range loadBalancers {
if *lb.Name == req.ID {
if lb.Name == req.ID {
loadBalancer = lb
nextPage = false
break
@@ -410,23 +456,16 @@ func (r *loadBalancerResource) ImportState(
page++
}
if loadBalancer.Id == nil {
resp.Diagnostics.AddError(
"Could not find load balancer by name",
fmt.Sprintf("Error finding load balancer: name=%s", req.ID),
)
return
}
diags := resp.State.SetAttribute(ctx, path.Root("id"), *loadBalancer.Id)
diags := resp.State.SetAttribute(ctx, path.Root("id"), loadBalancer.Id)
resp.Diagnostics.Append(diags...)
}
func setLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancerModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
func setLoadBalancerModelState(ctx context.Context, data *loadBalancerDataModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
var diags, diag diag.Diagnostics
data.Id = types.Int64Value(*lb.Id)
data.Name = types.StringValue(*lb.Name)
data.Id = types.Int64Value(lb.Id)
data.Name = types.StringValue(lb.Name)
data.Ip = types.StringValue(lb.Ip)
if lb.Region == nil {
data.Region = types.StringNull()
@@ -436,18 +475,14 @@ func setLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancer
data.ServerIds, diags = types.ListValueFrom(ctx, types.Int64Type, lb.ServerIds)
if lb.HealthCheck == nil {
data.HealthCheck = resources.NewHealthCheckValueNull()
} else {
data.HealthCheck, diag = resources.NewHealthCheckValue(
resources.HealthCheckValue{}.AttributeTypes(ctx),
map[string]attr.Value{
"path": types.StringValue(*lb.HealthCheck.Path),
"protocol": types.StringValue(*lb.HealthCheck.Protocol),
},
)
diags.Append(diag...)
}
data.HealthCheck, diag = resources.NewHealthCheckValue(
resources.HealthCheckValue{}.AttributeTypes(ctx),
map[string]attr.Value{
"path": types.StringValue(lb.HealthCheck.Path),
"protocol": types.StringValue(lb.HealthCheck.Protocol),
},
)
diags.Append(diag...)
data.ForwardingRules, diag = types.ListValueFrom(ctx,
types.ObjectType{AttrTypes: resources.ForwardingRulesValue{}.AttributeTypes(ctx)},
@@ -35,7 +35,7 @@ resource "binarylane_server" "test" {
resource "binarylane_load_balancer" "test" {
name = "tf-test-lb"
server_ids = [binarylane_server.test.0.id, binarylane_server.test.1.id]
forwarding_rules = [{ entry_protocol = "http" }]
# initially, skip defining forwarding_rules, health_check, to test default values
}
data "binarylane_load_balancer" "test" {
@@ -54,6 +54,9 @@ data "binarylane_load_balancer" "test" {
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.#", "1"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.path", "/"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.protocol", "http"),
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "ip"),
// Verify data source values
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "id", "binarylane_load_balancer.test", "id"),
@@ -64,6 +67,9 @@ data "binarylane_load_balancer" "test" {
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.#", "1"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.path", "/"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.protocol", "http"),
resource.TestCheckResourceAttrSet("data.binarylane_load_balancer.test", "ip"),
),
},
// Test import by ID
@@ -79,6 +85,63 @@ data "binarylane_load_balancer" "test" {
ImportStateId: "tf-test-lb",
ImportStateVerify: true,
},
{
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 + `"
public_ipv4_count = 1
}
resource "binarylane_load_balancer" "test" {
name = "tf-test-lb"
server_ids = [binarylane_server.test.0.id, binarylane_server.test.1.id]
# updated
forwarding_rules = [{ entry_protocol = "https" }]
health_check = {
path = "/test-health"
protocol = "https"
}
}
data "binarylane_load_balancer" "test" {
depends_on = [binarylane_load_balancer.test]
id = binarylane_load_balancer.test.id
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
// Verify resource values
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "id"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "name", "tf-test-lb"),
resource.TestCheckNoResourceAttr("binarylane_load_balancer.test", "region"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "server_ids.#", "2"),
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.#", "1"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "https"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.path", "/test-health"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "health_check.protocol", "https"),
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "ip"),
// Verify data source values
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "id", "binarylane_load_balancer.test", "id"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "name", "tf-test-lb"),
resource.TestCheckNoResourceAttr("data.binarylane_load_balancer.test", "region"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "server_ids.#", "2"),
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.#", "1"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "https"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.path", "/test-health"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "health_check.protocol", "https"),
resource.TestCheckResourceAttrSet("data.binarylane_load_balancer.test", "ip"),
),
},
},
})
}
@@ -117,15 +180,15 @@ func init() {
loadBalancers := *listResp.JSON200.LoadBalancers
for _, lb := range loadBalancers {
if strings.HasPrefix(*lb.Name, "tf-test-") {
deleteResp, err := client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, *lb.Id)
if strings.HasPrefix(lb.Name, "tf-test-") {
deleteResp, err := client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, lb.Id)
if err != nil {
return fmt.Errorf("Error deleting load balancer %d for test sweep: %w", *lb.Id, err)
return fmt.Errorf("Error deleting load balancer %d for test sweep: %w", lb.Id, err)
}
if deleteResp.StatusCode() != http.StatusNoContent {
return fmt.Errorf("Unexpected status %d deleting load balancer %d in test sweep: %s", deleteResp.StatusCode(), *lb.Id, deleteResp.Body)
return fmt.Errorf("Unexpected status %d deleting load balancer %d in test sweep: %s", deleteResp.StatusCode(), lb.Id, deleteResp.Body)
}
log.Println("Deleted load balancer during test sweep:", *lb.Id)
log.Println("Deleted load balancer during test sweep:", lb.Id)
}
}
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages == nil || listResp.JSON200.Links.Pages.Next == nil {
+30
View File
@@ -31,6 +31,8 @@ type serverDataModel struct {
resources.ServerModel
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
PublicIpv6Addresses types.List `tfsdk:"public_ipv6_addresses"`
PrivateIpv6Addresses types.List `tfsdk:"private_ipv6_addresses"`
Permalink types.String `tfsdk:"permalink"`
Memory types.Int32 `tfsdk:"memory"`
Disk types.Int32 `tfsdk:"disk"`
@@ -109,6 +111,7 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
data.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
@@ -162,6 +165,33 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
data.PrivateIPv4Addresses = tfPrivateIpv4Addresses
}
publicIpv6Addresses := []string{}
privateIpv6Addresses := []string{}
for _, v6address := range serverResp.JSON200.Server.Networks.V6 {
if v6address.Type == "public" {
publicIpv6Addresses = append(publicIpv6Addresses, v6address.IpAddress)
} else {
privateIpv6Addresses = append(privateIpv6Addresses, v6address.IpAddress)
}
}
tfPublicIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv6Addresses)
diags.Append(diag...)
if diag.HasError() {
data.PublicIpv6Addresses = types.ListUnknown(data.PublicIpv6Addresses.ElementType(ctx))
} else {
data.PublicIpv6Addresses = tfPublicIpv6Addresses
}
tfPrivateIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv6Addresses)
diags.Append(diag...)
if diag.HasError() {
data.PrivateIpv6Addresses = types.ListUnknown(data.PrivateIpv6Addresses.ElementType(ctx))
} else {
data.PrivateIpv6Addresses = tfPrivateIpv6Addresses
}
// Get user data script
userDataResp, err := d.bc.client.GetServersServerIdUserDataWithResponse(ctx, data.Id.ValueInt64())
if err != nil {
@@ -63,7 +63,7 @@ func (r *serverFirewallRulesResource) Schema(ctx context.Context, req resource.S
// Overrides
serverId := resp.Schema.Attributes["server_id"]
resp.Schema.Attributes["server_id"] = &schema.Int64Attribute{
resp.Schema.Attributes["server_id"] = schema.Int64Attribute{
Description: serverId.GetDescription(),
MarkdownDescription: serverId.GetMarkdownDescription(),
Required: true, // Server ID is required to define the firewall rules
@@ -251,7 +251,7 @@ func GetFirewallRulesState(ctx context.Context, firewallRules *[]binarylane.Adva
firewallRulesValue := resources.FirewallRulesValue{}
var firewallRulesValues []resources.FirewallRulesValue
if *firewallRules == nil || len(*firewallRules) == 0 {
if len(*firewallRules) == 0 {
firewallRulesValues = []resources.FirewallRulesValue{}
} else {
for _, rule := range *firewallRules {
+129 -18
View File
@@ -87,7 +87,7 @@ func serverSchema(ctx context.Context) schema.Schema {
// Overrides
id := s.Attributes["id"]
s.Attributes["id"] = &schema.Int64Attribute{
s.Attributes["id"] = schema.Int64Attribute{
Description: id.GetDescription(),
MarkdownDescription: id.GetMarkdownDescription(),
// read only
@@ -101,7 +101,7 @@ func serverSchema(ctx context.Context) schema.Schema {
imageDescription := "The slug of the selected operating system, such as `debian-12`. You can fetch a full list of images from the BinaryLane API."
image := s.Attributes["image"]
s.Attributes["image"] = &schema.StringAttribute{
s.Attributes["image"] = schema.StringAttribute{
Description: imageDescription,
MarkdownDescription: imageDescription,
Required: image.IsRequired(),
@@ -113,7 +113,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
backupsDescription := "If `true` this will enable two daily backups for the server. By default, backups are disabled."
s.Attributes["backups"] = &schema.BoolAttribute{
s.Attributes["backups"] = schema.BoolAttribute{
Description: backupsDescription,
MarkdownDescription: backupsDescription,
Optional: true,
@@ -122,7 +122,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
user_data := s.Attributes["user_data"]
s.Attributes["user_data"] = &schema.StringAttribute{
s.Attributes["user_data"] = schema.StringAttribute{
Description: user_data.GetDescription(),
MarkdownDescription: user_data.GetMarkdownDescription(),
Optional: true, // Optional as not all servers have an initialization script
@@ -130,7 +130,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
vpcId := s.Attributes["vpc_id"]
s.Attributes["vpc_id"] = &schema.Int64Attribute{
s.Attributes["vpc_id"] = schema.Int64Attribute{
Description: vpcId.GetDescription(),
MarkdownDescription: vpcId.GetMarkdownDescription(),
Optional: vpcId.IsOptional(),
@@ -138,7 +138,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
portBlocking := s.Attributes["port_blocking"]
s.Attributes["port_blocking"] = &schema.BoolAttribute{
s.Attributes["port_blocking"] = schema.BoolAttribute{
Description: portBlocking.GetDescription(),
MarkdownDescription: portBlocking.GetMarkdownDescription(),
Optional: portBlocking.IsOptional(),
@@ -147,7 +147,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
region := s.Attributes["region"].(schema.StringAttribute)
s.Attributes["region"] = &schema.StringAttribute{
s.Attributes["region"] = schema.StringAttribute{
Description: region.GetDescription(),
MarkdownDescription: region.GetMarkdownDescription(),
Optional: region.IsOptional(),
@@ -160,7 +160,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
sshKeys := s.Attributes["ssh_keys"]
s.Attributes["ssh_keys"] = &schema.ListAttribute{
s.Attributes["ssh_keys"] = schema.ListAttribute{
ElementType: types.Int64Type,
Description: sshKeys.GetMarkdownDescription(),
MarkdownDescription: sshKeys.GetDescription(),
@@ -174,7 +174,7 @@ func serverSchema(ctx context.Context) schema.Schema {
userDataDescription := "A script or cloud-config YAML file to configure the server. Can only be specified if the OS image supports UserData (i.e. not Windows)." +
" See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script"
userData := s.Attributes["user_data"]
s.Attributes["user_data"] = &schema.StringAttribute{
s.Attributes["user_data"] = schema.StringAttribute{
Description: userDataDescription,
MarkdownDescription: userDataDescription,
Required: userData.IsRequired(),
@@ -190,7 +190,7 @@ func serverSchema(ctx context.Context) schema.Schema {
"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."
s.Attributes["password"] = &schema.StringAttribute{
s.Attributes["password"] = schema.StringAttribute{
Description: pwDescription,
MarkdownDescription: pwDescription,
Optional: true, // Password optional, if not set will be emailed to user
@@ -199,7 +199,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
publicIpv4CountDescription := "The number of public IPv4 addresses to assign to the server."
s.Attributes["public_ipv4_count"] = &schema.Int32Attribute{
s.Attributes["public_ipv4_count"] = schema.Int32Attribute{
Description: publicIpv4CountDescription,
MarkdownDescription: publicIpv4CountDescription,
Required: true,
@@ -212,7 +212,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
publicIpv4AddressesDescription := "The public IPv4 addresses assigned to the server."
s.Attributes["public_ipv4_addresses"] = &schema.ListAttribute{
s.Attributes["public_ipv4_addresses"] = schema.ListAttribute{
Description: publicIpv4AddressesDescription,
MarkdownDescription: publicIpv4AddressesDescription,
ElementType: types.StringType,
@@ -229,7 +229,7 @@ func serverSchema(ctx context.Context) schema.Schema {
"to incorrect network configuration. When `source_and_destination_check` is `false`, your Cloud Server will be able " +
"to send and receive packets addressed to any server. This is typically used when you want to use " +
"your Cloud Server as a VPN endpoint, a NAT server to provide internet access, or IP forwarding."
s.Attributes["source_and_destination_check"] = &schema.BoolAttribute{
s.Attributes["source_and_destination_check"] = schema.BoolAttribute{
Description: sourceDestCheckDescription,
MarkdownDescription: sourceDestCheckDescription,
Optional: true,
@@ -243,7 +243,7 @@ func serverSchema(ctx context.Context) schema.Schema {
}
privateIpv4AddressesDescription := "The private IPv4 addresses assigned to the server."
s.Attributes["private_ipv4_addresses"] = &schema.ListAttribute{
s.Attributes["private_ipv4_addresses"] = schema.ListAttribute{
Description: privateIpv4AddressesDescription,
MarkdownDescription: privateIpv4AddressesDescription,
ElementType: types.StringType,
@@ -256,7 +256,41 @@ func serverSchema(ctx context.Context) schema.Schema {
},
}
s.Attributes["permalink"] = &schema.StringAttribute{
ipv6Description := "If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled."
s.Attributes["ipv6"] = schema.BoolAttribute{
Description: ipv6Description,
MarkdownDescription: ipv6Description,
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false), // Add default to ipv6
}
publicIpv6AddressesDescription := "The public IPv6 addresses assigned to the server."
s.Attributes["public_ipv6_addresses"] = schema.ListAttribute{
Description: publicIpv6AddressesDescription,
MarkdownDescription: publicIpv6AddressesDescription,
ElementType: types.StringType,
// read only
Optional: false,
Required: false,
Computed: true,
}
privateIpv6AddressesDescription := "The private IPv6 addresses assigned to the server."
s.Attributes["private_ipv6_addresses"] = schema.ListAttribute{
Description: privateIpv6AddressesDescription,
MarkdownDescription: privateIpv6AddressesDescription,
ElementType: types.StringType,
// read only
Optional: false,
Required: false,
Computed: true,
PlanModifiers: []planmodifier.List{
listplanmodifier.UseStateForUnknown(),
},
}
s.Attributes["permalink"] = schema.StringAttribute{
Description: "A randomly generated two-word identifier assigned to servers in regions that support this feature",
MarkdownDescription: "A randomly generated two-word identifier assigned to servers in regions that support this feature",
// read only
@@ -271,7 +305,7 @@ func serverSchema(ctx context.Context) schema.Schema {
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."
s.Attributes["password_change_supported"] = &schema.BoolAttribute{
s.Attributes["password_change_supported"] = schema.BoolAttribute{
Description: pwChangeDescription,
MarkdownDescription: pwChangeDescription,
// read only
@@ -293,7 +327,7 @@ func serverSchema(ctx context.Context) schema.Schema {
- \> 16384 MB must be a multiple of 2048
- \> 24576 MB must be a multiple of 4096`
s.Attributes["memory"] = &schema.Int32Attribute{
s.Attributes["memory"] = schema.Int32Attribute{
Description: memoryDescription + memoryValidValues,
MarkdownDescription: memoryDescription + memoryValidValuesMarkdown,
Optional: true,
@@ -316,7 +350,7 @@ func serverSchema(ctx context.Context) schema.Schema {
- \> 60 GB must be a multiple of 10
- \> 200 GB must be a multiple of 100`
s.Attributes["disk"] = &schema.Int32Attribute{
s.Attributes["disk"] = schema.Int32Attribute{
Description: diskDescription + diskValidValues,
MarkdownDescription: diskDescription + diskValidValuesMarkdown,
Optional: true,
@@ -414,6 +448,16 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
)
}
if !plan.Ipv6.Equal(state.Ipv6) {
if plan.Ipv6.ValueBool() {
plan.PublicIpv6Addresses = types.ListUnknown(state.PublicIpv6Addresses.ElementType(ctx))
plan.PrivateIpv6Addresses = types.ListUnknown(state.PrivateIpv6Addresses.ElementType(ctx))
} else {
plan.PublicIpv6Addresses = types.ListNull(state.PublicIpv6Addresses.ElementType(ctx))
plan.PrivateIpv6Addresses = types.ListNull(state.PrivateIpv6Addresses.ElementType(ctx))
}
}
// Use state for unknown disk/memory values, as long as server size is the same
if (plan.Memory.IsNull() || plan.Memory.IsUnknown()) && plan.Size.Equal(state.Size) {
plan.Memory = state.Memory
@@ -527,6 +571,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
Ipv4Addresses: data.PublicIpv4Count.ValueInt32Pointer(),
},
Backups: data.Backups.ValueBoolPointer(),
Ipv6: data.Ipv6.ValueBoolPointer(),
}
if !data.Memory.IsNull() && !data.Memory.IsUnknown() {
@@ -584,6 +629,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
data.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
@@ -890,6 +936,43 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
}
}
// Change ipv6
if !plan.Ipv6.Equal(state.Ipv6) {
ipv6Resp, err := r.bc.client.PostServersServerIdActionsChangeIpv6WithResponse(
ctx,
state.Id.ValueInt64(),
binarylane.PostServersServerIdActionsChangeIpv6JSONRequestBody{
Type: "change_ipv6",
Enabled: plan.Ipv6.ValueBool(),
},
)
if err != nil {
resp.Diagnostics.AddError("Error changing IPv6", err.Error())
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *ipv6Resp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for changing IPv6", err.Error())
return
}
state.Ipv6 = plan.Ipv6
state.PublicIpv6Addresses = plan.PublicIpv6Addresses
state.PrivateIpv6Addresses = plan.PrivateIpv6Addresses
if state.PublicIpv6Addresses.IsUnknown() || state.PrivateIpv6Addresses.IsUnknown() || listContainsUnknown(ctx, state.PublicIpv6Addresses) || listContainsUnknown(ctx, state.PrivateIpv6Addresses) {
refreshNeeded = true
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
// Rebuild operation
if !plan.SshKeys.Equal(state.SshKeys) || !plan.UserData.IsNull() && !plan.UserData.Equal(state.UserData) {
var rebuildReq *binarylane.PostServersServerIdActionsRebuildJSONRequestBody
@@ -1258,6 +1341,7 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
state.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
state.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
state.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
state.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
advFeat := *serverResp.JSON200.Server.AdvancedFeatures.EnabledAdvancedFeatures
state.AdvancedFeatures, diags = resources.NewAdvancedFeaturesValue(
@@ -1307,6 +1391,33 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
state.PrivateIPv4Addresses = tfPrivateIpv4Addresses
}
publicIpv6Addresses := []string{}
privateIpv6Addresses := []string{}
for _, v6address := range serverResp.JSON200.Server.Networks.V6 {
if v6address.Type == "public" {
publicIpv6Addresses = append(publicIpv6Addresses, v6address.IpAddress)
} else {
privateIpv6Addresses = append(privateIpv6Addresses, v6address.IpAddress)
}
}
tfPublicIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv6Addresses)
diags.Append(diag...)
if diag.HasError() || tfPublicIpv6Addresses.IsNull() {
state.PublicIpv6Addresses = types.ListUnknown(state.PublicIpv6Addresses.ElementType(ctx))
} else {
state.PublicIpv6Addresses = tfPublicIpv6Addresses
}
tfPrivateIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv6Addresses)
diags.Append(diag...)
if diag.HasError() || tfPrivateIpv6Addresses.IsNull() {
state.PrivateIpv6Addresses = types.ListUnknown(state.PrivateIpv6Addresses.ElementType(ctx))
} else {
state.PrivateIpv6Addresses = tfPrivateIpv6Addresses
}
return diags
}
@@ -104,6 +104,8 @@ echo "Hello World" > /var/tmp/output.txt
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.cloud_init", "true"),
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.qemu_guest_agent", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.uefi_boot", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "ipv6", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv6_addresses.#", "0"),
// Verify data source values
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
@@ -130,6 +132,8 @@ echo "Hello World" > /var/tmp/output.txt
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.cloud_init", "true"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.qemu_guest_agent", "false"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.uefi_boot", "false"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "ipv6", "false"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "public_ipv6_addresses.#", "0"),
),
},
// Test import by ID
@@ -180,6 +184,7 @@ resource "binarylane_server" "test" {
advanced_features = {
emulated_hyperv = true
}
ipv6 = true
# source_and_destination_check = null # defaults to null
# backups = false # defaults to false
@@ -214,6 +219,9 @@ echo "Hello Whitespace" > /var/tmp/output.txt
`),
resource.TestCheckResourceAttr("binarylane_server.test", "ipv6", "true"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv6_addresses.#", "1"),
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv6_addresses.#", "1"),
),
},
// Change password testing (Cannot run at same time as Rebuild operation, so it has it's own test)
+1 -1
View File
@@ -82,7 +82,7 @@ func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
}
fingerprintDescription := "The fingerprint of the SSH key."
resp.Schema.Attributes["fingerprint"] = &schema.StringAttribute{
resp.Schema.Attributes["fingerprint"] = schema.StringAttribute{
Description: fingerprintDescription,
MarkdownDescription: fingerprintDescription,
Optional: false,
+1 -1
View File
@@ -105,7 +105,7 @@ func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest,
}
fingerprintDescription := "The fingerprint of the SSH key."
resp.Schema.Attributes["fingerprint"] = &schema.StringAttribute{
resp.Schema.Attributes["fingerprint"] = schema.StringAttribute{
Description: fingerprintDescription,
MarkdownDescription: fingerprintDescription,
Optional: false,
+1 -2
View File
@@ -136,13 +136,13 @@ func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribu
}
nestedObjectAttrs[name] = nestedAttribute
}
return d_schema.ListNestedAttribute{
NestedObject: d_schema.NestedAttributeObject{
Attributes: nestedObjectAttrs,
CustomType: t.NestedObject.CustomType,
Validators: t.NestedObject.Validators,
},
CustomType: t.CustomType,
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
@@ -183,7 +183,6 @@ func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribu
AttributeTypes: attributeTypes,
}, nil
}
if t, isObject := attribute.GetType().(types.ObjectType); isObject {
return d_schema.ObjectAttribute{
AttributeTypes: t.AttrTypes,
+2 -2
View File
@@ -63,7 +63,7 @@ func (r *vpcResource) Schema(ctx context.Context, req resource.SchemaRequest, re
// Overrides
id := resp.Schema.Attributes["id"]
resp.Schema.Attributes["id"] = &schema.Int64Attribute{
resp.Schema.Attributes["id"] = schema.Int64Attribute{
Description: id.GetDescription(),
MarkdownDescription: id.GetMarkdownDescription(),
// read only
@@ -73,7 +73,7 @@ func (r *vpcResource) Schema(ctx context.Context, req resource.SchemaRequest, re
}
ipRange := resp.Schema.Attributes["ip_range"]
resp.Schema.Attributes["ip_range"] = &schema.StringAttribute{
resp.Schema.Attributes["ip_range"] = schema.StringAttribute{
Description: ipRange.GetDescription(),
MarkdownDescription: ipRange.GetMarkdownDescription(),
Required: false,
@@ -47,7 +47,7 @@ func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.S
// Overrides
vpcId := resp.Schema.Attributes["vpc_id"]
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
resp.Schema.Attributes["vpc_id"] = schema.Int64Attribute{
Description: vpcId.GetDescription(),
MarkdownDescription: vpcId.GetMarkdownDescription(),
Required: true, // vpc_id is required to define the route entries
@@ -55,7 +55,7 @@ func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.S
vpcRouteEntries := resp.Schema.Attributes["route_entries"].(schema.ListNestedAttribute)
vpcRouteEntriesDescription := "The route entries that control how network traffic is directed through the VPC environment."
resp.Schema.Attributes["route_entries"] = &schema.ListNestedAttribute{
resp.Schema.Attributes["route_entries"] = schema.ListNestedAttribute{
Description: vpcRouteEntriesDescription,
NestedObject: schema.NestedAttributeObject{
CustomType: vpcRouteEntries.NestedObject.CustomType,
@@ -48,7 +48,7 @@ func (r *vpcRouteEntriesResource) Schema(ctx context.Context, req resource.Schem
// Overrides
vpcId := resp.Schema.Attributes["vpc_id"]
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
resp.Schema.Attributes["vpc_id"] = schema.Int64Attribute{
Description: vpcId.GetDescription(),
MarkdownDescription: vpcId.GetMarkdownDescription(),
Required: true, // vpc_id is required to define the route entries
@@ -246,7 +246,7 @@ func GetRouteEntriesState(ctx context.Context, routeEntries *[]binarylane.RouteE
routeEntriesValue := resources.RouteEntriesValue{}
var routeEntriesValues []resources.RouteEntriesValue
if *routeEntries == nil || len(*routeEntries) == 0 {
if len(*routeEntries) == 0 {
routeEntriesValues = []resources.RouteEntriesValue{}
} else {
for _, route := range *routeEntries {
@@ -26,8 +26,8 @@ func LoadBalancerResourceSchema(ctx context.Context) schema.Schema {
Attributes: map[string]schema.Attribute{
"entry_protocol": schema.StringAttribute{
Required: true,
Description: "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
MarkdownDescription: "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
Description: "The protocol that traffic must match for this load balancer to forward traffic according to this rule.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The load balancer will forward HTTP traffic that matches this rule. |\n| https | The load balancer will forward HTTPS traffic that matches this rule. |\n\n",
MarkdownDescription: "The protocol that traffic must match for this load balancer to forward traffic according to this rule.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The load balancer will forward HTTP traffic that matches this rule. |\n| https | The load balancer will forward HTTPS traffic that matches this rule. |\n\n",
Validators: []validator.String{
stringvalidator.OneOf(
"http",
@@ -105,6 +105,12 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
Description: "The slug of the selected operating system.",
MarkdownDescription: "The slug of the selected operating system.",
},
"ipv6": schema.BoolAttribute{
Optional: true,
Computed: true,
Description: "If true this will enable IPv6 for this server.",
MarkdownDescription: "If true this will enable IPv6 for this server.",
},
"name": schema.StringAttribute{
Optional: true,
Computed: true,
@@ -164,6 +170,7 @@ type ServerModel struct {
Backups types.Bool `tfsdk:"backups"`
Id types.Int64 `tfsdk:"id"`
Image types.String `tfsdk:"image"`
Ipv6 types.Bool `tfsdk:"ipv6"`
Name types.String `tfsdk:"name"`
PortBlocking types.Bool `tfsdk:"port_blocking"`
Region types.String `tfsdk:"region"`
+68 -32
View File
@@ -8,7 +8,7 @@
"url": "https://support.binarylane.com.au",
"email": "support@binarylane.com.au"
},
"version": "0.32.0",
"version": "0.33.0",
"x-logo": {
"url": "https://www.binarylane.com.au/res/images/binarylane/logo.png",
"backgroundColor": "#000000",
@@ -7590,7 +7590,7 @@
"forwarding_rules": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ForwardingRule"
"$ref": "#/components/schemas/ForwardingRuleRequest"
},
"description": "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule.",
"nullable": true,
@@ -7601,7 +7601,7 @@
"health_check": {
"allOf": [
{
"$ref": "#/components/schemas/HealthCheck"
"$ref": "#/components/schemas/HealthCheckRequest"
}
],
"description": "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.",
@@ -8542,6 +8542,25 @@
}
}
},
"ForwardingRuleRequest": {
"required": [
"entry_protocol"
],
"type": "object",
"properties": {
"entry_protocol": {
"allOf": [
{
"$ref": "#/components/schemas/LoadBalancerRuleProtocol"
}
],
"description": "The protocol that traffic must match for this load balancer to forward traffic according to this rule.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The load balancer will forward HTTP traffic that matches this rule. |\n| https | The load balancer will forward HTTPS traffic that matches this rule. |\n\n",
"x-oapi-codegen-extra-tags": {
"tfsdk": "entry_protocol"
}
}
}
},
"ForwardingRulesRequest": {
"required": [
"forwarding_rules"
@@ -8551,13 +8570,44 @@
"forwarding_rules": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ForwardingRule"
"$ref": "#/components/schemas/ForwardingRuleRequest"
},
"description": "The rules that control which traffic the load balancer will forward to servers in the pool."
}
}
},
"HealthCheck": {
"required": [
"path",
"protocol"
],
"type": "object",
"properties": {
"protocol": {
"allOf": [
{
"$ref": "#/components/schemas/HealthCheckProtocol"
}
],
"description": "The protocol used for the health check.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The health check will be performed via HTTP. |\n| https | The health check will be performed via HTTPS. |\n| both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |\n\n"
},
"path": {
"minLength": 1,
"type": "string",
"description": "The path to the health check endpoint."
}
}
},
"HealthCheckProtocol": {
"type": "string",
"description": "\n| Value | Description |\n| ----- | ----------- |\n| http | The health check will be performed via HTTP. |\n| https | The health check will be performed via HTTPS. |\n| both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |\n\n",
"x-enum-descriptions": [
"The health check will be performed via HTTP.",
"The health check will be performed via HTTPS.",
"The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol."
]
},
"HealthCheckRequest": {
"type": "object",
"properties": {
"protocol": {
@@ -8577,15 +8627,6 @@
}
}
},
"HealthCheckProtocol": {
"type": "string",
"description": "\n| Value | Description |\n| ----- | ----------- |\n| http | The health check will be performed via HTTP. |\n| https | The health check will be performed via HTTPS. |\n| both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |\n\n",
"x-enum-descriptions": [
"The health check will be performed via HTTP.",
"The health check will be performed via HTTPS.",
"The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol."
]
},
"Host": {
"required": [
"display_name"
@@ -9295,6 +9336,7 @@
},
"LicensedSoftware": {
"required": [
"incompatible",
"licence_count",
"software"
],
@@ -9312,6 +9354,10 @@
"type": "integer",
"description": "The current licence count for the software.",
"format": "int32"
},
"incompatible": {
"type": "boolean",
"description": "Software that is incompatible with the server will be automatically removed at the next plan change.\nServers may have incompatible software due to changes made by support. Software is not incompatible merely because it is disabled;\ndisabled software may be retained by servers that already have it, incompatible software will be removed."
}
}
},
@@ -9380,21 +9426,18 @@
"type": "integer",
"description": "The ID of the load balancer.",
"format": "int64",
"readOnly": true,
"x-cli-format": 1
},
"name": {
"minLength": 1,
"type": "string",
"description": "The hostname of the load balancer.",
"readOnly": true,
"x-cli-format": 2
},
"ip": {
"minLength": 1,
"type": "string",
"description": "The IPv4 address of the load balancer.",
"readOnly": true,
"x-cli-format": 4
},
"status": {
@@ -9403,21 +9446,19 @@
"$ref": "#/components/schemas/LoadBalancerStatus"
}
],
"readOnly": true
"description": "The current status of the load balancer.\n\n| Value | Description |\n| ----- | ----------- |\n| new | The load balancer is currently being built and is not ready to accept connections. |\n| active | The load balancer is available. |\n| errored | The load balancer is in an errored state. |\n\n"
},
"created_at": {
"type": "string",
"description": "The date and time in ISO8601 format of the creation of the load balancer.",
"format": "date-time",
"readOnly": true
"format": "date-time"
},
"forwarding_rules": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ForwardingRule"
},
"description": "The rules that control which traffic the load balancer will forward to servers in the pool.",
"readOnly": true
"description": "The rules that control which traffic the load balancer will forward to servers in the pool."
},
"health_check": {
"allOf": [
@@ -9426,7 +9467,6 @@
}
],
"description": "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer.",
"readOnly": true,
"x-oapi-codegen-extra-tags": {
"tfsdk": "health_check"
}
@@ -9439,7 +9479,6 @@
],
"description": "The region the load balancer is located in. If this value is null the load balancer is an 'AnyCast' load balancer.",
"nullable": true,
"readOnly": true,
"x-cli-format": 3
},
"server_ids": {
@@ -9448,8 +9487,7 @@
"type": "integer",
"format": "int64"
},
"description": "The server IDs of the servers that are currently in the load balancer pool (regardless of their current 'health').",
"readOnly": true
"description": "The server IDs of the servers that are currently in the load balancer pool (regardless of their current 'health')."
}
}
},
@@ -9467,8 +9505,7 @@
"type": "string"
},
"description": "The slugs of regions where this load balancer option is available. If this is an Anycast load balancer option this will be null.",
"nullable": true,
"readOnly": true
"nullable": true
},
"anycast": {
"type": "boolean",
@@ -9478,8 +9515,7 @@
"price_monthly": {
"type": "number",
"description": "Monthly Price in AU$.",
"format": "double",
"readOnly": true
"format": "double"
},
"price_hourly": {
"type": "number",
@@ -10237,7 +10273,7 @@
"$ref": "#/components/schemas/ChangeLicenses"
}
],
"description": "This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed.",
"description": "This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed. Any licences that are invalid will be removed regardless, see the documentation for `servers/{server_id}/software` for more information.",
"nullable": true
},
"pre_action_backup": {
@@ -12290,7 +12326,7 @@
"forwarding_rules": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ForwardingRule"
"$ref": "#/components/schemas/ForwardingRuleRequest"
},
"description": "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule.",
"nullable": true
@@ -12298,7 +12334,7 @@
"health_check": {
"allOf": [
{
"$ref": "#/components/schemas/HealthCheck"
"$ref": "#/components/schemas/HealthCheckRequest"
}
],
"description": "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.",
+8 -1
View File
@@ -712,7 +712,7 @@
"name": "entry_protocol",
"string": {
"computed_optional_required": "required",
"description": "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
"description": "The protocol that traffic must match for this load balancer to forward traffic according to this rule.\n\n| Value | Description |\n| ----- | ----------- |\n| http | The load balancer will forward HTTP traffic that matches this rule. |\n| https | The load balancer will forward HTTPS traffic that matches this rule. |\n\n",
"validators": [
{
"custom": {
@@ -834,6 +834,13 @@
"description": "The slug of the selected operating system."
}
},
{
"name": "ipv6",
"bool": {
"computed_optional_required": "computed_optional",
"description": "If true this will enable IPv6 for this server."
}
},
{
"name": "name",
"string": {
-1
View File
@@ -40,7 +40,6 @@ resources:
default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to
disable deployment of default keys.
ignores:
- ipv6
- licenses
- links
- server
+2 -2
View File
@@ -2,6 +2,6 @@
GENERATOR_CONFIG=$(dirname "$0")/../provider_code_spec.json
# Advanced features configuration
ADVANCED_FEATURES_CONFIG=$(dirname "$0")/data/advanced_features.json
# Server configuration
ADVANCED_FEATURES_CONFIG=$(dirname "$0")/data/server_advanced_features.json
cat <<<$(jq --tab --slurpfile adv_feat_cfg $ADVANCED_FEATURES_CONFIG '.resources[1].schema.attributes |= . + $adv_feat_cfg' $GENERATOR_CONFIG) >$GENERATOR_CONFIG
+1
View File
@@ -59,6 +59,7 @@ cat <<<$(jq '.components.schemas.AdvancedFirewallRuleRequest.properties.source_a
## Load Balancer
cat <<<$(jq '.components.schemas.CreateLoadBalancerRequest.properties.forwarding_rules += {"x-oapi-codegen-extra-tags": {"tfsdk": "forwarding_rules"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "entry_protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.ForwardingRuleRequest.properties.entry_protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "entry_protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.LoadBalancer.properties.health_check += {"x-oapi-codegen-extra-tags": {"tfsdk": "health_check"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.HealthCheckProtocol |= del(.enum)' $OPENAPI_FILE) >$OPENAPI_FILE