Compare commits

..

10 Commits

Author SHA1 Message Date
dependabot[bot] 90b94254f1 🐹 Bump github.com/hashicorp/terraform-plugin-framework-timeouts (#60)
Bumps the minor-patch group with 1 update: [github.com/hashicorp/terraform-plugin-framework-timeouts](https://github.com/hashicorp/terraform-plugin-framework-timeouts).


Updates `github.com/hashicorp/terraform-plugin-framework-timeouts` from 0.4.1 to 0.5.0
- [Release notes](https://github.com/hashicorp/terraform-plugin-framework-timeouts/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-framework-timeouts/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-framework-timeouts/compare/v0.4.1...v0.5.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-framework-timeouts
  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-01-18 10:06:27 +08:00
Oscar Hermoso 46117cf31d 👩‍⚕️ flesh out the docs, fix optional/required attrs (#59)
* 👨‍⚕️ Create docs

* 👩‍⚕️ self pr feedback

* 👩‍⚕️ more self pr feedback
2025-01-18 10:04:57 +08:00
Oscar Hermoso 394638ff57 🛂 Support updates to server password and port_blocking (#56) 2024-12-20 09:28:20 +08:00
Oscar Hermoso 239e48e940 🎒 allow updating of the server backups property (#55) 2024-12-15 09:30:27 +08:00
dependabot[bot] 2221f8c853 🐹 Bump golang.org/x/crypto from 0.29.0 to 0.31.0 (#54)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.29.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-14 09:13:10 +08:00
dependabot[bot] 95487045c4 🐹 Bump github.com/hashicorp/terraform-plugin-framework-validators (#53)
Bumps the minor-patch group with 1 update: [github.com/hashicorp/terraform-plugin-framework-validators](https://github.com/hashicorp/terraform-plugin-framework-validators).


Updates `github.com/hashicorp/terraform-plugin-framework-validators` from 0.15.0 to 0.16.0
- [Release notes](https://github.com/hashicorp/terraform-plugin-framework-validators/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-framework-validators/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-framework-validators/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-framework-validators
  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>
2024-12-14 08:58:38 +08:00
Oscar Hermoso 7f5bae6b15 💾 allow customising server memory and disk (#52) 2024-12-08 17:09:54 +08:00
Oscar Hermoso 4ef9df6edf 🧁 allow update of server vpc_id without recreating resource, update api (#51) 2024-12-08 08:20:21 +08:00
dependabot[bot] 547763d639 🐹 Bump github.com/hashicorp/terraform-plugin-docs (#49)
Bumps the minor-patch group with 1 update: [github.com/hashicorp/terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs).


Updates `github.com/hashicorp/terraform-plugin-docs` from 0.20.0 to 0.20.1
- [Release notes](https://github.com/hashicorp/terraform-plugin-docs/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-docs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-docs/compare/v0.20.0...v0.20.1)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-docs
  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>
2024-11-29 07:22:41 +08:00
Oscar Hermoso f5fecdd69c 👯 consistent headings/curl summary for parameters in README 2024-11-22 10:40:17 +08:00
54 changed files with 1267 additions and 1956 deletions
+32 -24
View File
@@ -18,8 +18,8 @@ Planned features:
- [x] Servers
- [x] Firewall Rules
- [x] Cloud Init
- [ ] Updating server properties
- [ ] Backups
- [x] Updating server properties
- [ ] Customised Backups
- [ ] Software
- [ ] IPV6
- [ ] Alerts
@@ -39,9 +39,35 @@ Planned features:
- [ ] Virtual Private Cloud
- [ ] Kubernetes
## Parameters
## Server parameters
Images:
### Regions
<details>
<summary>curl for regions</summary>
```sh
curl -X GET "https://api.binarylane.com.au/v2/regions" \
-H "Authorization: Bearer $BINARYLANE_API_TOKEN" > tmp/regions.json
jq '[ .regions[] | .slug ] | sort' tmp/regions.json
```
</details>
```json
[
"bne",
"mel",
"per",
"sin",
"syd"
]
```
### Images
<details>
<summary>curl for images</summary>
```sh
curl -X GET "https://api.binarylane.com.au/v2/images?type=distribution&&page=1&per_page=200" \
@@ -49,6 +75,7 @@ curl -X GET "https://api.binarylane.com.au/v2/images?type=distribution&&page=1&p
jq '[ .images[] | .slug ] | sort' tmp/images.json
```
</details>
```json
[
@@ -78,29 +105,10 @@ jq '[ .images[] | .slug ] | sort' tmp/images.json
]
```
Regions:
```sh
curl -X GET "https://api.binarylane.com.au/v2/regions" \
-H "Authorization: Bearer $BINARYLANE_API_TOKEN" > tmp/regions.json
jq '[ .regions[] | .slug ] | sort' tmp/regions.json
```
```json
[
"bne",
"mel",
"per",
"sin",
"syd"
]
```
### Sizes
<details>
<summary>VM size</summary>
<summary>curl for sizes</summary>
```sh
curl -X GET "https://api.binarylane.com.au/v2/sizes" \
+8 -2
View File
@@ -10,7 +10,13 @@ description: |-
## Example Usage
```terraform
data "binarylane_load_balancer" "example" {
id = 123456
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -21,7 +27,7 @@ description: |-
### Read-Only
- `forwarding_rules` (List of Object) 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))
- `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))
- `name` (String) The hostname of the load balancer.
- `region` (String) Leave null to create an anycast load balancer.
@@ -32,7 +38,7 @@ description: |-
Read-Only:
- `entry_protocol` (String)
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
<a id="nestedatt--health_check"></a>
+13 -5
View File
@@ -10,7 +10,13 @@ description: |-
Provides a Binary Lane Server data source. This can be used to read existing server details.
## Example Usage
```terraform
data "binarylane_server" "example" {
id = 123456
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -21,15 +27,17 @@ Provides a Binary Lane Server data source. This can be used to read existing ser
### Read-Only
- `backups` (Boolean) If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.
- `backups` (Boolean) If `true`, the server will be backed up twice per day. By default, backups are disabled.
- `disk` (Number) The amount of storage in GB assigned to the server.
- `image` (String) The slug of the selected operating system.
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
- `memory` (Number) The amount of memory in MB assigned to the server.
- `name` (String) The hostname of your server, such as vps01.yourcompany.com.
- `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.
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
- `region` (String) The slug of the selected region.
- `size` (String) The slug of the selected size.
- `ssh_keys` (List of Number) This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.
- `user_data` (String) 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.
- `ssh_keys` (List of Number) This is a list of SSH key ids that were added to the server during creation.
- `user_data` (String) A script or cloud-config YAML file to configure the server.
- `vpc_id` (Number) ID of the Virtual Private Cloud (VPC) the server is connected to.
+15 -9
View File
@@ -3,14 +3,20 @@
page_title: "binarylane_server_firewall_rules Data Source - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_server_firewall_rules (Data Source)
TODO
## Example Usage
```terraform
data "binarylane_server_firewall_rules" "example" {
server_id = 123456
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -21,16 +27,16 @@ TODO
### Read-Only
- `firewall_rules` (List of Object) A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules. (see [below for nested schema](#nestedatt--firewall_rules))
- `firewall_rules` (Attributes List) A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules. (see [below for nested schema](#nestedatt--firewall_rules))
<a id="nestedatt--firewall_rules"></a>
### Nested Schema for `firewall_rules`
Read-Only:
- `action` (String)
- `description` (String)
- `destination_addresses` (List of String)
- `destination_ports` (List of String)
- `protocol` (String)
- `source_addresses` (List of String)
- `action` (String) The action to take when there is a match on this rule.
- `description` (String) A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. "Block access to RDP" or "Allow access from HQ".
- `destination_addresses` (List of String) The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
- `destination_ports` (List of String) The destination ports to match for this rule. Leave null or empty to match on all ports.
- `protocol` (String) The protocol to match for this rule.
- `source_addresses` (List of String) The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
+7 -1
View File
@@ -10,7 +10,13 @@ description: |-
## Example Usage
```terraform
data "binarylane_ssh_key" "example" {
id = 123456
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -21,7 +27,7 @@ description: |-
### Read-Only
- `default` (Boolean) Optional: If true this will be added to all new server installations (if we support SSH Key injection for the server's operating system).
- `default` (Boolean) If `true`, this SSH key will be included on all new server installations (if the operating system supports SSH key injection).
- `fingerprint` (String) The fingerprint of the SSH key.
- `name` (String) A name to help you identify the key.
- `public_key` (String) The public key in OpenSSH "authorized_keys" format.
+6
View File
@@ -10,7 +10,13 @@ description: |-
## Example Usage
```terraform
data "binarylane_vpc" "example" {
id = 123456
}
```
<!-- schema generated by tfplugindocs -->
## Schema
+10 -4
View File
@@ -10,7 +10,13 @@ description: |-
## Example Usage
```terraform
data "binarylane_vpc_route_entries" "example" {
vpc_id = 123456
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -21,13 +27,13 @@ description: |-
### Read-Only
- `route_entries` (List of Object) Submit null to leave unaltered, submit an empty list to clear all route entries. It is not possible to PATCH individual route entries, to alter a route entry submit the entire list of route entries you wish to save. (see [below for nested schema](#nestedatt--route_entries))
- `route_entries` (Attributes List) The route entries that control how network traffic is directed through the VPC environment. (see [below for nested schema](#nestedatt--route_entries))
<a id="nestedatt--route_entries"></a>
### Nested Schema for `route_entries`
Read-Only:
- `description` (String)
- `destination` (String)
- `router` (String)
- `description` (String) An optional description for the route.
- `destination` (String) The destination address for this route entry. This may be in CIDR format.
- `router` (String) The server that will receive traffic sent to the destination property in this VPC.
+24
View File
@@ -10,7 +10,31 @@ description: |-
## Example Usage
```terraform
terraform {
required_providers {
binarylane = {
source = "oscarhermoso/binarylane"
}
}
}
provider "binarylane" {
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
}
# Create a new server
resource "binarylane_server" "example" {
name = "tf-example-basic"
region = "per" # or "syd", "mel", "bne", "sin"
image = "ubuntu-24.04"
size = "std-min"
public_ipv4_count = 1
# ...
}
```
<!-- schema generated by tfplugindocs -->
## Schema
+25 -1
View File
@@ -10,7 +10,23 @@ description: |-
## Example Usage
```terraform
resource "binarylane_server" "example" {
count = 2
# ...
}
resource "binarylane_load_balancer" "example" {
name = "tf-example-lb"
forwarding_rules = [{ entry_protocol = "https" }]
server_ids = [
binarylane_server.example.0.id,
binarylane_server.example.1.id
]
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -36,7 +52,7 @@ description: |-
Required:
- `entry_protocol` (String) The protocol that traffic must match for this load balancer to forward traffic according to this rule.
- `entry_protocol` (String) The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
<a id="nestedatt--health_check"></a>
@@ -54,3 +70,11 @@ Optional:
Optional:
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
## Import
Import is supported using the following syntax:
```shell
terraform import binarylane_load_balancer.example "<name or id>"
```
+37 -6
View File
@@ -3,35 +3,58 @@
page_title: "binarylane_server Resource - terraform-provider-binarylane"
subcategory: ""
description: |-
Provides a Binary Lane Server resource. This can be used to create and delete servers.
---
# binarylane_server (Resource)
Provides a Binary Lane Server resource. This can be used to create and delete servers.
## Example Usage
```terraform
resource "binarylane_server" "example" {
name = "tf-example"
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
# Accepts a cloud-init script or cloud-config YAML file to configure the server
# See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
user_data = file("./init.sh")
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### 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.
- `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.
- `public_ipv4_count` (Number) The number of public IPv4 addresses to assign to the server.
- `region` (String) The slug of the selected region.
- `size` (String) The slug of the selected size.
### Optional
- `backups` (Boolean) If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.
- `backups` (Boolean) If `true` this will enable two daily backups for the server. By default, backups are disabled.
- `disk` (Number) The total storage in GB for this server. Leave null to accept the default for the size Valid values:
- must be a multiple of 5
- \> 60 GB must be a multiple of 10
- \> 200 GB must be a multiple of 100
- `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
- \> 16384 MB must be a multiple of 2048
- \> 24576 MB must be a multiple of 4096
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
- `password` (String, Sensitive) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions. If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
- `source_and_destination_check` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled (which is `true` by default), your server will only be able to send or receive packets that are directly addressed to one of the IP addresses associated with the Cloud Server. Generally, this is desirable behaviour because it prevents IP conflicts and other hard-to-diagnose networking faults due to incorrect network configuration. When `source_and_destination_check` is `false`, your Cloud Server will be able to send and receive packets addressed to any server. This is typically used when you want to use your Cloud Server as a VPN endpoint, a NAT server to provide internet access, or IP forwarding.
- `ssh_keys` (List of Number) This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
- `user_data` (String) 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.
- `user_data` (String) A script or cloud-config YAML file to configure the server. Can only be specified if the OS image supports UserData (i.e. not Windows). See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
### Read-Only
@@ -49,3 +72,11 @@ Optional:
- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
## Import
Import is supported using the following syntax:
```shell
terraform import binarylane_server.example "<server name or id>"
```
+28
View File
@@ -10,7 +10,27 @@ description: |-
## Example Usage
```terraform
resource "binarylane_server" "example" {
# ...
}
resource "binarylane_server_firewall_rules" "example" {
server_id = binarylane_server.example.id
firewall_rules = [
{
description = "Allow SSH"
protocol = "tcp"
source_addresses = ["0.0.0.0/0"]
destination_addresses = [binarylane_server.example.private_ipv4_addresses.0]
destination_ports = ["22"]
action = "accept"
}
]
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -34,3 +54,11 @@ Optional:
- `description` (String) A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. "Block access to RDP" or "Allow access from HQ".
- `destination_ports` (List of String) The destination ports to match for this rule. Leave null or empty to match on all ports.
## Import
Import is supported using the following syntax:
```shell
terraform import binarylane_server_firewall_rules.example "<server name or id>"
```
+24 -2
View File
@@ -10,7 +10,21 @@ description: |-
## Example Usage
```terraform
resource "binarylane_ssh_key" "example" {
name = "tf-example"
# Generate with: ssh-keygen -t ed25519 -C "name@example.com"
public_key = file("~/.ssh/id_ed25519.pub")
}
resource "binarylane_server" "example" {
# ...
ssh_keys = [binarylane_ssh_key.example.id]
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -22,9 +36,17 @@ description: |-
### Optional
- `default` (Boolean) Optional: If true this will be added to all new server installations (if we support SSH Key injection for the server's operating system).
- `id` (Number) The ID or fingerprint of the SSH Key to fetch.
- `default` (Boolean) If `true`, this SSH key will be included on all new server installations (if the operating system supports SSH key injection). Default is `false`.
### Read-Only
- `fingerprint` (String) The fingerprint of the SSH key.
- `id` (Number) The numeric ID of the SSH key.
## Import
Import is supported using the following syntax:
```shell
terraform import binarylane_ssh_key.example "<SSH Key ID or fingerprint>"
```
+20
View File
@@ -10,7 +10,19 @@ description: |-
## Example Usage
```terraform
resource "binarylane_vpc" "example" {
name = "tf-example-vpc"
ip_range = "10.240.0.0/16"
}
resource "binarylane_server" "example" {
# ...
vpc_id = binarylane_vpc.example.id
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -26,3 +38,11 @@ description: |-
### Read-Only
- `id` (Number) The target vpc id.
## Import
Import is supported using the following syntax:
```shell
terraform import binarylane_vpc.example "<vpc_id>"
```
+36
View File
@@ -10,7 +10,35 @@ description: |-
## Example Usage
```terraform
resource "binarylane_vpc" "example" {
name = "tf-example-vpc"
ip_range = "10.240.0.0/16"
}
resource "binarylane_server" "web" {
# ...
vpc_id = binarylane_vpc.example.id
}
resource "binarylane_vpc_route_entries" "example" {
vpc_id = binarylane_vpc.example.id
route_entries = [
{
description = "NAT"
destination = "0.0.0.0/0"
router = binarylane_server.web.private_ipv4_addresses.0
},
{
description = "VPN"
destination = "192.168.1.0/24"
router = binarylane_server.vpn.private_ipv4_addresses.0
}
]
}
```
<!-- schema generated by tfplugindocs -->
## Schema
@@ -34,3 +62,11 @@ Required:
Optional:
- `description` (String) An optional description for the route.
## Import
Import is supported using the following syntax:
```shell
terraform import binarylane_vpc_route_entries.example "<vpc_id>"
```
@@ -0,0 +1,3 @@
data "binarylane_load_balancer" "example" {
id = 123456
}
@@ -0,0 +1,3 @@
data "binarylane_server" "example" {
id = 123456
}
@@ -0,0 +1,3 @@
data "binarylane_server_firewall_rules" "example" {
server_id = 123456
}
@@ -0,0 +1,3 @@
data "binarylane_ssh_key" "example" {
id = 123456
}
@@ -0,0 +1,3 @@
data "binarylane_vpc" "example" {
id = 123456
}
@@ -0,0 +1,3 @@
data "binarylane_vpc_route_entries" "example" {
vpc_id = 123456
}
+21
View File
@@ -0,0 +1,21 @@
terraform {
required_providers {
binarylane = {
source = "oscarhermoso/binarylane"
}
}
}
provider "binarylane" {
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
}
# Create a new server
resource "binarylane_server" "example" {
name = "tf-example-basic"
region = "per" # or "syd", "mel", "bne", "sin"
image = "ubuntu-24.04"
size = "std-min"
public_ipv4_count = 1
# ...
}
@@ -0,0 +1 @@
terraform import binarylane_load_balancer.example "<name or id>"
@@ -0,0 +1,13 @@
resource "binarylane_server" "example" {
count = 2
# ...
}
resource "binarylane_load_balancer" "example" {
name = "tf-example-lb"
forwarding_rules = [{ entry_protocol = "https" }]
server_ids = [
binarylane_server.example.0.id,
binarylane_server.example.1.id
]
}
@@ -0,0 +1 @@
terraform import binarylane_server.example "<server name or id>"
@@ -0,0 +1,11 @@
resource "binarylane_server" "example" {
name = "tf-example"
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
# Accepts a cloud-init script or cloud-config YAML file to configure the server
# See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
user_data = file("./init.sh")
}
@@ -0,0 +1 @@
terraform import binarylane_server_firewall_rules.example "<server name or id>"
@@ -0,0 +1,17 @@
resource "binarylane_server" "example" {
# ...
}
resource "binarylane_server_firewall_rules" "example" {
server_id = binarylane_server.example.id
firewall_rules = [
{
description = "Allow SSH"
protocol = "tcp"
source_addresses = ["0.0.0.0/0"]
destination_addresses = [binarylane_server.example.private_ipv4_addresses.0]
destination_ports = ["22"]
action = "accept"
}
]
}
@@ -0,0 +1 @@
terraform import binarylane_ssh_key.example "<SSH Key ID or fingerprint>"
@@ -0,0 +1,11 @@
resource "binarylane_ssh_key" "example" {
name = "tf-example"
# Generate with: ssh-keygen -t ed25519 -C "name@example.com"
public_key = file("~/.ssh/id_ed25519.pub")
}
resource "binarylane_server" "example" {
# ...
ssh_keys = [binarylane_ssh_key.example.id]
}
@@ -0,0 +1 @@
terraform import binarylane_vpc.example "<vpc_id>"
@@ -0,0 +1,9 @@
resource "binarylane_vpc" "example" {
name = "tf-example-vpc"
ip_range = "10.240.0.0/16"
}
resource "binarylane_server" "example" {
# ...
vpc_id = binarylane_vpc.example.id
}
@@ -0,0 +1 @@
terraform import binarylane_vpc_route_entries.example "<vpc_id>"
@@ -0,0 +1,25 @@
resource "binarylane_vpc" "example" {
name = "tf-example-vpc"
ip_range = "10.240.0.0/16"
}
resource "binarylane_server" "web" {
# ...
vpc_id = binarylane_vpc.example.id
}
resource "binarylane_vpc_route_entries" "example" {
vpc_id = binarylane_vpc.example.id
route_entries = [
{
description = "NAT"
destination = "0.0.0.0/0"
router = binarylane_server.web.private_ipv4_addresses.0
},
{
description = "VPN"
destination = "192.168.1.0/24"
router = binarylane_server.vpn.private_ipv4_addresses.0
}
]
}
+7 -7
View File
@@ -6,10 +6,10 @@ 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.20.0
github.com/hashicorp/terraform-plugin-docs v0.20.1
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-framework-timeouts v0.5.0
github.com/hashicorp/terraform-plugin-framework-validators v0.16.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.11.0
@@ -99,13 +99,13 @@ require (
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.29.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.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-20241021214115-324edc3d5d38 // indirect
+14 -14
View File
@@ -145,14 +145,14 @@ github.com/hashicorp/terraform-plugin-codegen-openapi v0.3.0 h1:IKpc337XKk50QyQP
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.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-docs v0.20.1 h1:Fq7E/HrU8kuZu3hNliZGwloFWSYfWEOWnylFhYQIoys=
github.com/hashicorp/terraform-plugin-docs v0.20.1/go.mod h1:Yz6HoK7/EgzSrHPB9J/lWFzwl9/xep2OPnc5jaJDV90=
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-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.16.0 h1:O9QqGoYDzQT7lwTXUsZEtgabeWW96zUBh47Smn2lkFA=
github.com/hashicorp/terraform-plugin-framework-validators v0.16.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=
@@ -319,8 +319,8 @@ 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.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
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=
@@ -343,8 +343,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -370,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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.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=
@@ -382,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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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=
File diff suppressed because it is too large Load Diff
+3
View File
@@ -44,3 +44,6 @@ cat <<<$(jq '.components.schemas.CreateLoadBalancerRequest.properties.forwarding
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
# Edit description here because it's hard to override nested schema properties
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol.description = "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\"."' $OPENAPI_FILE) >$OPENAPI_FILE
File diff suppressed because it is too large Load Diff
+5 -42
View File
@@ -421,6 +421,7 @@ const (
PcI440fx4point2 VmMachineType = "pc_i440fx_4point2"
PcI440fx5point0 VmMachineType = "pc_i440fx_5point0"
PcI440fx5point1 VmMachineType = "pc_i440fx_5point1"
PcI440fx7point2 VmMachineType = "pc_i440fx_7point2"
)
// Account defines model for Account.
@@ -492,7 +493,7 @@ type Action struct {
// ResourceType The resource type (if any) associated with this action.
ResourceType *ResourceType `json:"resource_type"`
// ResultData Returned information from a completed action. For example: a successful completed 'ping' action will have the ping value in ms in this field.
// ResultData Returned information from a completed action. For example: a successful completed 'uptime' action will have the uptime value in this field.
ResultData *string `json:"result_data"`
// StartedAt The timestamp in ISO8601 format of when processing of this action started.
@@ -714,7 +715,7 @@ type BackupInfo struct {
// BackupDisks A list of the individual disks that make up this backup.
BackupDisks *[]BackupDisk `json:"backup_disks,omitempty"`
// Iso If this is true the backup is an ISO image and cannot be restored. ISO images may only be attached for use as a boot disk or an additional disk.
// Iso If this is true the backup is an ISO image and cannot be restored or downloaded. ISO images may only be attached for use as a boot disk or an additional disk.
Iso *bool `json:"iso,omitempty"`
// Locked If this is true the backup is locked and cannot be replaced.
@@ -1502,24 +1503,9 @@ type EnableIpv6 struct {
// EnableIpv6Type defines model for EnableIpv6.Type.
type EnableIpv6Type string
// FailoverIpsRequest defines model for FailoverIpsRequest.
type FailoverIpsRequest struct {
// FailoverIps The list of IP Failover addresses to assign to this server. This overwrites the current list, so any current IP Failover addresses that are omitted will be removed from the server.
FailoverIps []string `json:"failover_ips"`
}
// FailoverIpsResponse defines model for FailoverIpsResponse.
type FailoverIpsResponse struct {
FailoverIps *[]string `json:"failover_ips,omitempty"`
Links *Links `json:"links"`
// Meta Contains metadata about the response, currently this includes the total number of items.
Meta Meta `json:"meta"`
}
// ForwardingRule defines model for ForwardingRule.
type ForwardingRule struct {
// EntryProtocol The protocol that traffic must match for this load balancer to forward traffic according to this rule.
// EntryProtocol The protocol that traffic must match for the load balancer to forward it. Valid values are "http" and "https".
EntryProtocol LoadBalancerRuleProtocol `json:"entry_protocol" tfsdk:"entry_protocol"`
}
@@ -3029,6 +3015,7 @@ type VideoDevice string
// | pc_i440fx_4point2 | PC I440 FX 4.2 |
// | pc_i440fx_5point0 | PC I440 FX 5.0 |
// | pc_i440fx_5point1 | PC I440 FX 5.1 |
// | pc_i440fx_7point2 | PC I440 FX 7.2 |
type VmMachineType string
// Vpc defines model for Vpc.
@@ -3214,27 +3201,6 @@ type PutDomainsDomainNameRecordsRecordIdParamsDomainName0 = int
// PutDomainsDomainNameRecordsRecordIdParamsDomainName1 defines parameters for PutDomainsDomainNameRecordsRecordId.
type PutDomainsDomainNameRecordsRecordIdParamsDomainName1 = string
// GetFailoverIpsServerIdParams defines parameters for GetFailoverIpsServerId.
type GetFailoverIpsServerIdParams struct {
// Page The selected page. Page numbering starts at 1
Page *int32 `form:"page,omitempty" json:"page,omitempty"`
// PerPage The number of results to show per page.
PerPage *int32 `form:"per_page,omitempty" json:"per_page,omitempty"`
}
// PostFailoverIpsServerIdJSONBody defines parameters for PostFailoverIpsServerId.
type PostFailoverIpsServerIdJSONBody = FailoverIpsRequest
// GetFailoverIpsServerIdAvailableParams defines parameters for GetFailoverIpsServerIdAvailable.
type GetFailoverIpsServerIdAvailableParams struct {
// Page The selected page. Page numbering starts at 1
Page *int32 `form:"page,omitempty" json:"page,omitempty"`
// PerPage The number of results to show per page.
PerPage *int32 `form:"per_page,omitempty" json:"per_page,omitempty"`
}
// GetImagesParams defines parameters for GetImages.
type GetImagesParams struct {
// Type Queries for distribution will include images that have pre-installed applications.
@@ -3522,9 +3488,6 @@ type PostDomainsDomainNameRecordsJSONRequestBody = PostDomainsDomainNameRecordsJ
// PutDomainsDomainNameRecordsRecordIdJSONRequestBody defines body for PutDomainsDomainNameRecordsRecordId for application/json ContentType.
type PutDomainsDomainNameRecordsRecordIdJSONRequestBody = PutDomainsDomainNameRecordsRecordIdJSONBody
// PostFailoverIpsServerIdJSONRequestBody defines body for PostFailoverIpsServerId for application/json ContentType.
type PostFailoverIpsServerIdJSONRequestBody = PostFailoverIpsServerIdJSONBody
// PostLoadBalancersJSONRequestBody defines body for PostLoadBalancers for application/json ContentType.
type PostLoadBalancersJSONRequestBody = PostLoadBalancersJSONBody
@@ -93,7 +93,7 @@ func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRe
return
}
diags := SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
+4 -4
View File
@@ -152,7 +152,7 @@ func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRe
return
}
diags = SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
@@ -245,7 +245,7 @@ func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadReques
return
}
diags := SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags := setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
@@ -309,7 +309,7 @@ func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRe
return
}
diags = SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
diags = setLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
@@ -414,7 +414,7 @@ func (r *loadBalancerResource) ImportState(
resp.Diagnostics.Append(diags...)
}
func SetLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancerModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
func setLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancerModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
var diags, diag diag.Diagnostics
data.Id = types.Int64Value(*lb.Id)
+95 -8
View File
@@ -30,6 +30,8 @@ type serverDataModel struct {
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
Permalink types.String `tfsdk:"permalink"`
Memory types.Int32 `tfsdk:"memory"`
Disk types.Int32 `tfsdk:"disk"`
}
func (d *serverDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
@@ -76,10 +78,58 @@ func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
Required: true, // ID is required to find the server
}
nameDescription := "The hostname of your server, such as vps01.yourcompany.com."
resp.Schema.Attributes["name"] = &schema.StringAttribute{
Description: nameDescription,
MarkdownDescription: nameDescription,
Optional: false,
Required: false,
Computed: true,
}
backupsDescription := "If `true`, the server will be backed up twice per day. By default, backups are disabled."
resp.Schema.Attributes["backups"] = &schema.BoolAttribute{
Description: backupsDescription,
MarkdownDescription: backupsDescription,
Optional: false,
Required: false,
Computed: true,
}
sshKeysDescription := "This is a list of SSH key ids that were added to the server during creation."
resp.Schema.Attributes["ssh_keys"] = &schema.ListAttribute{
ElementType: types.Int64Type,
Description: sshKeysDescription,
MarkdownDescription: sshKeysDescription,
Optional: false,
Required: false,
Computed: true,
}
userDataDescription := "A script or cloud-config YAML file to configure the server."
resp.Schema.Attributes["user_data"] = &schema.StringAttribute{
Description: userDataDescription,
MarkdownDescription: userDataDescription,
Optional: false,
Required: false,
Computed: true,
}
vpcIdDescription := "ID of the Virtual Private Cloud (VPC) the server is connected to."
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
Description: vpcIdDescription,
MarkdownDescription: vpcIdDescription,
Optional: false,
Required: false,
Computed: true,
}
// Additional attributes
resp.Schema.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",
Optional: false,
Required: false,
Computed: true,
}
@@ -88,6 +138,8 @@ func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
Description: publicIpv4AddressesDescription,
MarkdownDescription: publicIpv4AddressesDescription,
ElementType: types.StringType,
Optional: false,
Required: false,
Computed: true,
}
@@ -96,6 +148,26 @@ func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
Description: privateIpv4AddressesDescription,
MarkdownDescription: privateIpv4AddressesDescription,
ElementType: types.StringType,
Optional: false,
Required: false,
Computed: true,
}
memoryDescription := "The amount of memory in MB assigned to the server."
resp.Schema.Attributes["memory"] = &schema.Int32Attribute{
Description: memoryDescription,
MarkdownDescription: memoryDescription,
Optional: false,
Required: false,
Computed: true,
}
diskDescription := "The amount of storage in GB assigned to the server."
resp.Schema.Attributes["disk"] = &schema.Int32Attribute{
Description: diskDescription,
MarkdownDescription: diskDescription,
Optional: false,
Required: false,
Computed: true,
}
}
@@ -128,16 +200,19 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
return
}
// Set data values
data.Id = types.Int64Value(*serverResp.JSON200.Server.Id)
data.Name = types.StringValue(*serverResp.JSON200.Server.Name)
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
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)
var publicIpv4Addresses []string
var privateIpv4Addresses []string
data.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
data.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
publicIpv4Addresses := []string{}
privateIpv4Addresses := []string{}
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
if v4address.Type == "public" {
@@ -146,10 +221,22 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
}
}
data.PublicIpv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
resp.Diagnostics.Append(diags...)
data.PrivateIPv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
resp.Diagnostics.Append(diags...)
tfPublicIpv4Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
diags.Append(diag...)
if diag.HasError() {
data.PublicIpv4Addresses = types.ListUnknown(data.PublicIpv4Addresses.ElementType(ctx))
} else {
data.PublicIpv4Addresses = tfPublicIpv4Addresses
}
tfPrivateIpv4Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
diags.Append(diag...)
if diag.HasError() {
data.PrivateIPv4Addresses = types.ListUnknown(data.PrivateIPv4Addresses.ElementType(ctx))
} else {
data.PrivateIPv4Addresses = tfPrivateIpv4Addresses
}
// Get user data script
userDataResp, err := d.bc.client.GetServersServerIdUserDataWithResponse(ctx, data.Id.ValueInt64())
@@ -43,7 +43,7 @@ func (d *serverFirewallRulesDataSource) Schema(ctx context.Context, req datasour
return
}
resp.Schema = *ds
resp.Schema.Description = "TODO"
// resp.Schema.Description = "TODO"
}
func (d *serverFirewallRulesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
@@ -59,7 +59,6 @@ func (r *serverFirewallRulesResource) Metadata(ctx context.Context, req resource
func (r *serverFirewallRulesResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.ServerFirewallRulesResourceSchema(ctx)
// resp.Schema.Description = "TODO"
// Overrides
serverId := resp.Schema.Attributes["server_id"]
+366 -94
View File
@@ -18,6 +18,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -49,13 +50,11 @@ type serverResource struct {
bc *BinarylaneClient
}
type serverModel struct {
resources.ServerModel
type serverResourceModel struct {
serverDataModel
PublicIpv4Count types.Int32 `tfsdk:"public_ipv4_count"`
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
SourceAndDestinationCheck types.Bool `tfsdk:"source_and_destination_check"`
Permalink types.String `tfsdk:"permalink"`
Password types.String `tfsdk:"password"`
PasswordChangeSupported types.Bool `tfsdk:"password_change_supported"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
@@ -85,7 +84,6 @@ func (r *serverResource) Metadata(ctx context.Context, req resource.MetadataRequ
func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.ServerResourceSchema(ctx)
resp.Schema.Description = "Provides a Binary Lane Server resource. This can be used to create and delete servers."
// Overrides
id := resp.Schema.Attributes["id"]
@@ -101,10 +99,11 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
},
}
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 := resp.Schema.Attributes["image"]
resp.Schema.Attributes["image"] = &schema.StringAttribute{
Description: image.GetDescription(),
MarkdownDescription: image.GetMarkdownDescription(),
Description: imageDescription,
MarkdownDescription: imageDescription,
Required: image.IsRequired(),
Optional: image.IsOptional(),
Computed: image.IsComputed(),
@@ -113,14 +112,13 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
},
}
backups := resp.Schema.Attributes["backups"]
backupsDescription := "If `true` this will enable two daily backups for the server. By default, backups are disabled."
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
PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()}, // TODO (#37)
Description: backupsDescription,
MarkdownDescription: backupsDescription,
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false), // Add default to backups
}
user_data := resp.Schema.Attributes["user_data"]
@@ -136,8 +134,7 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
Description: vpcId.GetDescription(),
MarkdownDescription: vpcId.GetMarkdownDescription(),
Optional: vpcId.IsOptional(),
Computed: false, // VPC ID is not computed, defined at creation
PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()},
Computed: false, // vpc_id is not computed, defined at creation
}
portBlocking := resp.Schema.Attributes["port_blocking"]
@@ -147,7 +144,19 @@ 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()},
}
region := resp.Schema.Attributes["region"].(schema.StringAttribute)
resp.Schema.Attributes["region"] = &schema.StringAttribute{
Description: region.GetDescription(),
MarkdownDescription: region.GetMarkdownDescription(),
Optional: region.IsOptional(),
Computed: region.IsComputed(),
Required: region.IsRequired(),
Validators: region.StringValidators(),
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
}
sshKeys := resp.Schema.Attributes["ssh_keys"]
@@ -162,6 +171,20 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
},
}
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 := resp.Schema.Attributes["user_data"]
resp.Schema.Attributes["user_data"] = &schema.StringAttribute{
Description: userDataDescription,
MarkdownDescription: userDataDescription,
Required: userData.IsRequired(),
Optional: userData.IsOptional(),
Computed: userData.IsComputed(),
Validators: []validator.String{
stringvalidator.LengthAtMost(65536),
},
}
// Additional attributes
pwDescription :=
"If this is provided the specified or default remote user's account password will be set to this value. " +
@@ -173,20 +196,18 @@ 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
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
}
publicIpv4CountDescription := "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."
publicIpv4CountDescription := "The number of public IPv4 addresses to assign to the server."
resp.Schema.Attributes["public_ipv4_count"] = &schema.Int32Attribute{
Description: publicIpv4CountDescription,
MarkdownDescription: publicIpv4CountDescription,
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),
int32validator.AtMost(8),
},
}
@@ -262,6 +283,53 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
},
}
memoryDescription := `The total memory in MB for this server. Leave null to accept the default size.`
memoryValidValues := "Valid values must be a multiple of 128. If the value is greater than 2048 MB, it must be a " +
"multiple of 1024. If the value is greater than 16384 MB, it must be a multiple of 2048. If the value is greater " +
"than 24576 MB, it must be a multiple of 4096."
memoryValidValuesMarkdown := ` Valid values:
- must be a multiple of 128
- \> 2048 MB must be a multiple of 1024
- \> 16384 MB must be a multiple of 2048
- \> 24576 MB must be a multiple of 4096`
resp.Schema.Attributes["memory"] = &schema.Int32Attribute{
Description: memoryDescription + memoryValidValues,
MarkdownDescription: memoryDescription + memoryValidValuesMarkdown,
Optional: true,
Required: false,
Computed: true,
Validators: []validator.Int32{
int32validator.AtLeast(128),
MultipleOfValidator{Multiple: 128},
MultipleOfValidator{Multiple: 1024, RangeFrom: 2048, RangeTo: 16384},
MultipleOfValidator{Multiple: 2048, RangeFrom: 16384, RangeTo: 24576},
MultipleOfValidator{Multiple: 4096, RangeFrom: 24576},
},
}
diskDescription := `The total storage in GB for this server. Leave null to accept the default for the size`
diskValidValues := "Valid values must be a multiple of 5. If the value is greater than 60 GB, it must be a multiple of 10. " +
"if the value is greater than 200 GB, it must be a multiple of 100. "
diskValidValuesMarkdown := ` Valid values:
- must be a multiple of 5
- \> 60 GB must be a multiple of 10
- \> 200 GB must be a multiple of 100`
resp.Schema.Attributes["disk"] = &schema.Int32Attribute{
Description: diskDescription + diskValidValues,
MarkdownDescription: diskDescription + diskValidValuesMarkdown,
Optional: true,
Required: false,
Computed: true,
Validators: []validator.Int32{
int32validator.AtLeast(20),
MultipleOfValidator{Multiple: 5},
MultipleOfValidator{Multiple: 10, RangeFrom: 60, RangeTo: 200},
MultipleOfValidator{Multiple: 100, RangeFrom: 200},
},
}
resp.Schema.Attributes["timeouts"] =
timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
@@ -270,12 +338,13 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
}
func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) {
var plan, state serverModel
var plan, state serverResourceModel
if req.Plan.Raw.IsNull() {
// Destruction plan, no modification needed
return
}
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
@@ -295,12 +364,17 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
// Creation plan, no further modification needed
return
}
// Read Terraform state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
if !plan.VpcId.Equal(state.VpcId) {
plan.PrivateIPv4Addresses = types.ListUnknown(plan.PrivateIPv4Addresses.ElementType(ctx))
}
// When IP count is changed, plan should show addition/removal of public IPs
plannedPublicIpV4Count := int(plan.PublicIpv4Count.ValueInt32())
plannedPublicIpAddresses := make([]attr.Value, plannedPublicIpV4Count)
@@ -333,12 +407,20 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
)
}
// 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
}
if (plan.Disk.IsNull() || plan.Disk.IsUnknown()) && plan.Size.Equal(state.Size) {
plan.Disk = state.Disk
}
// Save data into Terraform state
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
}
func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data serverModel
var data serverResourceModel
// Read Terraform plan data into the model
diags := req.Plan.Get(ctx, &data)
@@ -374,6 +456,14 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
Options: &binarylane.SizeOptionsRequest{
Ipv4Addresses: data.PublicIpv4Count.ValueInt32Pointer(),
},
Backups: data.Backups.ValueBoolPointer(),
}
if !data.Memory.IsNull() && !data.Memory.IsUnknown() {
body.Options.Memory = data.Memory.ValueInt32Pointer()
}
if !data.Disk.IsNull() && !data.Disk.IsUnknown() {
body.Options.Disk = data.Disk.ValueInt32Pointer()
}
if data.Password.IsNull() {
@@ -428,6 +518,8 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
data.PasswordChangeSupported = types.BoolValue(*serverResp.JSON200.Server.PasswordChangeSupported)
data.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
data.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
plannedSourceDestCheck := data.SourceAndDestinationCheck
serverRespSourceDestCheck := types.BoolPointerValue(serverResp.JSON200.Server.Networks.SourceAndDestinationCheck)
data.SourceAndDestinationCheck = serverRespSourceDestCheck
@@ -464,7 +556,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
}
func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data serverModel
var data serverResourceModel
// Read Terraform prior state data into the model
diags := req.State.Get(ctx, &data)
@@ -492,36 +584,8 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
return
}
data.Id = types.Int64Value(*serverResp.JSON200.Server.Id)
data.Name = types.StringValue(*serverResp.JSON200.Server.Name)
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
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)
data.SourceAndDestinationCheck = types.BoolPointerValue(serverResp.JSON200.Server.Networks.SourceAndDestinationCheck)
publicIpv4Addresses := []string{}
privateIpv4Addresses := []string{}
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
if v4address.Type == "public" {
publicIpv4Addresses = append(publicIpv4Addresses, v4address.IpAddress)
} else {
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
}
}
data.PublicIpv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
resp.Diagnostics.Append(diags...)
data.PrivateIPv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
resp.Diagnostics.Append(diags...)
data.PublicIpv4Count = types.Int32Value(int32(len(publicIpv4Addresses)))
if resp.Diagnostics.HasError() {
return
}
diag := setServerResourceState(ctx, &data, serverResp.JSON200)
resp.Diagnostics.Append(diag...)
// Get user data script
userDataResp, err := r.bc.client.GetServersServerIdUserDataWithResponse(ctx, data.Id.ValueInt64())
@@ -546,7 +610,7 @@ 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 plan, state serverModel
var plan, state serverResourceModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
@@ -561,6 +625,35 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
defer cancel()
rebuildNeeded := len(attrsRequiringRebuild(&plan, &state)) > 0
refreshNeeded := false
defer (func() {
if !refreshNeeded {
return
}
serverResp, err := r.bc.client.GetServersServerIdWithResponse(ctx, state.Id.ValueInt64())
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error reading server: id=%s, name=%s", state.Id.String(), state.Name.ValueString()),
err.Error(),
)
return
}
if serverResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
fmt.Sprintf("Unexpected HTTP status code %s reading server: name=%s, id=%s", serverResp.Status(), state.Name.ValueString(), state.Id.String()),
string(serverResp.Body),
)
return
}
diag := setServerResourceState(ctx, &state, serverResp.JSON200)
resp.Diagnostics.Append(diag...)
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
})()
// Rename
if !plan.Name.Equal(state.Name) && !rebuildNeeded {
@@ -597,19 +690,78 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
state.Name = types.StringValue(plan.Name.ValueString())
}
// Change network
if !plan.VpcId.Equal(state.VpcId) {
networkResp, err := r.bc.client.PostServersServerIdActionsChangeNetworkWithResponse(
ctx,
state.Id.ValueInt64(),
binarylane.PostServersServerIdActionsChangeNetworkJSONRequestBody{
Type: "change_network",
VpcId: plan.VpcId.ValueInt64Pointer(),
},
)
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error changing network for server: server_id=%s", state.Id.String()),
err.Error())
return
}
if networkResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code changing network for server",
fmt.Sprintf("Received %s changing network for server: server_id=%s. Details: %s", networkResp.Status(), state.Id.String(), networkResp.Body))
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *networkResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for server to change network", err.Error())
return
}
state.VpcId = plan.VpcId
refreshNeeded = true // Refresh to get new private IP addresses
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
// Resize operation
if !plan.Size.Equal(state.Size) || !plan.PublicIpv4Count.Equal(state.PublicIpv4Count) || !plan.Image.Equal(state.Image) {
if !plan.Size.Equal(state.Size) ||
!plan.Memory.IsNull() && !plan.Memory.IsUnknown() && !plan.Memory.Equal(state.Memory) ||
!plan.Disk.IsNull() && !plan.Disk.IsUnknown() && !plan.Disk.Equal(state.Disk) ||
!plan.Image.Equal(state.Image) ||
!plan.PublicIpv4Count.Equal(state.PublicIpv4Count) {
resizeReq := &binarylane.PostServersServerIdActionsResizeJSONRequestBody{
Type: "resize",
Type: "resize",
Options: &binarylane.ChangeSizeOptionsRequest{},
}
if !plan.Size.Equal(state.Size) {
if !plan.Size.Equal(state.Size) ||
!plan.Memory.IsNull() && !plan.Memory.IsUnknown() && !plan.Memory.Equal(state.Memory) ||
!plan.Disk.IsNull() && !plan.Disk.IsUnknown() && !plan.Disk.Equal(state.Disk) {
resizeReq.Size = plan.Size.ValueStringPointer()
state.Size = plan.Size
}
if !plan.PublicIpv4Count.Equal(state.PublicIpv4Count) {
resizeReq.Options = &binarylane.ChangeSizeOptionsRequest{
Ipv4Addresses: plan.PublicIpv4Count.ValueInt32Pointer(),
if !plan.Memory.IsUnknown() && !plan.Memory.IsNull() {
resizeReq.Options.Memory = plan.Memory.ValueInt32Pointer()
}
if !plan.Disk.IsNull() && !plan.Disk.IsUnknown() {
resizeReq.Options.Disk = plan.Disk.ValueInt32Pointer()
}
state.Size = plan.Size
state.Memory = plan.Memory
state.Disk = plan.Disk
}
if !plan.Image.Equal(state.Image) {
resizeReq.ChangeImage = &binarylane.ChangeImage{
Image: plan.Image.ValueStringPointer(),
}
state.Image = plan.Image
}
if !plan.PublicIpv4Count.Equal(state.PublicIpv4Count) {
resizeReq.Options.Ipv4Addresses = plan.PublicIpv4Count.ValueInt32Pointer()
if plan.PublicIpv4Count.ValueInt32() < state.PublicIpv4Count.ValueInt32() {
currentIps := []string{}
diags := state.PublicIpv4Addresses.ElementsAs(ctx, &currentIps, false)
@@ -627,12 +779,6 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
state.PublicIpv4Count = plan.PublicIpv4Count
state.PublicIpv4Addresses = plan.PublicIpv4Addresses
}
if !plan.Image.Equal(state.Image) {
resizeReq.ChangeImage = &binarylane.ChangeImage{
Image: plan.Image.ValueStringPointer(),
}
state.Image = plan.Image
}
tflog.Info(ctx, fmt.Sprintf("Resizing server: server_id=%s", state.Id.String()))
@@ -654,38 +800,22 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
fmt.Sprintf("Received %s resizing server: server_id=%s. Details: %s", resizeResp.Status(), state.Id.String(), resizeResp.Body))
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *resizeResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for server to be resized", err.Error())
return
}
// Success
if state.PublicIpv4Addresses.IsUnknown() || listContainsUnknown(ctx, state.PublicIpv4Addresses) {
// 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
}
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...)
if state.PublicIpv4Addresses.IsUnknown() || listContainsUnknown(ctx, state.PublicIpv4Addresses) || state.Memory.IsNull() || state.Memory.IsUnknown() {
refreshNeeded = true
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
// Rebuild operation
@@ -737,6 +867,34 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
} else
// Reset Password (only needed if server didn't rebuild)
if !plan.Password.Equal(state.Password) {
passwordResp, err := r.bc.client.PostServersServerIdActionsPasswordResetWithResponse(ctx, state.Id.ValueInt64(),
binarylane.PasswordReset{
Type: "password_reset",
Password: plan.Password.ValueStringPointer(),
},
)
if err != nil {
resp.Diagnostics.AddError("Error resetting password", err.Error())
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *passwordResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for password reset", err.Error())
return
}
state.Password = plan.Password
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
// Check source_and_destination_check
@@ -751,13 +909,77 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
state.SourceAndDestinationCheck = plan.SourceAndDestinationCheck
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
// Enable or disable backups
if !plan.Backups.Equal(state.Backups) {
if plan.Backups.ValueBool() {
backupResp, err := r.bc.client.PostServersServerIdActionsEnableBackupsWithResponse(ctx, state.Id.ValueInt64(), binarylane.EnableBackups{
Type: "enable_backups",
})
if err != nil {
resp.Diagnostics.AddError("Error enabling backups", err.Error())
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *backupResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for backups to be enabled", err.Error())
return
}
} else {
backupResp, err := r.bc.client.PostServersServerIdActionsDisableBackupsWithResponse(ctx, state.Id.ValueInt64(), binarylane.DisableBackups{
Type: "disable_backups",
})
if err != nil {
resp.Diagnostics.AddError("Error disabling backups", err.Error())
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *backupResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for backups to be disabled", err.Error())
return
}
}
state.Backups = plan.Backups
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
}
// Change port blocking
if !plan.PortBlocking.Equal(state.PortBlocking) {
portBlockingResp, err := r.bc.client.PostServersServerIdActionsChangePortBlockingWithResponse(ctx, state.Id.ValueInt64(),
binarylane.ChangePortBlocking{
Type: "change_port_blocking",
Enabled: plan.PortBlocking.ValueBool(),
},
)
if err != nil {
resp.Diagnostics.AddError("Error changing \"port_blocking\" attribute", err.Error())
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *portBlockingResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for \"port_blocking\" attribute to change", err.Error())
return
}
state.PortBlocking = plan.PortBlocking
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
}
func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data serverModel
var data serverResourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
@@ -871,7 +1093,7 @@ func (r *serverResource) waitForServerAction(ctx context.Context, serverId int64
}
}
func attrsRequiringRebuild(plan *serverModel, state *serverModel) []string {
func attrsRequiringRebuild(plan *serverResourceModel, state *serverResourceModel) []string {
attrs := []string{}
if !plan.SshKeys.Equal(state.SshKeys) {
@@ -917,3 +1139,53 @@ func (r *serverResource) updateSourceDestCheck(
return nil
}
func setServerResourceState(ctx context.Context, data *serverResourceModel, serverResp *binarylane.ServerResponse) diag.Diagnostics {
var diags diag.Diagnostics
data.Id = types.Int64Value(*serverResp.Server.Id)
data.Name = types.StringValue(*serverResp.Server.Name)
data.Image = types.StringValue(*serverResp.Server.Image.Slug)
data.Region = types.StringValue(*serverResp.Server.Region.Slug)
data.Size = types.StringValue(*serverResp.Server.Size.Slug)
data.Backups = types.BoolValue(serverResp.Server.NextBackupWindow != nil)
data.PortBlocking = types.BoolValue(serverResp.Server.Networks.PortBlocking)
data.VpcId = types.Int64PointerValue(serverResp.Server.VpcId)
data.Permalink = types.StringValue(*serverResp.Server.Permalink)
data.PasswordChangeSupported = types.BoolValue(*serverResp.Server.PasswordChangeSupported)
data.SourceAndDestinationCheck = types.BoolPointerValue(serverResp.Server.Networks.SourceAndDestinationCheck)
data.Memory = types.Int32Value(*serverResp.Server.Memory)
data.Disk = types.Int32Value(*serverResp.Server.Disk)
data.Backups = types.BoolValue(serverResp.Server.NextBackupWindow != nil)
publicIpv4Addresses := []string{}
privateIpv4Addresses := []string{}
for _, v4address := range serverResp.Server.Networks.V4 {
if v4address.Type == "public" {
publicIpv4Addresses = append(publicIpv4Addresses, v4address.IpAddress)
} else {
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
}
}
tfPublicIpv4Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
diags.Append(diag...)
if diag.HasError() {
data.PublicIpv4Addresses = types.ListUnknown(data.PublicIpv4Addresses.ElementType(ctx))
data.PublicIpv4Count = types.Int32Unknown()
} else {
data.PublicIpv4Addresses = tfPublicIpv4Addresses
data.PublicIpv4Count = types.Int32Value(int32(len(publicIpv4Addresses)))
}
tfPrivateIpv4Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
diags.Append(diag...)
if diag.HasError() {
data.PrivateIPv4Addresses = types.ListUnknown(data.PrivateIPv4Addresses.ElementType(ctx))
} else {
data.PrivateIPv4Addresses = tfPrivateIpv4Addresses
}
return diags
}
+80 -11
View File
@@ -16,13 +16,13 @@ import (
func TestServerResource(t *testing.T) {
// Must assign a password to the server or Binary Lane will send emails
pw_bytes := make([]byte, 12)
_, err := rand.Read(pw_bytes)
if err != nil {
t.Errorf("Failed to generate password: %s", err)
return
}
password := base64.URLEncoding.EncodeToString(pw_bytes)
pwBytes := make([]byte, 12)
rand.Read(pwBytes)
password := base64.URLEncoding.EncodeToString(pwBytes)
pwBytes2 := make([]byte, 12)
rand.Read(pwBytes2)
password2 := base64.URLEncoding.EncodeToString(pwBytes2)
sshPublicKeyInitial := GeneratePublicKey(t)
sshPublicKeyUpdated := GeneratePublicKey(t)
@@ -56,11 +56,14 @@ resource "binarylane_server" "test" {
region = "per"
image = "debian-11"
size = "std-min"
memory = 1152
password = "` + password + `"
vpc_id = binarylane_vpc.test.id
public_ipv4_count = 1
ssh_keys = [binarylane_ssh_key.initial.id]
source_and_destination_check = false
backups = true
port_blocking = false
user_data = <<EOT
#cloud-config
echo "Hello World" > /var/tmp/output.txt
@@ -80,6 +83,8 @@ data "binarylane_server" "test" {
resource.TestCheckResourceAttr("binarylane_server.test", "region", "per"),
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-11"),
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "1152"),
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "20"),
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_id"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "1"),
resource.TestCheckResourceAttr("binarylane_server.test", "password", password),
@@ -88,11 +93,12 @@ echo "Hello World" > /var/tmp/output.txt
`),
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", "port_blocking", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"),
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.initial", "id"),
resource.TestCheckResourceAttrSet("binarylane_server.test", "permalink"),
resource.TestCheckResourceAttr("binarylane_server.test", "source_and_destination_check", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "true"),
// Verify data source values
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
@@ -100,10 +106,15 @@ echo "Hello World" > /var/tmp/output.txt
resource.TestCheckResourceAttr("data.binarylane_server.test", "region", "per"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "debian-11"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "size", "std-min"),
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "vpc_id"),
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "permalink", "binarylane_server.test", "permalink"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "user_data", `#cloud-config
echo "Hello World" > /var/tmp/output.txt
`),
resource.TestCheckResourceAttr("data.binarylane_server.test", "memory", "1152"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "disk", "20"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "backups", "true"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "port_blocking", "false"),
),
},
// Test import by ID
@@ -146,11 +157,16 @@ resource "binarylane_server" "test" {
region = "per"
image = "debian-12"
size = "std-1vcpu"
disk = "45"
password = "` + password + `"
vpc_id = binarylane_vpc.test.id
vpc_id = null
public_ipv4_count = 0
ssh_keys = [binarylane_ssh_key.updated.id]
# source_and_destination_check = true # defaults to true
# source_and_destination_check = null # defaults to null
# backups = false # defaults to false
# port_blocking = true # defaults to true
user_data = <<EOT
#cloud-config
echo "Hello Whitespace" > /var/tmp/output.txt
@@ -162,12 +178,17 @@ EOT
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource-2"),
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-1vcpu"),
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "2048"),
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "45"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "0"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "0"),
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-12"),
resource.TestCheckNoResourceAttr("binarylane_server.test", "vpc_id"),
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"),
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.updated", "id"),
resource.TestCheckResourceAttr("binarylane_server.test", "source_and_destination_check", "true"),
resource.TestCheckNoResourceAttr("binarylane_server.test", "source_and_destination_check"),
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "port_blocking", "true"),
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", // test extra whitespace
`#cloud-config
echo "Hello Whitespace" > /var/tmp/output.txt
@@ -176,6 +197,54 @@ echo "Hello Whitespace" > /var/tmp/output.txt
`),
),
},
// Change password testing (Cannot run at same time as Rebuild operation, so it has it's own test)
{
Config: providerConfig + `
resource "binarylane_vpc" "test" {
name = "tf-test-server-resource"
ip_range = "10.240.0.0/16"
}
resource "binarylane_ssh_key" "initial" {
name = "tf-test-server-resource-initial"
public_key = "` + sshPublicKeyInitial + `"
default = true
}
resource "binarylane_ssh_key" "updated" {
name = "tf-test-server-resource-updated"
public_key = "` + sshPublicKeyUpdated + `"
default = true
}
resource "binarylane_server" "test" {
name = "tf-test-server-resource-2"
region = "per"
image = "debian-12"
size = "std-1vcpu"
disk = "45"
password = "` + password2 + `"
vpc_id = null
public_ipv4_count = 0
ssh_keys = [binarylane_ssh_key.updated.id]
# source_and_destination_check = null # defaults to null
# backups = false # defaults to false
# port_blocking = true # defaults to true
user_data = <<EOT
#cloud-config
echo "Hello Whitespace" > /var/tmp/output.txt
EOT
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource-2"),
resource.TestCheckResourceAttr("binarylane_server.test", "password", password2),
),
},
},
})
}
+11 -1
View File
@@ -59,7 +59,17 @@ func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
return
}
resp.Schema = *ds
// resp.Schema.Description = "TODO"
// Overrides
defaultDescription := "If `true`, this SSH key will be included on all new server installations (if the operating " +
"system supports SSH key injection)."
resp.Schema.Attributes["default"] = schema.BoolAttribute{
Description: defaultDescription,
MarkdownDescription: defaultDescription,
Optional: false,
Required: false,
Computed: true,
}
// Additional attributes
fingerprintDescription := "The fingerprint of the SSH key."
+14 -5
View File
@@ -61,15 +61,24 @@ func (r *sshKeyResource) Metadata(ctx context.Context, req resource.MetadataRequ
func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.SshKeyResourceSchema(ctx)
// resp.Schema.Description = "TODO"
// Overrides
default_ := resp.Schema.Attributes["default"]
idDescription := "The numeric ID of the SSH key."
resp.Schema.Attributes["id"] = schema.Int64Attribute{
Required: false,
Optional: false,
Computed: true,
Description: idDescription,
MarkdownDescription: idDescription,
}
defaultDescription := "If `true`, this SSH key will be included on all new server installations (if the operating " +
"system supports SSH key injection). Default is `false`."
resp.Schema.Attributes["default"] = schema.BoolAttribute{
Optional: true,
Computed: true,
Description: default_.GetDescription(),
MarkdownDescription: default_.GetMarkdownDescription(),
Description: defaultDescription,
MarkdownDescription: defaultDescription,
Default: booldefault.StaticBool(false), // Add default to backups
}
@@ -265,7 +274,7 @@ func (r *sshKeyResource) ImportState(
sshResp, err := r.bc.client.GetAccountKeysWithResponse(ctx, &params)
if err != nil {
resp.Diagnostics.AddError(fmt.Sprintf("Error getting SSH key for import: fingerprint=%s", req.ID), err.Error())
resp.Diagnostics.AddError(fmt.Sprintf("Error getting SSH keys for import: fingerprint=%s", req.ID), err.Error())
return
}
+149 -116
View File
@@ -32,129 +32,162 @@ func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema, cfg AttributeCo
required := cfg.RequiredAttributes != nil && slices.Contains(*cfg.RequiredAttributes, name)
optional := cfg.OptionalAttributes != nil && slices.Contains(*cfg.OptionalAttributes, name)
switch attribute.GetType() {
case types.BoolType:
ds.Attributes[name] = d_schema.BoolAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
case types.DynamicType:
ds.Attributes[name] = d_schema.DynamicAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
case types.Float32Type:
ds.Attributes[name] = d_schema.Float32Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
case types.Float64Type:
ds.Attributes[name] = d_schema.Float64Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
case types.Int32Type:
ds.Attributes[name] = d_schema.Int32Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
case types.Int64Type:
ds.Attributes[name] = d_schema.Int64Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
case types.NumberType:
ds.Attributes[name] = d_schema.NumberAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
case types.StringType:
ds.Attributes[name] = d_schema.StringAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
default:
if listType, isList := attribute.GetType().(types.ListType); isList {
ds.Attributes[name] = d_schema.ListAttribute{
ElementType: listType.ElemType,
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
continue
}
if objType, isObject := attribute.(r_schema.SingleNestedAttribute); isObject {
attributeTypes := make(map[string]attr.Type, len(objType.GetAttributes()))
for name, attribute := range objType.GetAttributes() {
attributeTypes[name] = attribute.GetType()
}
ds.Attributes[name] = d_schema.ObjectAttribute{
CustomType: objType.CustomType,
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
AttributeTypes: attributeTypes,
}
continue
}
return nil, fmt.Errorf("failed to convert resource schema attribute to data source schema attribute: name=%s, type=%s", name, attribute.GetType())
attr, err := convertResourceAttrToDataSourceAttr(name, attribute, required, optional)
if err != nil {
return nil, fmt.Errorf("failed to convert resource schema attribute to data source schema attribute: %w", err)
}
ds.Attributes[name] = attr
}
return &ds, nil
}
func convertResourceAttrToDataSourceAttr(name string, attribute r_schema.Attribute, required, optional bool) (d_schema.Attribute, error) {
switch attribute.GetType() {
case types.BoolType:
return d_schema.BoolAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
case types.DynamicType:
return d_schema.DynamicAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
case types.Float32Type:
return d_schema.Float32Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
case types.Float64Type:
return d_schema.Float64Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
case types.Int32Type:
return d_schema.Int32Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
case types.Int64Type:
return d_schema.Int64Attribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
case types.NumberType:
return d_schema.NumberAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
case types.StringType:
return d_schema.StringAttribute{
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
default:
if t, isList := attribute.(r_schema.ListNestedAttribute); isList {
nestedObjectAttrs := make(map[string]d_schema.Attribute)
for name, attribute := range t.NestedObject.Attributes {
nestedAttribute, err := convertResourceAttrToDataSourceAttr(name, attribute, required, optional)
if err != nil {
return nil, err
}
nestedObjectAttrs[name] = nestedAttribute
}
return d_schema.ListNestedAttribute{
NestedObject: d_schema.NestedAttributeObject{
Attributes: nestedObjectAttrs,
CustomType: t.NestedObject.CustomType,
Validators: t.NestedObject.Validators,
},
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
}
if t, isList := attribute.GetType().(types.ListType); isList {
return d_schema.ListAttribute{
ElementType: t.ElemType,
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}, nil
}
if t, isObject := attribute.(r_schema.SingleNestedAttribute); isObject {
attributeTypes := make(map[string]attr.Type, len(t.GetAttributes()))
for name, attribute := range t.GetAttributes() {
attributeTypes[name] = attribute.GetType()
}
return d_schema.ObjectAttribute{
CustomType: t.CustomType,
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
AttributeTypes: attributeTypes,
}, nil
}
return nil, fmt.Errorf("conversion of attribute type is not implemented: name=%s, type=%s", name, attribute.GetType())
}
}
func listContainsUnknown(ctx context.Context, list types.List) bool {
// If the whole list is unknown, return true
if list.IsUnknown() {
+55
View File
@@ -0,0 +1,55 @@
package provider
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
)
var (
_ validator.Int32 = &MultipleOfValidator{}
)
type MultipleOfValidator struct {
Multiple, RangeFrom, RangeTo int32
}
func (v MultipleOfValidator) Description(ctx context.Context) string {
if v.RangeFrom == 0 {
return fmt.Sprintf(`must be a multiple of %d`, v.Multiple)
}
return fmt.Sprintf("when greater than %d, must be a multiple of %d", v.RangeFrom, v.Multiple)
}
func (v MultipleOfValidator) MarkdownDescription(ctx context.Context) string {
if v.RangeFrom == 0 {
return fmt.Sprintf(`must be a multiple of %d`, v.Multiple)
}
return fmt.Sprintf("when greater than %d, must be a multiple of %d", v.RangeFrom, v.Multiple)
}
func (v MultipleOfValidator) ValidateInt32(ctx context.Context, req validator.Int32Request, resp *validator.Int32Response) {
if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() {
return
}
memory := req.ConfigValue.ValueInt32()
if memory%v.Multiple == 0 || v.RangeFrom != 0 && memory < v.RangeFrom || v.RangeTo != 0 && memory >= v.RangeTo {
return
}
if v.RangeFrom != 0 {
resp.Diagnostics.AddAttributeError(
req.Path,
"Not a Multiple",
fmt.Sprintf("if greater than %d, value must be a multiple of %d", v.RangeFrom, v.Multiple),
)
} else {
resp.Diagnostics.AddAttributeError(
req.Path,
"Not a Multiple",
fmt.Sprintf("value must be a multiple of %d", v.Multiple),
)
}
}
@@ -7,7 +7,7 @@ import (
"terraform-provider-binarylane/internal/resources"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
var (
@@ -50,7 +50,20 @@ func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.S
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
Description: vpcId.GetDescription(),
MarkdownDescription: vpcId.GetMarkdownDescription(),
Required: true, // VPC ID is required to define the route entries
Required: true, // vpc_id is required to define the route entries
}
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{
Description: vpcRouteEntriesDescription,
NestedObject: schema.NestedAttributeObject{
CustomType: vpcRouteEntries.NestedObject.CustomType,
Attributes: vpcRouteEntries.NestedObject.Attributes,
},
CustomType: vpcRouteEntries.CustomType,
Required: false,
Computed: true,
}
}
@@ -50,7 +50,7 @@ func (r *vpcRouteEntriesResource) Schema(ctx context.Context, req resource.Schem
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
Description: vpcId.GetDescription(),
MarkdownDescription: vpcId.GetMarkdownDescription(),
Required: true, // VPC ID is required to define the route entries
Required: true, // vpc_id is required to define the route entries
Validators: []validator.Int64{
int64validator.AtLeast(1),
},
@@ -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 this load balancer to forward traffic according to this rule.",
MarkdownDescription: "The protocol that traffic must match for this load balancer to forward traffic according to this rule.",
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\".",
Validators: []validator.String{
stringvalidator.OneOf(
"http",
+1 -1
View File
@@ -17,7 +17,7 @@
"name": "entry_protocol",
"string": {
"computed_optional_required": "required",
"description": "The protocol that traffic must match for this load balancer to forward traffic according to this rule.",
"description": "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\".",
"validators": [
{
"custom": {