Compare commits

..

6 Commits

Author SHA1 Message Date
Oscar Hermoso 44772aa55d 🚛 load balancer resource (#17)
* 🚛 load balancer resource

* Fix whitespace in server resource
2024-09-16 15:55:01 +08:00
Oscar Hermoso 600e3f56e7 👆 update docs & data sources (#16) 2024-09-14 14:15:26 +08:00
Oscar Hermoso 7a978731bb 🥉 k3s example working (#14)
* WIP commit - helm example

* 🥉 k3s working!
2024-09-07 17:00:24 +08:00
Oscar Hermoso 54dd00fa72 📰 Create LICENSE 2024-09-01 08:17:56 +08:00
Anurag e93659373d 🕸 Add server import by hostname & fix missing port_blocking (#12)
* Add server import by hostname

* Fix missing port blocking in server create

* hostname -> name
2024-08-31 15:27:03 +08:00
Oscar Hermoso dd3aaa2d75 ⚡ allow importing of binarylane_server resources (#10) 2024-08-31 14:39:31 +08:00
52 changed files with 2610 additions and 369 deletions
+39
View File
@@ -0,0 +1,39 @@
name: Terraform Provider Tests
on:
pull_request:
paths:
- '.github/workflows/test.yaml'
- '**.go'
permissions:
# Permission for checking out code
contents: read
jobs:
acceptance:
name: Acceptance Tests
runs-on: ubuntu-latest
environment: test
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: 'go.mod'
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: '1.5.*'
terraform_wrapper: false
- run: go test -v -cover ./...
env:
TF_ACC: '1'
BINARYLANE_API_TOKEN: ${{ secrets.BINARYLANE_API_TOKEN }}
# unit:
# name: Unit Tests
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: actions/setup-go@v4
# with:
# go-version-file: 'go.mod'
# - run: go test -v -cover ./...
+23 -4
View File
@@ -35,7 +35,13 @@ terraform apply
```sh
TF_ACC=1 go test -v ./...
TF_DEBUG=1 BINARYLANE_API_TOKEN=********* TF_ACC=1 go test -v ./...
```
### Update modules
```sh
go get -u && go mod tidy && go generate
```
### Adding resources/data sources to the provider
@@ -125,7 +131,7 @@ Add a `Configure` method to the resource:
+ }
```
Add the new data source/resource to `provider.go`:
Add the new resource to `provider.go`:
```diff
return []func() resource.Resource{
@@ -198,12 +204,25 @@ Use the generated schema to define the data source schema.
- },
- },
- }
+ ds, err := convertResourceSchemaToDataSourceSchema(resources.ExampleResourceSchema(ctx))
+ ds, err := convertResourceSchemaToDataSourceSchema(
+ resources.ExampleResourceSchema(ctx)
+ AttributeConfig{
+ RequiredAttributes: &[]string{"id"},
+ },
+ )
+ if err != nil {
+ resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
+ return
+ }
+ resp.Schema = *ds
+ resp.Schema.Description = "TODO"
+ // resp.Schema.Description = "TODO"
}
```
Add the data source to `provider.go`:
```diff
return []func() datasource.DataSource{
# ...
+ NewExampleDataSource,
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Oscar Hermoso
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+31 -9
View File
@@ -1,6 +1,6 @@
# terraform-provider-binarylane
See the examples in the [examples directory](./examples/basic/main.tf).
See the examples in the [examples directory](./examples/basic/README.md).
```terraform
resource "binarylane_server" "example" {
@@ -10,15 +10,33 @@ resource "binarylane_server" "example" {
}
```
## WIP
## Progress
If somehow you use this in production I would be pretty impressed.
Planned features:
- [x] Create/delete a server when runing locally
- [x] Publish
- [x] [Terraform Registry](https://registry.terraform.io/providers/oscarhermoso/binarylane/latest/docs/resources/server)
- [x] OpenTofu
- [ ] Deploy the rest of the owl
- [x] Servers
- [x] Firewall Rules
- [x] Cloud Init
- [ ] Updating server properties
- [ ] Backups
- [ ] Software
- [ ] IPV6
- [ ] Alerts
- [x] SSH Keys
- [x] Virtual Private Cloud
- [x] Load Balancers
- [ ] Images
- [ ] DNS
- [x] Docs
- [x] Generated docs
- [ ] Review & fill out remaining descriptions
- [ ] Include examples in docs
- [x] Examples
- [x] Basic
- [x] Cloud Init
- [ ] NixOS
- [ ] Virtual Private Cloud
- [ ] Kubernetes
## Parameters
@@ -78,7 +96,10 @@ jq '[ .regions[] | .slug ] | sort' tmp/regions.json
]
```
Sizes (not all sizes are available in all regions):
### Sizes
<details>
<summary>VM size</summary>
```sh
curl -X GET "https://api.binarylane.com.au/v2/sizes" \
@@ -86,6 +107,7 @@ curl -X GET "https://api.binarylane.com.au/v2/sizes" \
jq '[ .sizes[] | .slug ] | sort' tmp/sizes.json
```
</details>
```json
[
+44
View File
@@ -0,0 +1,44 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "binarylane_load_balancer Data Source - terraform-provider-binarylane"
subcategory: ""
description: |-
---
# binarylane_load_balancer (Data Source)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `id` (Number) The ID of the load balancer to fetch.
### 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))
- `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.
- `server_ids` (List of Number) A list of server IDs to assign to this load balancer.
<a id="nestedatt--forwarding_rules"></a>
### Nested Schema for `forwarding_rules`
Read-Only:
- `entry_protocol` (String)
<a id="nestedatt--health_check"></a>
### Nested Schema for `health_check`
Read-Only:
- `path` (String)
- `protocol` (String)
+9 -10
View File
@@ -3,12 +3,12 @@
page_title: "binarylane_server Data Source - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
Provides a Binary Lane Server data source. This can be used to read existing server details.
---
# binarylane_server (Data Source)
TODO
Provides a Binary Lane Server data source. This can be used to read existing server details.
@@ -19,18 +19,17 @@ TODO
- `id` (Number) The ID of the server to fetch.
### Optional
### 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.
- `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.
- `password` (String) 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 (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
- `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.
### Read-Only
- `image` (String) The slug of the selected operating system.
- `region` (String) The slug of the selected region.
- `size` (String) The slug of the selected size.
+1 -1
View File
@@ -15,7 +15,7 @@ TODO
<!-- schema generated by tfplugindocs -->
## Schema
### Optional
### Required
- `server_id` (Number) The ID of the server for which firewall rules should be listed.
+4 -4
View File
@@ -3,24 +3,24 @@
page_title: "binarylane_ssh_key Data Source - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_ssh_key (Data Source)
TODO
<!-- schema generated by tfplugindocs -->
## Schema
### Optional
### Required
- `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.
### 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).
- `name` (String) A name to help you identify the key.
- `public_key` (String) The public key in OpenSSH "authorized_keys" format.
+4 -4
View File
@@ -3,23 +3,23 @@
page_title: "binarylane_vpc Data Source - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_vpc (Data Source)
TODO
<!-- schema generated by tfplugindocs -->
## Schema
### Optional
### Required
- `id` (Number) The target vpc id.
- `ip_range` (String) A private address range that you select during creation, such as the default value of 10.240.0.0/16. Because the virtual network is dedicated to your use, you may use whatever IP address range you like.
### Read-Only
- `ip_range` (String) A private address range that you select during creation, such as the default value of 10.240.0.0/16. Because the virtual network is dedicated to your use, you may use whatever IP address range you like.
- `name` (String) A name to help identify this VPC.
+4 -4
View File
@@ -3,12 +3,12 @@
page_title: "binarylane_vpc_route_entries Data Source - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_vpc_route_entries (Data Source)
TODO
@@ -19,14 +19,14 @@ TODO
- `vpc_id` (Number) The target vpc id.
### Optional
### 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))
<a id="nestedatt--route_entries"></a>
### Nested Schema for `route_entries`
Optional:
Read-Only:
- `description` (String)
- `destination` (String)
+47
View File
@@ -0,0 +1,47 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "binarylane_load_balancer Resource - terraform-provider-binarylane"
subcategory: ""
description: |-
---
# binarylane_load_balancer (Resource)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `name` (String) The hostname of the load balancer.
### Optional
- `forwarding_rules` (Attributes List) The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule. (see [below for nested schema](#nestedatt--forwarding_rules))
- `health_check` (Attributes) The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules. (see [below for nested schema](#nestedatt--health_check))
- `region` (String) Leave null to create an anycast load balancer.
- `server_ids` (List of Number) A list of server IDs to assign to this load balancer.
### Read-Only
- `id` (Number) The ID of the load balancer to fetch.
<a id="nestedatt--forwarding_rules"></a>
### Nested Schema for `forwarding_rules`
Required:
- `entry_protocol` (String) The protocol that traffic must match for this load balancer to forward traffic according to this rule.
<a id="nestedatt--health_check"></a>
### Nested Schema for `health_check`
Optional:
- `path` (String) Leave null to accept the default '/' path.
- `protocol` (String) Leave null to accept the default HTTP protocol.
+2 -1
View File
@@ -25,7 +25,7 @@ Provides a Binary Lane Server resource. This can be used to create and delete se
- `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.
- `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 (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
- `password` (String, Sensitive) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions. If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
- `public_ipv4_count` (Number) The number of public IPv4 addresses to assign to the server. If this is not provided, the server will be created with the default number of public IPv4 addresses.
- `ssh_keys` (List of Number) This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.
@@ -36,5 +36,6 @@ Provides a Binary Lane Server resource. This can be used to create and delete se
### Read-Only
- `id` (Number) The ID of the server to fetch.
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
+2 -2
View File
@@ -3,12 +3,12 @@
page_title: "binarylane_server_firewall_rules Resource - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_server_firewall_rules (Resource)
TODO
+2 -2
View File
@@ -3,12 +3,12 @@
page_title: "binarylane_ssh_key Resource - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_ssh_key (Resource)
TODO
+2 -2
View File
@@ -3,12 +3,12 @@
page_title: "binarylane_vpc Resource - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_vpc (Resource)
TODO
+2 -2
View File
@@ -3,12 +3,12 @@
page_title: "binarylane_vpc_route_entries Resource - terraform-provider-binarylane"
subcategory: ""
description: |-
TODO
---
# binarylane_vpc_route_entries (Resource)
TODO
+1 -1
View File
@@ -11,7 +11,7 @@ provider "binarylane" {
}
resource "binarylane_server" "example" {
name = "tf-example"
name = "tf-example-basic"
region = "per" # or "syd", "mel", "bne", "sin"
image = "ubuntu-24.04"
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
+3
View File
@@ -35,3 +35,6 @@ override.tf.json
# Ignore CLI configuration files
.terraformrc
terraform.rc
# .kube/config contains credentials
.kube
+9 -1
View File
@@ -2,6 +2,14 @@
This WIP example shows how to create a Kubernetes cluster on Binary Lane.
Prior art:
## Prior art
- [MartinHodges/create_k8s](https://github.com/MartinHodges/create_k8s) repo and the [medium article](https://medium.com/@martin.hodges/creating-a-kubernetes-cluster-from-scratch-in-1-hour-using-automation-a25e387be547) that goes with it.
- https://github.com/inscapist/terraform-k3s-private-cloud
- https://github.com/schnerring/schnerring.github.io/blob/07d06fb40e3d01f2483a739f516ac4d711a5742c/content/blog/use-terraform-to-deploy-an-azure-kubernetes-service-aks-cluster-traefik-2-cert-manager-and-lets-encrypt-certificates/index.md
## Remaining TODOs
- [ ] Consider rewriting module to use `microk8s` instead of `k3s`
- [ ] Use `kustomize` instead of `helm`
- [ ] Use the `binarylane_load_balancer` resource instead of `kubectl port-forward` (or use the load balancer in another example)
+3
View File
@@ -10,4 +10,7 @@ curl -sfL https://get.k3s.io | sh -s - agent \
unset K3S_TOKEN
unset K3S_URL
# Disable firewall recommended
ufw disable
echo "K3s Node Join Completed"
+4 -1
View File
@@ -10,7 +10,7 @@ curl -sfL https://get.k3s.io | sh -s - server \
# --disable-cloud-controller \
# --disable servicelb \
# --disable local-storage \
# --disable traefik \
# --disable traefik
unset INSTALL_K3S_NAME
unset K3S_TOKEN
@@ -19,4 +19,7 @@ unset K3S_KUBECONFIG_MODE
echo "Installing Helm"
curl -sfL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
# Disable firewall recommended
ufw disable
echo "K3s Setup Completed"
+66 -9
View File
@@ -12,6 +12,13 @@ resource "random_password" "cluster_token" {
special = false
}
# SSH Key
# -------
resource "binarylane_ssh_key" "example" {
name = "tf-example-k8s"
public_key = file("~/.ssh/id_ed25519.pub")
}
# Virtual Private Cloud
# ---------------------
resource "binarylane_vpc" "example" {
@@ -42,9 +49,35 @@ resource "binarylane_server" "server" {
image = "ubuntu-24.04"
size = "std-min"
password = random_password.binarylane.result
ssh_keys = [binarylane_ssh_key.example.id]
vpc_id = binarylane_vpc.example.id
public_ipv4_count = 1
user_data = sensitive(data.cloudinit_config.server.rendered)
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
}
data "external" "kubeconfig" {
program = [
"/usr/bin/ssh",
"-o UserKnownHostsFile=/dev/null",
"-o StrictHostKeyChecking=no",
"root@${binarylane_server.server.0.permalink}",
"echo '{\"kubeconfig\":\"'$(cat /etc/rancher/k3s/k3s.yaml | base64)'\"}'",
]
}
resource "local_sensitive_file" "kubeconfig" {
depends_on = [binarylane_server.server]
content = replace(
base64decode(
replace(data.external.kubeconfig.result.kubeconfig, " ", "")
),
"server: https://127.0.0.1:6443",
"server: https://${binarylane_server.server.0.permalink}:6443",
)
filename = "${path.module}/.kube/config"
file_permission = "0600"
}
# k3s Agents
@@ -70,9 +103,11 @@ resource "binarylane_server" "agent" {
image = "ubuntu-24.04"
size = "std-min"
password = random_password.binarylane.result
ssh_keys = [binarylane_ssh_key.example.id]
vpc_id = binarylane_vpc.example.id
public_ipv4_count = 1
user_data = sensitive(data.cloudinit_config.agent.rendered)
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
}
# Virtual Private Cloud Routing
@@ -88,20 +123,26 @@ resource "binarylane_vpc_route_entries" "example" {
]
}
locals {
agent_ips = flatten([for _, a in binarylane_server.agent : a.private_ipv4_addresses])
server_ips = flatten([for _, s in binarylane_server.server : s.private_ipv4_addresses])
}
resource "binarylane_server_firewall_rules" "example" {
for_each = { for server in flatten([binarylane_server.server, binarylane_server.agent]) : server.id => server }
for_each = { for server in concat(binarylane_server.server, binarylane_server.agent) : server.name => server }
server_id = each.value.id
firewall_rules = [
{
description = "K3s supervisor and Kubernetes API Server"
protocol = "tcp"
source_addresses = flatten([for _, a in binarylane_server.agent : a.private_ipv4_addresses])
destination_addresses = flatten([for _, s in binarylane_server.server : s.private_ipv4_addresses])
destination_ports = ["6443"]
action = "accept"
},
# TODO: Due to a bug, this only works if you uncomment AFTER creating servers
# {
# description = "K3s supervisor and Kubernetes API Server"
# protocol = "tcp"
# source_addresses = ["0.0.0.0/0"]
# destination_addresses = local.server_ips
# destination_ports = ["6443"]
# action = "accept"
# },
{
description = "Flannel VXLAN"
protocol = "udp"
@@ -118,5 +159,21 @@ resource "binarylane_server_firewall_rules" "example" {
destination_ports = ["10250"]
action = "accept"
},
{
description = "SSH"
protocol = "udp"
source_addresses = ["0.0.0.0/0"]
destination_addresses = [binarylane_vpc.example.ip_range]
destination_ports = ["22"]
action = "accept"
},
{
description = "HTTP"
protocol = "tcp"
source_addresses = ["0.0.0.0/0"]
destination_addresses = [binarylane_vpc.example.ip_range]
destination_ports = ["80"]
action = "accept"
},
]
}
+64
View File
@@ -0,0 +1,64 @@
resource "kubectl_manifest" "hello" {
yaml_body = <<YAML
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-hello
labels:
app: nginx-hello
spec:
selector:
matchLabels:
app: nginx-hello
template:
metadata:
labels:
app: nginx-hello
spec:
containers:
- image: nginxdemos/hello
name: nginx-hello
ports:
- name: http
containerPort: 8080
protocol: TCP
YAML
}
resource "kubectl_manifest" "hello_service" {
yaml_body = <<YAML
apiVersion: v1
kind: Service
metadata:
name: nginx-hello
labels:
app: nginx-hello
spec:
type: ClusterIP
selector:
app: nginx-hello
ports:
- port: 80
YAML
}
resource "kubectl_manifest" "hello_ingress" {
yaml_body = <<YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-hello
spec:
rules:
- host: "*.bnr.la"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-hello
port:
number: 80
YAML
}
+18
View File
@@ -3,7 +3,25 @@ terraform {
binarylane = {
source = "oscarhermoso/binarylane"
}
kubectl = {
source = "alekc/kubectl"
version = ">= 2.0.0"
}
}
}
provider "binarylane" {}
provider "helm" {
kubernetes {
host = "${binarylane_server.server.0.permalink}:6443"
config_path = local_sensitive_file.kubeconfig.filename
insecure = true # TODO
}
}
provider "kubectl" {
host = "${binarylane_server.server.0.permalink}:6443"
config_path = local_sensitive_file.kubeconfig.filename
insecure = true # TODO
}
+5 -7
View File
@@ -28,13 +28,11 @@ resource "binarylane_ssh_key" "example" {
}
resource "binarylane_server" "example" {
name = "tf-example-ssh"
region = "per"
image = "ubuntu-24.04"
size = "std-min"
ssh_keys = [
binarylane_ssh_key.example.id
]
name = "tf-example-ssh"
region = "per"
image = "ubuntu-24.04"
size = "std-min"
ssh_keys = [binarylane_ssh_key.example.id]
# If you are using a global default SSH key, you need to explicitly wait for
# it to be created before creating the server.
+4 -4
View File
@@ -9,13 +9,13 @@ terraform {
provider "binarylane" {}
resource "binarylane_vpc" "example" {
name = "tf-example"
name = "tf-example-vpc"
ip_range = "10.240.0.0/16"
}
# Web & NAT server
resource "binarylane_server" "web" {
name = "tf-vpc-example-web"
name = "tf-example-vpc-web"
region = "per"
image = "ubuntu-24.04"
size = "std-min"
@@ -25,7 +25,7 @@ resource "binarylane_server" "web" {
# Database server
resource "binarylane_server" "db" {
name = "tf-vpc-example-db"
name = "tf-example-vpc-db"
region = "per"
image = "ubuntu-24.04"
size = "std-min"
@@ -35,7 +35,7 @@ resource "binarylane_server" "db" {
# VPN server
resource "binarylane_server" "vpn" {
name = "tf-vpc-example-vpn"
name = "tf-example-vpc-vpn"
region = "per"
image = "ubuntu-24.04"
size = "std-min"
+8 -8
View File
@@ -49,7 +49,7 @@ require (
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.6.0 // indirect
github.com/hashicorp/go-plugin v1.6.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
@@ -77,7 +77,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/onsi/gomega v1.33.1 // indirect
github.com/pb33f/libopenapi v0.15.0 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
@@ -97,17 +97,17 @@ 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.26.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.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-20240730163845-b1a4ccb954bf // indirect
google.golang.org/grpc v1.64.1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.2 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+16 -16
View File
@@ -120,8 +120,8 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A=
github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI=
github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -225,8 +225,8 @@ github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 h1:rICjNsHbPP1LttefanBPnwsSwl09Sq
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0/go.mod h1:4k+cJeSq5ntkwlcpQSxLxICCxQzCL772o30PxdibRt4=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
@@ -315,8 +315,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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/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=
@@ -333,8 +333,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -366,8 +366,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.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -378,8 +378,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.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -393,10 +393,10 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+135 -45
View File
@@ -155,29 +155,47 @@ type ClientInterface interface {
PostDomainsRefreshNameserverCache(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
// DeleteDomainsDomainName request
DeleteDomainsDomainName(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*http.Response, error)
DeleteDomainsDomainName(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*http.Response, error)
// GetDomainsDomainName request
GetDomainsDomainName(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*http.Response, error)
GetDomainsDomainName(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*http.Response, error)
// GetDomainsDomainNameRecords request
GetDomainsDomainNameRecords(ctx context.Context, domainName string, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
GetDomainsDomainNameRecords(ctx context.Context, domainName struct {
union json.RawMessage
}, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
// PostDomainsDomainNameRecordsWithBody request with any body
PostDomainsDomainNameRecordsWithBody(ctx context.Context, domainName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
PostDomainsDomainNameRecordsWithBody(ctx context.Context, domainName struct {
union json.RawMessage
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
PostDomainsDomainNameRecords(ctx context.Context, domainName string, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
PostDomainsDomainNameRecords(ctx context.Context, domainName struct {
union json.RawMessage
}, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// DeleteDomainsDomainNameRecordsRecordId request
DeleteDomainsDomainNameRecordsRecordId(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error)
DeleteDomainsDomainNameRecordsRecordId(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error)
// GetDomainsDomainNameRecordsRecordId request
GetDomainsDomainNameRecordsRecordId(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error)
GetDomainsDomainNameRecordsRecordId(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error)
// PutDomainsDomainNameRecordsRecordIdWithBody request with any body
PutDomainsDomainNameRecordsRecordIdWithBody(ctx context.Context, domainName string, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
PutDomainsDomainNameRecordsRecordIdWithBody(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
PutDomainsDomainNameRecordsRecordId(ctx context.Context, domainName string, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
PutDomainsDomainNameRecordsRecordId(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
// GetFailoverIpsServerId request
GetFailoverIpsServerId(ctx context.Context, serverId int64, params *GetFailoverIpsServerIdParams, reqEditors ...RequestEditorFn) (*http.Response, error)
@@ -843,7 +861,9 @@ func (c *Client) PostDomainsRefreshNameserverCache(ctx context.Context, reqEdito
return c.Client.Do(req)
}
func (c *Client) DeleteDomainsDomainName(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) DeleteDomainsDomainName(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewDeleteDomainsDomainNameRequest(c.Server, domainName)
if err != nil {
return nil, err
@@ -855,7 +875,9 @@ func (c *Client) DeleteDomainsDomainName(ctx context.Context, domainName string,
return c.Client.Do(req)
}
func (c *Client) GetDomainsDomainName(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) GetDomainsDomainName(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewGetDomainsDomainNameRequest(c.Server, domainName)
if err != nil {
return nil, err
@@ -867,7 +889,9 @@ func (c *Client) GetDomainsDomainName(ctx context.Context, domainName string, re
return c.Client.Do(req)
}
func (c *Client) GetDomainsDomainNameRecords(ctx context.Context, domainName string, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) GetDomainsDomainNameRecords(ctx context.Context, domainName struct {
union json.RawMessage
}, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewGetDomainsDomainNameRecordsRequest(c.Server, domainName, params)
if err != nil {
return nil, err
@@ -879,7 +903,9 @@ func (c *Client) GetDomainsDomainNameRecords(ctx context.Context, domainName str
return c.Client.Do(req)
}
func (c *Client) PostDomainsDomainNameRecordsWithBody(ctx context.Context, domainName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) PostDomainsDomainNameRecordsWithBody(ctx context.Context, domainName struct {
union json.RawMessage
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewPostDomainsDomainNameRecordsRequestWithBody(c.Server, domainName, contentType, body)
if err != nil {
return nil, err
@@ -891,7 +917,9 @@ func (c *Client) PostDomainsDomainNameRecordsWithBody(ctx context.Context, domai
return c.Client.Do(req)
}
func (c *Client) PostDomainsDomainNameRecords(ctx context.Context, domainName string, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) PostDomainsDomainNameRecords(ctx context.Context, domainName struct {
union json.RawMessage
}, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewPostDomainsDomainNameRecordsRequest(c.Server, domainName, body)
if err != nil {
return nil, err
@@ -903,7 +931,9 @@ func (c *Client) PostDomainsDomainNameRecords(ctx context.Context, domainName st
return c.Client.Do(req)
}
func (c *Client) DeleteDomainsDomainNameRecordsRecordId(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) DeleteDomainsDomainNameRecordsRecordId(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewDeleteDomainsDomainNameRecordsRecordIdRequest(c.Server, domainName, recordId)
if err != nil {
return nil, err
@@ -915,7 +945,9 @@ func (c *Client) DeleteDomainsDomainNameRecordsRecordId(ctx context.Context, dom
return c.Client.Do(req)
}
func (c *Client) GetDomainsDomainNameRecordsRecordId(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) GetDomainsDomainNameRecordsRecordId(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewGetDomainsDomainNameRecordsRecordIdRequest(c.Server, domainName, recordId)
if err != nil {
return nil, err
@@ -927,7 +959,9 @@ func (c *Client) GetDomainsDomainNameRecordsRecordId(ctx context.Context, domain
return c.Client.Do(req)
}
func (c *Client) PutDomainsDomainNameRecordsRecordIdWithBody(ctx context.Context, domainName string, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) PutDomainsDomainNameRecordsRecordIdWithBody(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewPutDomainsDomainNameRecordsRecordIdRequestWithBody(c.Server, domainName, recordId, contentType, body)
if err != nil {
return nil, err
@@ -939,7 +973,9 @@ func (c *Client) PutDomainsDomainNameRecordsRecordIdWithBody(ctx context.Context
return c.Client.Do(req)
}
func (c *Client) PutDomainsDomainNameRecordsRecordId(ctx context.Context, domainName string, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
func (c *Client) PutDomainsDomainNameRecordsRecordId(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewPutDomainsDomainNameRecordsRecordIdRequest(c.Server, domainName, recordId, body)
if err != nil {
return nil, err
@@ -3512,7 +3548,9 @@ func NewPostDomainsRefreshNameserverCacheRequest(server string) (*http.Request,
}
// NewDeleteDomainsDomainNameRequest generates requests for DeleteDomainsDomainName
func NewDeleteDomainsDomainNameRequest(server string, domainName string) (*http.Request, error) {
func NewDeleteDomainsDomainNameRequest(server string, domainName struct {
union json.RawMessage
}) (*http.Request, error) {
var err error
var pathParam0 string
@@ -3546,7 +3584,9 @@ func NewDeleteDomainsDomainNameRequest(server string, domainName string) (*http.
}
// NewGetDomainsDomainNameRequest generates requests for GetDomainsDomainName
func NewGetDomainsDomainNameRequest(server string, domainName string) (*http.Request, error) {
func NewGetDomainsDomainNameRequest(server string, domainName struct {
union json.RawMessage
}) (*http.Request, error) {
var err error
var pathParam0 string
@@ -3580,7 +3620,9 @@ func NewGetDomainsDomainNameRequest(server string, domainName string) (*http.Req
}
// NewGetDomainsDomainNameRecordsRequest generates requests for GetDomainsDomainNameRecords
func NewGetDomainsDomainNameRecordsRequest(server string, domainName string, params *GetDomainsDomainNameRecordsParams) (*http.Request, error) {
func NewGetDomainsDomainNameRecordsRequest(server string, domainName struct {
union json.RawMessage
}, params *GetDomainsDomainNameRecordsParams) (*http.Request, error) {
var err error
var pathParam0 string
@@ -3684,7 +3726,9 @@ func NewGetDomainsDomainNameRecordsRequest(server string, domainName string, par
}
// NewPostDomainsDomainNameRecordsRequest calls the generic PostDomainsDomainNameRecords builder with application/json body
func NewPostDomainsDomainNameRecordsRequest(server string, domainName string, body PostDomainsDomainNameRecordsJSONRequestBody) (*http.Request, error) {
func NewPostDomainsDomainNameRecordsRequest(server string, domainName struct {
union json.RawMessage
}, body PostDomainsDomainNameRecordsJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
@@ -3695,7 +3739,9 @@ func NewPostDomainsDomainNameRecordsRequest(server string, domainName string, bo
}
// NewPostDomainsDomainNameRecordsRequestWithBody generates requests for PostDomainsDomainNameRecords with any type of body
func NewPostDomainsDomainNameRecordsRequestWithBody(server string, domainName string, contentType string, body io.Reader) (*http.Request, error) {
func NewPostDomainsDomainNameRecordsRequestWithBody(server string, domainName struct {
union json.RawMessage
}, contentType string, body io.Reader) (*http.Request, error) {
var err error
var pathParam0 string
@@ -3731,7 +3777,9 @@ func NewPostDomainsDomainNameRecordsRequestWithBody(server string, domainName st
}
// NewDeleteDomainsDomainNameRecordsRecordIdRequest generates requests for DeleteDomainsDomainNameRecordsRecordId
func NewDeleteDomainsDomainNameRecordsRecordIdRequest(server string, domainName string, recordId int64) (*http.Request, error) {
func NewDeleteDomainsDomainNameRecordsRecordIdRequest(server string, domainName struct {
union json.RawMessage
}, recordId int64) (*http.Request, error) {
var err error
var pathParam0 string
@@ -3772,7 +3820,9 @@ func NewDeleteDomainsDomainNameRecordsRecordIdRequest(server string, domainName
}
// NewGetDomainsDomainNameRecordsRecordIdRequest generates requests for GetDomainsDomainNameRecordsRecordId
func NewGetDomainsDomainNameRecordsRecordIdRequest(server string, domainName string, recordId int64) (*http.Request, error) {
func NewGetDomainsDomainNameRecordsRecordIdRequest(server string, domainName struct {
union json.RawMessage
}, recordId int64) (*http.Request, error) {
var err error
var pathParam0 string
@@ -3813,7 +3863,9 @@ func NewGetDomainsDomainNameRecordsRecordIdRequest(server string, domainName str
}
// NewPutDomainsDomainNameRecordsRecordIdRequest calls the generic PutDomainsDomainNameRecordsRecordId builder with application/json body
func NewPutDomainsDomainNameRecordsRecordIdRequest(server string, domainName string, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody) (*http.Request, error) {
func NewPutDomainsDomainNameRecordsRecordIdRequest(server string, domainName struct {
union json.RawMessage
}, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody) (*http.Request, error) {
var bodyReader io.Reader
buf, err := json.Marshal(body)
if err != nil {
@@ -3824,7 +3876,9 @@ func NewPutDomainsDomainNameRecordsRecordIdRequest(server string, domainName str
}
// NewPutDomainsDomainNameRecordsRecordIdRequestWithBody generates requests for PutDomainsDomainNameRecordsRecordId with any type of body
func NewPutDomainsDomainNameRecordsRecordIdRequestWithBody(server string, domainName string, recordId int64, contentType string, body io.Reader) (*http.Request, error) {
func NewPutDomainsDomainNameRecordsRecordIdRequestWithBody(server string, domainName struct {
union json.RawMessage
}, recordId int64, contentType string, body io.Reader) (*http.Request, error) {
var err error
var pathParam0 string
@@ -8569,29 +8623,47 @@ type ClientWithResponsesInterface interface {
PostDomainsRefreshNameserverCacheWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostDomainsRefreshNameserverCacheResponse, error)
// DeleteDomainsDomainNameWithResponse request
DeleteDomainsDomainNameWithResponse(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameResponse, error)
DeleteDomainsDomainNameWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameResponse, error)
// GetDomainsDomainNameWithResponse request
GetDomainsDomainNameWithResponse(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameResponse, error)
GetDomainsDomainNameWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameResponse, error)
// GetDomainsDomainNameRecordsWithResponse request
GetDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName string, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsResponse, error)
GetDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsResponse, error)
// PostDomainsDomainNameRecordsWithBodyWithResponse request with any body
PostDomainsDomainNameRecordsWithBodyWithResponse(ctx context.Context, domainName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error)
PostDomainsDomainNameRecordsWithBodyWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error)
PostDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName string, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error)
PostDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error)
// DeleteDomainsDomainNameRecordsRecordIdWithResponse request
DeleteDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameRecordsRecordIdResponse, error)
DeleteDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameRecordsRecordIdResponse, error)
// GetDomainsDomainNameRecordsRecordIdWithResponse request
GetDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsRecordIdResponse, error)
GetDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsRecordIdResponse, error)
// PutDomainsDomainNameRecordsRecordIdWithBodyWithResponse request with any body
PutDomainsDomainNameRecordsRecordIdWithBodyWithResponse(ctx context.Context, domainName string, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error)
PutDomainsDomainNameRecordsRecordIdWithBodyWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error)
PutDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName string, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error)
PutDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error)
// GetFailoverIpsServerIdWithResponse request
GetFailoverIpsServerIdWithResponse(ctx context.Context, serverId int64, params *GetFailoverIpsServerIdParams, reqEditors ...RequestEditorFn) (*GetFailoverIpsServerIdResponse, error)
@@ -11955,7 +12027,9 @@ func (c *ClientWithResponses) PostDomainsRefreshNameserverCacheWithResponse(ctx
}
// DeleteDomainsDomainNameWithResponse request returning *DeleteDomainsDomainNameResponse
func (c *ClientWithResponses) DeleteDomainsDomainNameWithResponse(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameResponse, error) {
func (c *ClientWithResponses) DeleteDomainsDomainNameWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameResponse, error) {
rsp, err := c.DeleteDomainsDomainName(ctx, domainName, reqEditors...)
if err != nil {
return nil, err
@@ -11964,7 +12038,9 @@ func (c *ClientWithResponses) DeleteDomainsDomainNameWithResponse(ctx context.Co
}
// GetDomainsDomainNameWithResponse request returning *GetDomainsDomainNameResponse
func (c *ClientWithResponses) GetDomainsDomainNameWithResponse(ctx context.Context, domainName string, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameResponse, error) {
func (c *ClientWithResponses) GetDomainsDomainNameWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameResponse, error) {
rsp, err := c.GetDomainsDomainName(ctx, domainName, reqEditors...)
if err != nil {
return nil, err
@@ -11973,7 +12049,9 @@ func (c *ClientWithResponses) GetDomainsDomainNameWithResponse(ctx context.Conte
}
// GetDomainsDomainNameRecordsWithResponse request returning *GetDomainsDomainNameRecordsResponse
func (c *ClientWithResponses) GetDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName string, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsResponse, error) {
func (c *ClientWithResponses) GetDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, params *GetDomainsDomainNameRecordsParams, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsResponse, error) {
rsp, err := c.GetDomainsDomainNameRecords(ctx, domainName, params, reqEditors...)
if err != nil {
return nil, err
@@ -11982,7 +12060,9 @@ func (c *ClientWithResponses) GetDomainsDomainNameRecordsWithResponse(ctx contex
}
// PostDomainsDomainNameRecordsWithBodyWithResponse request with arbitrary body returning *PostDomainsDomainNameRecordsResponse
func (c *ClientWithResponses) PostDomainsDomainNameRecordsWithBodyWithResponse(ctx context.Context, domainName string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error) {
func (c *ClientWithResponses) PostDomainsDomainNameRecordsWithBodyWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error) {
rsp, err := c.PostDomainsDomainNameRecordsWithBody(ctx, domainName, contentType, body, reqEditors...)
if err != nil {
return nil, err
@@ -11990,7 +12070,9 @@ func (c *ClientWithResponses) PostDomainsDomainNameRecordsWithBodyWithResponse(c
return ParsePostDomainsDomainNameRecordsResponse(rsp)
}
func (c *ClientWithResponses) PostDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName string, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error) {
func (c *ClientWithResponses) PostDomainsDomainNameRecordsWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, body PostDomainsDomainNameRecordsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostDomainsDomainNameRecordsResponse, error) {
rsp, err := c.PostDomainsDomainNameRecords(ctx, domainName, body, reqEditors...)
if err != nil {
return nil, err
@@ -11999,7 +12081,9 @@ func (c *ClientWithResponses) PostDomainsDomainNameRecordsWithResponse(ctx conte
}
// DeleteDomainsDomainNameRecordsRecordIdWithResponse request returning *DeleteDomainsDomainNameRecordsRecordIdResponse
func (c *ClientWithResponses) DeleteDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameRecordsRecordIdResponse, error) {
func (c *ClientWithResponses) DeleteDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*DeleteDomainsDomainNameRecordsRecordIdResponse, error) {
rsp, err := c.DeleteDomainsDomainNameRecordsRecordId(ctx, domainName, recordId, reqEditors...)
if err != nil {
return nil, err
@@ -12008,7 +12092,9 @@ func (c *ClientWithResponses) DeleteDomainsDomainNameRecordsRecordIdWithResponse
}
// GetDomainsDomainNameRecordsRecordIdWithResponse request returning *GetDomainsDomainNameRecordsRecordIdResponse
func (c *ClientWithResponses) GetDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName string, recordId int64, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsRecordIdResponse, error) {
func (c *ClientWithResponses) GetDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, reqEditors ...RequestEditorFn) (*GetDomainsDomainNameRecordsRecordIdResponse, error) {
rsp, err := c.GetDomainsDomainNameRecordsRecordId(ctx, domainName, recordId, reqEditors...)
if err != nil {
return nil, err
@@ -12017,7 +12103,9 @@ func (c *ClientWithResponses) GetDomainsDomainNameRecordsRecordIdWithResponse(ct
}
// PutDomainsDomainNameRecordsRecordIdWithBodyWithResponse request with arbitrary body returning *PutDomainsDomainNameRecordsRecordIdResponse
func (c *ClientWithResponses) PutDomainsDomainNameRecordsRecordIdWithBodyWithResponse(ctx context.Context, domainName string, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error) {
func (c *ClientWithResponses) PutDomainsDomainNameRecordsRecordIdWithBodyWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error) {
rsp, err := c.PutDomainsDomainNameRecordsRecordIdWithBody(ctx, domainName, recordId, contentType, body, reqEditors...)
if err != nil {
return nil, err
@@ -12025,7 +12113,9 @@ func (c *ClientWithResponses) PutDomainsDomainNameRecordsRecordIdWithBodyWithRes
return ParsePutDomainsDomainNameRecordsRecordIdResponse(rsp)
}
func (c *ClientWithResponses) PutDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName string, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error) {
func (c *ClientWithResponses) PutDomainsDomainNameRecordsRecordIdWithResponse(ctx context.Context, domainName struct {
union json.RawMessage
}, recordId int64, body PutDomainsDomainNameRecordsRecordIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutDomainsDomainNameRecordsRecordIdResponse, error) {
rsp, err := c.PutDomainsDomainNameRecordsRecordId(ctx, domainName, recordId, body, reqEditors...)
if err != nil {
return nil, err
+6 -4
View File
@@ -21,10 +21,7 @@ cat <<<$(jq '.paths["/account/keys/{key_id}"].delete.parameters[0].schema |= del
# Remove the "/paths/{image_id}" path because its duplicated by "/images/{image_id_or_slug}"
cat <<<$(jq 'del(.paths."/images/{image_id}")' $OPENAPI_FILE) >$OPENAPI_FILE
# Override descriptions
cat <<<$(jq '.components.schemas.CreateServerRequest.properties.ssh_keys.description |= "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."' $OPENAPI_FILE) >$OPENAPI_FILE
# Add x-oapi-codegen-extra-tags so Go structs can be reflected
# Add x-oapi-codegen-extra-tags so structs can be reflected
## RouteEntryRequest
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.destination += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination"}}' $OPENAPI_FILE) >$OPENAPI_FILE
@@ -38,3 +35,8 @@ cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.action += {"x-o
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.destination_addresses += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination_addresses"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.destination_ports += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination_ports"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.source_addresses += {"x-oapi-codegen-extra-tags": {"tfsdk": "source_addresses"}}' $OPENAPI_FILE) >$OPENAPI_FILE
## Load Balancer
cat <<<$(jq '.components.schemas.CreateLoadBalancerRequest.properties.forwarding_rules += {"x-oapi-codegen-extra-tags": {"tfsdk": "forwarding_rules"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "entry_protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
cat <<<$(jq '.components.schemas.HealthCheckProtocol |= del(.enum)' $OPENAPI_FILE) >$OPENAPI_FILE
+87 -23
View File
@@ -8,7 +8,7 @@
"url": "https://support.binarylane.com.au",
"email": "support@binarylane.com.au"
},
"version": "0.24.4",
"version": "0.25.0",
"x-logo": {
"url": "https://www.binarylane.com.au/res/images/binarylane/logo.png",
"backgroundColor": "#000000",
@@ -695,10 +695,18 @@
{
"name": "domain_name",
"in": "path",
"description": "The name of the domain to fetch.",
"description": "The name or domain ID of the domain to fetch.",
"required": true,
"schema": {
"type": "string"
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"example": 5
}
}
],
@@ -744,10 +752,18 @@
{
"name": "domain_name",
"in": "path",
"description": "The name of the domain to delete.",
"description": "The name or domain ID of the domain to delete.",
"required": true,
"schema": {
"type": "string"
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"example": 5
}
}
],
@@ -788,10 +804,18 @@
{
"name": "domain_name",
"in": "path",
"description": "The domain name for which records should be listed.",
"description": "The domain name or domain ID for for which records should be listed.",
"required": true,
"schema": {
"type": "string"
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"example": 5
}
},
{
@@ -883,10 +907,18 @@
{
"name": "domain_name",
"in": "path",
"description": "The domain name for which the record should be created.",
"description": "The domain name or domain ID for for which the record should be created.",
"required": true,
"schema": {
"type": "string"
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"example": 5
}
}
],
@@ -958,10 +990,18 @@
{
"name": "domain_name",
"in": "path",
"description": "The domain name for which the record should be fetched.",
"description": "The domain name or domain ID for for which the record should be fetched.",
"required": true,
"schema": {
"type": "string"
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"example": 5
}
},
{
@@ -1017,10 +1057,18 @@
{
"name": "domain_name",
"in": "path",
"description": "The domain name for which the record should be updated.",
"description": "The domain name or domain ID for for which the record should be updated.",
"required": true,
"schema": {
"type": "string"
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"example": 5
}
},
{
@@ -1100,10 +1148,18 @@
{
"name": "domain_name",
"in": "path",
"description": "The domain name for which the record should be deleted.",
"description": "The domain name or domain ID for which the record should be deleted.",
"required": true,
"schema": {
"type": "string"
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"example": 5
}
},
{
@@ -10001,7 +10057,10 @@
"$ref": "#/components/schemas/ForwardingRule"
},
"description": "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule.",
"nullable": true
"nullable": true,
"x-oapi-codegen-extra-tags": {
"tfsdk": "forwarding_rules"
}
},
"health_check": {
"allOf": [
@@ -10103,7 +10162,7 @@
"example": 5,
"type": "integer"
},
"description": "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.",
"description": "This may be either the SSH keys Ids or fingerprints. If this is null or not provided any SSH keys that have been marked as default will be deployed (if the operating system supports SSH keys). Submit an empty array to disable deployment of default keys.",
"nullable": true
},
"options": {
@@ -10517,11 +10576,18 @@
"Domain": {
"required": [
"current_nameservers",
"id",
"name",
"zone_file"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "The ID of this domain.",
"format": "int64",
"readOnly": true
},
"name": {
"minLength": 1,
"type": "string",
@@ -10935,7 +11001,10 @@
"$ref": "#/components/schemas/LoadBalancerRuleProtocol"
}
],
"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 this load balancer to forward traffic according to this rule.",
"x-oapi-codegen-extra-tags": {
"tfsdk": "entry_protocol"
}
}
}
},
@@ -10975,11 +11044,6 @@
}
},
"HealthCheckProtocol": {
"enum": [
"http",
"https",
"both"
],
"type": "string",
"description": "\n| Value | Description |\n| ----- | ----------- |\n| http | The health check will be performed via HTTP. |\n| https | The health check will be performed via HTTPS. |\n| both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |\n\n",
"x-enum-descriptions": [
+51 -13
View File
@@ -230,13 +230,6 @@ const (
EnableIpv6TypeEnableIpv6 EnableIpv6Type = "enable_ipv6"
)
// Defines values for HealthCheckProtocol.
const (
HealthCheckProtocolBoth HealthCheckProtocol = "both"
HealthCheckProtocolHttp HealthCheckProtocol = "http"
HealthCheckProtocolHttps HealthCheckProtocol = "https"
)
// Defines values for ImageQueryType.
const (
ImageQueryTypeBackup ImageQueryType = "backup"
@@ -265,8 +258,8 @@ const (
// Defines values for LoadBalancerRuleProtocol.
const (
LoadBalancerRuleProtocolHttp LoadBalancerRuleProtocol = "http"
LoadBalancerRuleProtocolHttps LoadBalancerRuleProtocol = "https"
Http LoadBalancerRuleProtocol = "http"
Https LoadBalancerRuleProtocol = "https"
)
// Defines values for LoadBalancerStatus.
@@ -1131,7 +1124,7 @@ type ConsoleResponse struct {
// CreateLoadBalancerRequest defines model for CreateLoadBalancerRequest.
type CreateLoadBalancerRequest struct {
// ForwardingRules The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default "HTTP" only forwarding rule.
ForwardingRules *[]ForwardingRule `json:"forwarding_rules"`
ForwardingRules *[]ForwardingRule `json:"forwarding_rules" tfsdk:"forwarding_rules"`
// HealthCheck The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules.
HealthCheck *HealthCheck `json:"health_check"`
@@ -1184,7 +1177,7 @@ type CreateServerRequest struct {
// Size The slug of the selected size.
Size string `json:"size"`
// SshKeys 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.
// SshKeys This may be either the SSH keys Ids or fingerprints. If this is null or not provided any SSH keys that have been marked as default will be deployed (if the operating system supports SSH keys). Submit an empty array to disable deployment of default keys.
SshKeys *[]int `json:"ssh_keys"`
// UserData 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.
@@ -1364,6 +1357,9 @@ type Domain struct {
// CurrentNameservers The current authoritative name servers for this domain.
CurrentNameservers *[]string `json:"current_nameservers,omitempty"`
// Id The ID of this domain.
Id *int64 `json:"id,omitempty"`
// Name The name of the domain.
Name *string `json:"name,omitempty"`
@@ -1535,7 +1531,7 @@ type FailoverIpsResponse struct {
// 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 LoadBalancerRuleProtocol `json:"entry_protocol"`
EntryProtocol LoadBalancerRuleProtocol `json:"entry_protocol" tfsdk:"entry_protocol"`
}
// ForwardingRulesRequest defines model for ForwardingRulesRequest.
@@ -1559,7 +1555,7 @@ type HealthCheck struct {
// | http | The health check will be performed via HTTP. |
// | https | The health check will be performed via HTTPS. |
// | both | The health check will be performed via both HTTP and HTTPS. Failing a health check on one protocol will remove the server from the pool of servers only for that protocol. |
type HealthCheckProtocol string
type HealthCheckProtocol = string
// Host defines model for Host.
type Host struct {
@@ -3176,6 +3172,18 @@ type GetDomainsParams struct {
// PostDomainsJSONBody defines parameters for PostDomains.
type PostDomainsJSONBody = DomainRequest
// DeleteDomainsDomainNameParamsDomainName0 defines parameters for DeleteDomainsDomainName.
type DeleteDomainsDomainNameParamsDomainName0 = int
// DeleteDomainsDomainNameParamsDomainName1 defines parameters for DeleteDomainsDomainName.
type DeleteDomainsDomainNameParamsDomainName1 = string
// GetDomainsDomainNameParamsDomainName0 defines parameters for GetDomainsDomainName.
type GetDomainsDomainNameParamsDomainName0 = int
// GetDomainsDomainNameParamsDomainName1 defines parameters for GetDomainsDomainName.
type GetDomainsDomainNameParamsDomainName1 = string
// GetDomainsDomainNameRecordsParams defines parameters for GetDomainsDomainNameRecords.
type GetDomainsDomainNameRecordsParams struct {
// Type
@@ -3203,12 +3211,42 @@ type GetDomainsDomainNameRecordsParams struct {
PerPage *int32 `form:"per_page,omitempty" json:"per_page,omitempty"`
}
// GetDomainsDomainNameRecordsParamsDomainName0 defines parameters for GetDomainsDomainNameRecords.
type GetDomainsDomainNameRecordsParamsDomainName0 = int
// GetDomainsDomainNameRecordsParamsDomainName1 defines parameters for GetDomainsDomainNameRecords.
type GetDomainsDomainNameRecordsParamsDomainName1 = string
// PostDomainsDomainNameRecordsJSONBody defines parameters for PostDomainsDomainNameRecords.
type PostDomainsDomainNameRecordsJSONBody = DomainRecordRequest
// PostDomainsDomainNameRecordsParamsDomainName0 defines parameters for PostDomainsDomainNameRecords.
type PostDomainsDomainNameRecordsParamsDomainName0 = int
// PostDomainsDomainNameRecordsParamsDomainName1 defines parameters for PostDomainsDomainNameRecords.
type PostDomainsDomainNameRecordsParamsDomainName1 = string
// DeleteDomainsDomainNameRecordsRecordIdParamsDomainName0 defines parameters for DeleteDomainsDomainNameRecordsRecordId.
type DeleteDomainsDomainNameRecordsRecordIdParamsDomainName0 = int
// DeleteDomainsDomainNameRecordsRecordIdParamsDomainName1 defines parameters for DeleteDomainsDomainNameRecordsRecordId.
type DeleteDomainsDomainNameRecordsRecordIdParamsDomainName1 = string
// GetDomainsDomainNameRecordsRecordIdParamsDomainName0 defines parameters for GetDomainsDomainNameRecordsRecordId.
type GetDomainsDomainNameRecordsRecordIdParamsDomainName0 = int
// GetDomainsDomainNameRecordsRecordIdParamsDomainName1 defines parameters for GetDomainsDomainNameRecordsRecordId.
type GetDomainsDomainNameRecordsRecordIdParamsDomainName1 = string
// PutDomainsDomainNameRecordsRecordIdJSONBody defines parameters for PutDomainsDomainNameRecordsRecordId.
type PutDomainsDomainNameRecordsRecordIdJSONBody = DomainRecordRequest
// PutDomainsDomainNameRecordsRecordIdParamsDomainName0 defines parameters for PutDomainsDomainNameRecordsRecordId.
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
@@ -0,0 +1,104 @@
package provider
import (
"context"
"fmt"
"net/http"
"terraform-provider-binarylane/internal/resources"
"github.com/hashicorp/terraform-plugin-framework/datasource"
)
var (
_ datasource.DataSource = &loadBalancerDataSource{}
_ datasource.DataSourceWithConfigure = &loadBalancerDataSource{}
)
func NewLoadBalancerDataSource() datasource.DataSource {
return &loadBalancerDataSource{}
}
type loadBalancerDataSource struct {
bc *BinarylaneClient
}
type loadBalancerDataSourceModel struct {
resources.LoadBalancerModel
}
func (d *loadBalancerDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_load_balancer"
}
func (d *loadBalancerDataSource) Configure(
_ context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
if req.ProviderData == nil {
return
}
bc, ok := req.ProviderData.(BinarylaneClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData))
return
}
d.bc = &bc
}
func (d *loadBalancerDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
ds, err := convertResourceSchemaToDataSourceSchema(
resources.LoadBalancerResourceSchema(ctx),
AttributeConfig{
RequiredAttributes: &[]string{"id"},
},
)
if err != nil {
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
return
}
resp.Schema = *ds
// resp.Schema.Description = "TODO"
}
func (d *loadBalancerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data loadBalancerDataSourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Read API call logic
lbResp, err := d.bc.client.GetLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64())
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error reading load balancer: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
if lbResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code reading load balancer",
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
)
return
}
diags := SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
+389
View File
@@ -0,0 +1,389 @@
package provider
import (
"context"
"fmt"
"net/http"
"strconv"
"terraform-provider-binarylane/internal/binarylane"
"terraform-provider-binarylane/internal/resources"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
var (
_ resource.Resource = &loadBalancerResource{}
_ resource.ResourceWithConfigure = &loadBalancerResource{}
_ resource.ResourceWithImportState = &loadBalancerResource{}
)
func NewLoadBalancerResource() resource.Resource {
return &loadBalancerResource{}
}
type loadBalancerResource struct {
bc *BinarylaneClient
}
type loadBalancerResourceModel struct {
resources.LoadBalancerModel
}
func (r *loadBalancerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_load_balancer"
}
func (d *loadBalancerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
bc, ok := req.ProviderData.(BinarylaneClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
)
return
}
d.bc = &bc
}
func (r *loadBalancerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.LoadBalancerResourceSchema(ctx)
// resp.Schema.Description = "TODO"
// Overrides
id := resp.Schema.Attributes["id"]
resp.Schema.Attributes["id"] = &schema.Int64Attribute{
Description: id.GetDescription(),
MarkdownDescription: id.GetMarkdownDescription(),
// read only
Optional: false,
Required: false,
Computed: true,
PlanModifiers: []planmodifier.Int64{
int64planmodifier.UseStateForUnknown(),
},
}
region := resp.Schema.Attributes["region"]
resp.Schema.Attributes["region"] = &schema.StringAttribute{
Description: region.GetDescription(),
MarkdownDescription: region.GetMarkdownDescription(),
Optional: true,
Computed: false, // region is not computed, defined at creation
PlanModifiers: []planmodifier.String{ // region is not allowed to be changed
stringplanmodifier.RequiresReplace(),
},
}
}
func (r *loadBalancerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data loadBalancerResourceModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Create API call logic
forwardingRules := []binarylane.ForwardingRule{}
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, &forwardingRules, false)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
serverIds := []int64{}
diags = data.ServerIds.ElementsAs(ctx, &serverIds, false)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
body := binarylane.CreateLoadBalancerRequest{
Name: data.Name.ValueString(),
ForwardingRules: &forwardingRules,
ServerIds: &serverIds,
}
if data.HealthCheck.Path.ValueStringPointer() != nil || data.HealthCheck.Protocol.ValueStringPointer() != nil {
body.HealthCheck = &binarylane.HealthCheck{
Path: data.HealthCheck.Path.ValueStringPointer(),
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
}
}
tflog.Info(ctx, fmt.Sprintf("Creating Load Balancer: name=%s", data.Name.ValueString()))
lbResp, err := r.bc.client.PostLoadBalancersWithResponse(ctx, body)
if err != nil {
tflog.Info(ctx, fmt.Sprintf("Attempted to create new load balancer: request=%+v", body))
resp.Diagnostics.AddError(
fmt.Sprintf("Error sending request to create load balancer: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
if lbResp.StatusCode() != http.StatusOK {
tflog.Info(ctx, fmt.Sprintf("Attempted to create new load balancer: request=%+v", body))
resp.Diagnostics.AddError(
"Unexpected HTTP status code creating load balancer",
fmt.Sprintf("Received %s creating new load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body))
return
}
diags = SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data loadBalancerResourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Read API call logic
lbResp, err := r.bc.client.GetLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64())
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error reading load balancer: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
if lbResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code reading load balancer",
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
)
return
}
diags := SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *loadBalancerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data loadBalancerResourceModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Update API call logic
forwardingRules := &[]binarylane.ForwardingRule{}
diags := data.LoadBalancerModel.ForwardingRules.ElementsAs(ctx, forwardingRules, true)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
serverIds := &[]int64{}
diags = data.ServerIds.ElementsAs(ctx, serverIds, true)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Debug(ctx, fmt.Sprintf("Updating Load Balancer: name=%s", data.Name.ValueString()))
body := binarylane.UpdateLoadBalancerRequest{
Name: data.Name.ValueString(),
ForwardingRules: forwardingRules,
HealthCheck: &binarylane.HealthCheck{
Path: data.HealthCheck.Path.ValueStringPointer(),
Protocol: data.HealthCheck.Protocol.ValueStringPointer(),
},
ServerIds: serverIds,
}
lbResp, err := r.bc.client.PutLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64(), body)
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error reading load balancer: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
if lbResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code reading load balancer",
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body),
)
return
}
diags = SetLoadBalancerModelState(ctx, &data.LoadBalancerModel, lbResp.JSON200.LoadBalancer)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *loadBalancerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data loadBalancerResourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Delete API call logic
tflog.Debug(ctx, fmt.Sprintf("Deleting Load Balancer: name=%s", data.Name.ValueString()))
lbResp, err := r.bc.client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, data.Id.ValueInt64())
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error deleting load balancer: name=%s", data.Name.ValueString()),
err.Error(),
)
return
}
if lbResp.StatusCode() != http.StatusNoContent {
resp.Diagnostics.AddError(
"Unexpected HTTP status code deleting load balancer",
fmt.Sprintf("Received %s deleting load balancer: name=%s. Details: %s", lbResp.Status(), data.Name.ValueString(), lbResp.Body))
return
}
}
func (r *loadBalancerResource) ImportState(
ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
) {
// Import by ID
id, err := strconv.ParseInt(req.ID, 10, 32)
if err == nil {
diags := resp.State.SetAttribute(ctx, path.Root("id"), int32(id))
resp.Diagnostics.Append(diags...)
return
}
// Import by name
var page int32 = 1
perPage := int32(200)
var loadBalancer binarylane.LoadBalancer
var nextPage bool = true
for nextPage { // Need to paginate because the API does not support filtering by name
params := binarylane.GetLoadBalancersParams{
Page: &page,
PerPage: &perPage,
}
lbResp, err := r.bc.client.GetLoadBalancersWithResponse(ctx, &params)
if err != nil {
resp.Diagnostics.AddError(fmt.Sprintf("Error getting load balancer for import: name=%s", req.ID), err.Error())
return
}
if lbResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code getting load balancer for import",
fmt.Sprintf("Received %s reading load balancer: name=%s. Details: %s", lbResp.Status(), req.ID,
lbResp.Body))
return
}
loadBalancers := *lbResp.JSON200.LoadBalancers
for _, lb := range loadBalancers {
if *lb.Name == req.ID {
loadBalancer = lb
nextPage = false
break
}
}
if lbResp.JSON200.Links == nil || lbResp.JSON200.Links.Pages == nil || lbResp.JSON200.Links.Pages.Next == nil {
nextPage = false
break
}
page++
}
if loadBalancer.Id == nil {
resp.Diagnostics.AddError(
"Could not find load balancer by name",
fmt.Sprintf("Error finding load balancer: name=%s", req.ID),
)
return
}
diags := resp.State.SetAttribute(ctx, path.Root("id"), *loadBalancer.Id)
resp.Diagnostics.Append(diags...)
}
func SetLoadBalancerModelState(ctx context.Context, data *resources.LoadBalancerModel, lb *binarylane.LoadBalancer) diag.Diagnostics {
var diags, diag diag.Diagnostics
data.Id = types.Int64Value(*lb.Id)
data.Name = types.StringValue(*lb.Name)
if lb.Region == nil {
data.Region = types.StringNull()
} else {
data.Region = types.StringValue(*lb.Region.Slug)
}
data.ServerIds, diags = types.ListValueFrom(ctx, types.Int64Type, lb.ServerIds)
if lb.HealthCheck == nil {
data.HealthCheck = resources.NewHealthCheckValueNull()
} else {
data.HealthCheck, diag = resources.NewHealthCheckValue(
resources.HealthCheckValue{}.AttributeTypes(ctx),
map[string]attr.Value{
"path": types.StringValue(*lb.HealthCheck.Path),
"protocol": types.StringValue(*lb.HealthCheck.Protocol),
},
)
diags.Append(diag...)
}
data.ForwardingRules, diag = types.ListValueFrom(ctx,
types.ObjectType{AttrTypes: resources.ForwardingRulesValue{}.AttributeTypes(ctx)},
lb.ForwardingRules,
)
diags.Append(diag...)
return diags
}
@@ -0,0 +1,86 @@
package provider
import (
"crypto/rand"
"encoding/base64"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestLoadBalancerResource(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)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: providerConfig + `
resource "binarylane_server" "test" {
count = 2
name = "tf-test-lb-server-${count.index}"
region = "per"
image = "debian-12"
size = "std-min"
password = "` + password + `"
wait_for_create = 60
}
resource "binarylane_load_balancer" "test" {
name = "tf-test-lb"
server_ids = [binarylane_server.test.0.id, binarylane_server.test.1.id]
forwarding_rules = [{ entry_protocol = "http" }]
}
data "binarylane_load_balancer" "test" {
depends_on = [binarylane_load_balancer.test]
id = binarylane_load_balancer.test.id
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
// Verify resource values
resource.TestCheckResourceAttrSet("binarylane_load_balancer.test", "id"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "name", "tf-test-lb"),
resource.TestCheckNoResourceAttr("binarylane_load_balancer.test", "region"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "server_ids.#", "2"),
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
resource.TestCheckResourceAttrPair("binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.#", "1"),
resource.TestCheckResourceAttr("binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
// Verify data source values
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "id", "binarylane_load_balancer.test", "id"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "name", "tf-test-lb"),
resource.TestCheckNoResourceAttr("data.binarylane_load_balancer.test", "region"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "server_ids.#", "2"),
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.0", "binarylane_server.test.0", "id"),
resource.TestCheckResourceAttrPair("data.binarylane_load_balancer.test", "server_ids.1", "binarylane_server.test.1", "id"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.#", "1"),
resource.TestCheckResourceAttr("data.binarylane_load_balancer.test", "forwarding_rules.0.entry_protocol", "http"),
),
},
// Test import by ID
{
ResourceName: "binarylane_load_balancer.test",
ImportState: true,
ImportStateVerify: true,
},
// Test import by name
{
ResourceName: "binarylane_load_balancer.test",
ImportState: true,
ImportStateId: "tf-test-lb",
ImportStateVerify: true,
},
},
})
}
+2
View File
@@ -147,6 +147,7 @@ func (p *binarylaneProvider) DataSources(_ context.Context) []func() datasource.
NewSshKeyDataSource,
NewVpcDataSource,
NewVpcRouteEntriesDataSource,
NewLoadBalancerDataSource,
}
}
@@ -157,5 +158,6 @@ func (p *binarylaneProvider) Resources(_ context.Context) []func() resource.Reso
NewSshKeyResource,
NewVpcResource,
NewVpcRouteEntriesResource,
NewLoadBalancerResource,
}
}
+64 -31
View File
@@ -7,7 +7,7 @@ import (
"terraform-provider-binarylane/internal/resources"
"github.com/hashicorp/terraform-plugin-framework/datasource"
d_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)
@@ -25,6 +25,13 @@ type serverDataSource struct {
bc *BinarylaneClient
}
type serverDataModel struct {
resources.ServerModel
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
Permalink types.String `tfsdk:"permalink"`
}
func (d *serverDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
@@ -48,78 +55,104 @@ func (d *serverDataSource) Metadata(ctx context.Context, req datasource.Metadata
}
func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
ds, err := convertResourceSchemaToDataSourceSchema(resources.ServerResourceSchema(ctx))
ds, err := convertResourceSchemaToDataSourceSchema(
resources.ServerResourceSchema(ctx),
AttributeConfig{
RequiredAttributes: &[]string{"id"},
ExcludedAttributes: &[]string{"password"},
})
if err != nil {
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
return
}
resp.Schema = *ds
resp.Schema.Description = "TODO"
resp.Schema.Description = "Provides a Binary Lane Server data source. This can be used to read existing server details."
// Overrides
id := resp.Schema.Attributes["id"]
resp.Schema.Attributes["id"] = d_schema.Int64Attribute{
resp.Schema.Attributes["id"] = schema.Int64Attribute{
Description: id.GetDescription(),
MarkdownDescription: id.GetMarkdownDescription(),
Required: true, // ID is required to find the server
}
// 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",
Computed: true,
}
publicIpv4AddressesDescription := "The public IPv4 addresses assigned to the server."
resp.Schema.Attributes["public_ipv4_addresses"] = &schema.ListAttribute{
Description: publicIpv4AddressesDescription,
MarkdownDescription: publicIpv4AddressesDescription,
ElementType: types.StringType,
Computed: true,
}
privateIpv4AddressesDescription := "The private IPv4 addresses assigned to the server."
resp.Schema.Attributes["private_ipv4_addresses"] = &schema.ListAttribute{
Description: privateIpv4AddressesDescription,
MarkdownDescription: privateIpv4AddressesDescription,
ElementType: types.StringType,
Computed: true,
}
}
func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data resources.ServerModel
var data serverDataModel
// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
diags := req.Config.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// TODO: Use go routines here to make requests concurrently
// Read server
serverResp, err := d.bc.client.GetServersServerIdWithResponse(ctx, data.Id.ValueInt64())
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error reading server: name=%s", data.Id.String()),
fmt.Sprintf("Error reading server: name=%s, id=%s", data.Name.ValueString(), data.Id.String()),
err.Error(),
)
return
}
if serverResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
fmt.Sprintf("Unexpected HTTP status code reading server: name=%s", data.Id.String()),
fmt.Sprintf("Unexpected HTTP status code reading server: %s", serverResp.Body),
fmt.Sprintf("Unexpected HTTP status code reading server: name=%s, id=%s", data.Name.ValueString(), data.Id.String()),
string(serverResp.Body),
)
return
}
// TODO: Fetch user data
// Read user data
// userDataResp, err := d.bc.client.GetServersServerIdUserDataWithResponse(ctx, data.Id.ValueInt64())
// if err != nil {
// resp.Diagnostics.AddError(
// fmt.Sprintf("Error reading server user data: name=%s", data.Id.String()),
// err.Error(),
// )
// return
// }
// if userDataResp.StatusCode() != http.StatusOK {
// resp.Diagnostics.AddError(
// fmt.Sprintf("Unexpected HTTP status code reading server user data: name=%s", data.Id.String()),
// fmt.Sprintf("Unexpected HTTP status code reading server user data: %s", serverResp.Body),
// )
// 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.UserData = types.StringValue(*userDataResp.JSON200.UserData)
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
var publicIpv4Addresses []string
var 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...)
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
@@ -32,7 +32,12 @@ func (d *serverFirewallRulesDataSource) Metadata(ctx context.Context, req dataso
}
func (d *serverFirewallRulesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
ds, err := convertResourceSchemaToDataSourceSchema(resources.ServerFirewallRulesResourceSchema(ctx))
ds, err := convertResourceSchemaToDataSourceSchema(
resources.ServerFirewallRulesResourceSchema(ctx),
AttributeConfig{
RequiredAttributes: &[]string{"server_id"},
},
)
if err != nil {
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
return
@@ -59,7 +59,7 @@ 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"
// resp.Schema.Description = "TODO"
// Overrides
serverId := resp.Schema.Attributes["server_id"]
@@ -78,7 +78,6 @@ func (r *serverFirewallRulesResource) Create(ctx context.Context, req resource.C
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
+122 -47
View File
@@ -4,6 +4,8 @@ import (
"context"
"fmt"
"net/http"
"slices"
"strconv"
"terraform-provider-binarylane/internal/binarylane"
"terraform-provider-binarylane/internal/resources"
"time"
@@ -11,10 +13,12 @@ import (
"github.com/hashicorp/terraform-plugin-framework-validators/int32validator"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int32default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
@@ -25,9 +29,9 @@ import (
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &serverResource{}
_ resource.ResourceWithConfigure = &serverResource{}
// _ resource.ResourceWithImportState = &serverResource{}
_ resource.Resource = &serverResource{}
_ resource.ResourceWithConfigure = &serverResource{}
_ resource.ResourceWithImportState = &serverResource{}
)
// Helper function to simplify the provider implementation.
@@ -41,10 +45,12 @@ type serverResource struct {
type serverModel struct {
resources.ServerModel
WaitForCreateSeconds int32 `tfsdk:"wait_for_create"`
PublicIpv4Count types.Int32 `tfsdk:"public_ipv4_count"`
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
WaitForCreateSeconds types.Int32 `tfsdk:"wait_for_create"`
PublicIpv4Count types.Int32 `tfsdk:"public_ipv4_count"`
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
Permalink types.String `tfsdk:"permalink"`
Password types.String `tfsdk:"password"`
}
func (d *serverResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
@@ -82,15 +88,9 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
Optional: false,
Required: false,
Computed: true,
}
pw := resp.Schema.Attributes["password"]
resp.Schema.Attributes["password"] = &schema.StringAttribute{
Description: pw.GetDescription(),
MarkdownDescription: pw.GetMarkdownDescription(),
Optional: pw.IsOptional(),
Computed: false, // Computed must be false to allow server to be created without password
Sensitive: true, // Mark password as sensitive
PlanModifiers: []planmodifier.Int64{
int64planmodifier.UseStateForUnknown(),
},
}
backups := resp.Schema.Attributes["backups"]
@@ -144,6 +144,18 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
}
// Additional attributes
pwDescription :=
"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."
resp.Schema.Attributes["password"] = &schema.StringAttribute{
Description: pwDescription,
MarkdownDescription: pwDescription,
Optional: true, // Password optional, if not set will be emailed to user
Computed: false, // Computed must be false to allow server to be created without password
Sensitive: true, // Mark password as sensitive
}
waitDescription := "The number of seconds to wait for the server to be created, after which, a timeout error will " +
"be reported. If `wait_seconds` is left empty or set to 0, Terraform will succeed without waiting for the " +
"server creation to complete."
@@ -151,8 +163,7 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
Description: waitDescription,
MarkdownDescription: waitDescription,
Optional: true,
Computed: true,
Default: int32default.StaticInt32(0),
Computed: false,
}
publicIpv4CountDescription := "The number of public IPv4 addresses to assign to the server. If this is not provided, the " +
@@ -162,6 +173,7 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
MarkdownDescription: publicIpv4CountDescription,
Optional: true,
Computed: true,
Default: int32default.StaticInt32(1),
Validators: []validator.Int32{
int32validator.AtLeast(0),
},
@@ -176,6 +188,9 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
Optional: false,
Required: false,
Computed: true,
PlanModifiers: []planmodifier.List{
listplanmodifier.UseStateForUnknown(),
},
}
privateIpv4AddressesDescription := "The private IPv4 addresses assigned to the server."
@@ -187,6 +202,18 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
Optional: false,
Required: false,
Computed: true,
PlanModifiers: []planmodifier.List{
listplanmodifier.UseStateForUnknown(),
},
}
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",
// read only
Optional: false,
Required: false,
Computed: true,
}
}
@@ -211,13 +238,14 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
}
body := binarylane.CreateServerRequest{
Name: data.Name.ValueStringPointer(),
Image: data.Image.ValueString(),
Region: data.Region.ValueString(),
Size: data.Size.ValueString(),
UserData: data.UserData.ValueStringPointer(),
VpcId: data.VpcId.ValueInt64Pointer(),
SshKeys: &sshKeys,
Name: data.Name.ValueStringPointer(),
Image: data.Image.ValueString(),
Region: data.Region.ValueString(),
Size: data.Size.ValueString(),
UserData: data.UserData.ValueStringPointer(),
VpcId: data.VpcId.ValueInt64Pointer(),
PortBlocking: data.PortBlocking.ValueBoolPointer(),
SshKeys: &sshKeys,
Options: &binarylane.SizeOptionsRequest{
Ipv4Addresses: data.PublicIpv4Count.ValueInt32Pointer(),
},
@@ -251,20 +279,20 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
publicV4AddressCount := 0
var publicIpv4Addresses []string
var privateIpv4Addresses []string
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
if v4address.Type == "public" {
publicV4AddressCount++
publicIpv4Addresses = append(publicIpv4Addresses, v4address.IpAddress)
} else {
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
}
}
data.PublicIpv4Count = types.Int32Value(int32(publicV4AddressCount))
data.PublicIpv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, publicIpv4Addresses)
resp.Diagnostics.Append(diags...)
data.PrivateIPv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
@@ -273,7 +301,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
if data.WaitForCreateSeconds <= 0 {
if data.WaitForCreateSeconds.ValueInt32() <= 0 {
return
}
@@ -292,7 +320,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
return
}
timeLimit := time.Now().Add(time.Duration(data.WaitForCreateSeconds) * time.Second)
timeLimit := time.Now().Add(time.Duration(data.WaitForCreateSeconds.ValueInt32()) * time.Second)
for {
tflog.Info(ctx, "Waiting for server to be ready...")
@@ -325,7 +353,8 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
var data serverModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
diags := req.State.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
@@ -344,8 +373,9 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
if serverResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code reading server",
fmt.Sprintf("Received %s reading server: id=%s, name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), data.Id.String(), serverResp.Body))
fmt.Sprintf("Unexpected HTTP status code %s reading server: name=%s, id=%s", serverResp.Status(), data.Name.ValueString(), data.Id.String()),
string(serverResp.Body),
)
return
}
@@ -354,6 +384,25 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
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
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...)
if resp.Diagnostics.HasError() {
return
@@ -415,19 +464,45 @@ func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest,
}
}
// func (r *serverResource) ImportState(
// ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
// ) {
// id, err := strconv.ParseInt(req.ID, 10, 32)
func (r *serverResource) ImportState(
ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
) {
id, err := strconv.ParseInt(req.ID, 10, 32)
if err == nil {
diags := resp.State.SetAttribute(ctx, path.Root("id"), int32(id))
resp.Diagnostics.Append(diags...)
} else {
name := req.ID
params := binarylane.GetServersParams{
Hostname: &name,
}
// if err != nil {
// resp.Diagnostics.AddError(
// "Error importing server",
// "Could not import server, unexpected error (ID should be an integer): "+err.Error(),
// )
// return
// }
serverResp, err := r.bc.client.GetServersWithResponse(ctx, &params)
if err != nil {
resp.Diagnostics.AddError(fmt.Sprintf("Error getting server: hostname=%s", name), err.Error())
return
}
// diags := resp.State.SetAttribute(ctx, path.Root("id"), int32(id))
// resp.Diagnostics.Append(diags...)
// }
if serverResp.StatusCode() != http.StatusOK {
resp.Diagnostics.AddError(
"Unexpected HTTP status code getting server",
fmt.Sprintf("Received %s reading server: hostname=%s. Details: %s", serverResp.Status(), name,
serverResp.Body))
return
}
servers := *serverResp.JSON200.Servers
idx := slices.IndexFunc(servers, func(s binarylane.Server) bool { return *s.Name == name })
if idx == -1 {
resp.Diagnostics.AddError(
"Could not find server by hostname",
fmt.Sprintf("Error finding server: hostname=%s", name),
)
return
}
server := servers[idx]
diags := resp.State.SetAttribute(ctx, path.Root("id"), int32(*server.Id))
resp.Diagnostics.Append(diags...)
}
}
+17
View File
@@ -80,6 +80,7 @@ echo "Hello World" > /var/tmp/output.txt
resource.TestCheckResourceAttr("binarylane_server.test", "port_blocking", "true"),
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"), // Only the test SSH key should be registered
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.test", "id"),
resource.TestCheckResourceAttrSet("binarylane_server.test", "permalink"),
// Verify data source values
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
@@ -87,8 +88,24 @@ echo "Hello World" > /var/tmp/output.txt
resource.TestCheckResourceAttr("data.binarylane_server.test", "region", "per"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "debian-12"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "size", "std-min"),
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "permalink", "binarylane_server.test", "permalink"),
),
},
// Test import by ID
{
ResourceName: "binarylane_server.test",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "user_data", "wait_for_create", "public_ipv4_count"},
},
// Test import by name
{
ResourceName: "binarylane_server.test",
ImportState: true,
ImportStateId: "tf-test-server-resource",
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "user_data", "wait_for_create", "public_ipv4_count"},
},
},
})
}
+7 -2
View File
@@ -47,13 +47,18 @@ func (d *sshKeyDataSource) Metadata(ctx context.Context, req datasource.Metadata
}
func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
ds, err := convertResourceSchemaToDataSourceSchema(resources.SshKeyResourceSchema(ctx))
ds, err := convertResourceSchemaToDataSourceSchema(
resources.SshKeyResourceSchema(ctx),
AttributeConfig{
RequiredAttributes: &[]string{"id"},
},
)
if err != nil {
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
return
}
resp.Schema = *ds
resp.Schema.Description = "TODO"
// resp.Schema.Description = "TODO"
}
func (d *sshKeyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+9 -8
View File
@@ -7,6 +7,7 @@ import (
"terraform-provider-binarylane/internal/binarylane"
"terraform-provider-binarylane/internal/resources"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
@@ -17,9 +18,9 @@ import (
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &sshKeyResource{}
_ resource.ResourceWithConfigure = &sshKeyResource{}
// _ resource.ResourceWithImportState = &serverResource{}
_ resource.Resource = &sshKeyResource{}
_ resource.ResourceWithConfigure = &sshKeyResource{}
_ resource.ResourceWithImportState = &serverResource{}
)
func NewSshKeyResource() resource.Resource {
@@ -54,7 +55,7 @@ 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"
// resp.Schema.Description = "TODO"
// Overrides
default_ := resp.Schema.Attributes["default"]
@@ -217,7 +218,7 @@ func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest,
}
}
// func (r *sshKeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// // Retrieve import ID and save to id attribute
// resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
// }
func (r *sshKeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Retrieve import ID and save to id attribute
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
+105 -63
View File
@@ -2,14 +2,21 @@ package provider
import (
"fmt"
"strings"
"slices"
"github.com/hashicorp/terraform-plugin-framework/attr"
d_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
r_schema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)
func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema) (*d_schema.Schema, error) {
type AttributeConfig struct {
RequiredAttributes *[]string
OptionalAttributes *[]string
ExcludedAttributes *[]string
}
func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema, cfg AttributeConfig) (*d_schema.Schema, error) {
ds := d_schema.Schema{
Attributes: make(map[string]d_schema.Attribute, len(rs.Attributes)),
Blocks: make(map[string]d_schema.Block),
@@ -17,95 +24,130 @@ func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema) (*d_schema.Sche
MarkdownDescription: rs.GetMarkdownDescription(),
DeprecationMessage: rs.GetDeprecationMessage(),
}
for name, attr := range rs.Attributes {
switch attr.GetType() {
for name, attribute := range rs.Attributes {
if cfg.ExcludedAttributes != nil && slices.Contains(*cfg.ExcludedAttributes, name) {
continue
}
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
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: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
default:
// Feel free to to raise a PR and remove this hack
if strings.HasPrefix(attr.GetType().String(), "types.ListType") {
if listType, isList := attribute.GetType().(types.ListType); isList {
ds.Attributes[name] = d_schema.ListAttribute{
ElementType: attr.GetType().(types.ListType).ElemType,
Description: attr.GetDescription(),
Optional: attr.IsOptional(),
Computed: true,
Sensitive: attr.IsSensitive(),
MarkdownDescription: attr.GetMarkdownDescription(),
DeprecationMessage: attr.GetDeprecationMessage(),
ElementType: listType.ElemType,
Description: attribute.GetDescription(),
Required: required,
Optional: optional,
Computed: !required,
Sensitive: attribute.IsSensitive(),
MarkdownDescription: attribute.GetMarkdownDescription(),
DeprecationMessage: attribute.GetDeprecationMessage(),
}
} else {
return nil, fmt.Errorf("failed to convert resource schema attribute to data source schema attribute: name=%s, type=%s", name, attr.GetType())
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())
}
}
+7 -2
View File
@@ -50,14 +50,19 @@ func (d *vpcDataSource) Metadata(ctx context.Context, req datasource.MetadataReq
}
func (d *vpcDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
ds, err := convertResourceSchemaToDataSourceSchema(resources.VpcResourceSchema(ctx))
ds, err := convertResourceSchemaToDataSourceSchema(
resources.VpcResourceSchema(ctx),
AttributeConfig{
RequiredAttributes: &[]string{"id"},
},
)
if err != nil {
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
return
}
resp.Schema = *ds
resp.Schema.Description = "TODO"
// resp.Schema.Description = "TODO"
}
func (d *vpcDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+1 -1
View File
@@ -56,7 +56,7 @@ func (r *vpcResource) Metadata(ctx context.Context, req resource.MetadataRequest
func (r *vpcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.VpcResourceSchema(ctx)
resp.Schema.Description = "TODO"
// resp.Schema.Description = "TODO"
// Overrides
id := resp.Schema.Attributes["id"]
@@ -32,13 +32,18 @@ func (d *vpcRouteEntriesDataSource) Metadata(ctx context.Context, req datasource
}
func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
ds, err := convertResourceSchemaToDataSourceSchema(resources.VpcRouteEntriesResourceSchema(ctx))
ds, err := convertResourceSchemaToDataSourceSchema(
resources.VpcRouteEntriesResourceSchema(ctx),
AttributeConfig{
RequiredAttributes: &[]string{"vpc_id"},
},
)
if err != nil {
resp.Diagnostics.AddError("Failed to convert resource schema to data source schema", err.Error())
return
}
resp.Schema = *ds
resp.Schema.Description = "TODO"
// resp.Schema.Description = "TODO"
// Overrides
vpcId := resp.Schema.Attributes["vpc_id"]
@@ -43,7 +43,7 @@ func (r *vpcRouteEntriesResource) Metadata(ctx context.Context, req resource.Met
func (r *vpcRouteEntriesResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = resources.VpcRouteEntriesResourceSchema(ctx)
resp.Schema.Description = "TODO"
// resp.Schema.Description = "TODO"
// Overrides
vpcId := resp.Schema.Attributes["vpc_id"]
@@ -0,0 +1,819 @@
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package resources
import (
"context"
"fmt"
"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/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"regexp"
"strings"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
)
func LoadBalancerResourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"forwarding_rules": schema.ListNestedAttribute{
NestedObject: schema.NestedAttributeObject{
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.",
Validators: []validator.String{
stringvalidator.OneOf(
"http",
"https",
),
},
},
},
CustomType: ForwardingRulesType{
ObjectType: types.ObjectType{
AttrTypes: ForwardingRulesValue{}.AttributeTypes(ctx),
},
},
},
Optional: true,
Computed: true,
Description: "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule.",
MarkdownDescription: "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.",
},
"health_check": schema.SingleNestedAttribute{
Attributes: map[string]schema.Attribute{
"path": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Leave null to accept the default '/' path.",
MarkdownDescription: "Leave null to accept the default '/' path.",
Validators: []validator.String{
stringvalidator.RegexMatches(regexp.MustCompile("/[A-Za-z0-9/.?=&+%_-]*"), ""),
},
},
"protocol": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Leave null to accept the default HTTP protocol.",
MarkdownDescription: "Leave null to accept the default HTTP protocol.",
},
},
CustomType: HealthCheckType{
ObjectType: types.ObjectType{
AttrTypes: HealthCheckValue{}.AttributeTypes(ctx),
},
},
Optional: true,
Computed: true,
Description: "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules.",
MarkdownDescription: "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.",
},
"id": schema.Int64Attribute{
Optional: true,
Computed: true,
Description: "The ID of the load balancer to fetch.",
MarkdownDescription: "The ID of the load balancer to fetch.",
},
"name": schema.StringAttribute{
Required: true,
Description: "The hostname of the load balancer.",
MarkdownDescription: "The hostname of the load balancer.",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"region": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Leave null to create an anycast load balancer.",
MarkdownDescription: "Leave null to create an anycast load balancer.",
},
"server_ids": schema.ListAttribute{
ElementType: types.Int64Type,
Optional: true,
Computed: true,
Description: "A list of server IDs to assign to this load balancer.",
MarkdownDescription: "A list of server IDs to assign to this load balancer.",
},
},
}
}
type LoadBalancerModel struct {
ForwardingRules types.List `tfsdk:"forwarding_rules"`
HealthCheck HealthCheckValue `tfsdk:"health_check"`
Id types.Int64 `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Region types.String `tfsdk:"region"`
ServerIds types.List `tfsdk:"server_ids"`
}
var _ basetypes.ObjectTypable = ForwardingRulesType{}
type ForwardingRulesType struct {
basetypes.ObjectType
}
func (t ForwardingRulesType) Equal(o attr.Type) bool {
other, ok := o.(ForwardingRulesType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t ForwardingRulesType) String() string {
return "ForwardingRulesType"
}
func (t ForwardingRulesType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
attributes := in.Attributes()
entryProtocolAttribute, ok := attributes["entry_protocol"]
if !ok {
diags.AddError(
"Attribute Missing",
`entry_protocol is missing from object`)
return nil, diags
}
entryProtocolVal, ok := entryProtocolAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`entry_protocol expected to be basetypes.StringValue, was: %T`, entryProtocolAttribute))
}
if diags.HasError() {
return nil, diags
}
return ForwardingRulesValue{
EntryProtocol: entryProtocolVal,
state: attr.ValueStateKnown,
}, diags
}
func NewForwardingRulesValueNull() ForwardingRulesValue {
return ForwardingRulesValue{
state: attr.ValueStateNull,
}
}
func NewForwardingRulesValueUnknown() ForwardingRulesValue {
return ForwardingRulesValue{
state: attr.ValueStateUnknown,
}
}
func NewForwardingRulesValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (ForwardingRulesValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing ForwardingRulesValue Attribute Value",
"While creating a ForwardingRulesValue value, a missing attribute value was detected. "+
"A ForwardingRulesValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("ForwardingRulesValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid ForwardingRulesValue Attribute Type",
"While creating a ForwardingRulesValue value, an invalid attribute value was detected. "+
"A ForwardingRulesValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("ForwardingRulesValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("ForwardingRulesValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra ForwardingRulesValue Attribute Value",
"While creating a ForwardingRulesValue value, an extra attribute value was detected. "+
"A ForwardingRulesValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra ForwardingRulesValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewForwardingRulesValueUnknown(), diags
}
entryProtocolAttribute, ok := attributes["entry_protocol"]
if !ok {
diags.AddError(
"Attribute Missing",
`entry_protocol is missing from object`)
return NewForwardingRulesValueUnknown(), diags
}
entryProtocolVal, ok := entryProtocolAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`entry_protocol expected to be basetypes.StringValue, was: %T`, entryProtocolAttribute))
}
if diags.HasError() {
return NewForwardingRulesValueUnknown(), diags
}
return ForwardingRulesValue{
EntryProtocol: entryProtocolVal,
state: attr.ValueStateKnown,
}, diags
}
func NewForwardingRulesValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) ForwardingRulesValue {
object, diags := NewForwardingRulesValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewForwardingRulesValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t ForwardingRulesType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewForwardingRulesValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewForwardingRulesValueUnknown(), nil
}
if in.IsNull() {
return NewForwardingRulesValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewForwardingRulesValueMust(ForwardingRulesValue{}.AttributeTypes(ctx), attributes), nil
}
func (t ForwardingRulesType) ValueType(ctx context.Context) attr.Value {
return ForwardingRulesValue{}
}
var _ basetypes.ObjectValuable = ForwardingRulesValue{}
type ForwardingRulesValue struct {
EntryProtocol basetypes.StringValue `tfsdk:"entry_protocol"`
state attr.ValueState
}
func (v ForwardingRulesValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 1)
var val tftypes.Value
var err error
attrTypes["entry_protocol"] = basetypes.StringType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 1)
val, err = v.EntryProtocol.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["entry_protocol"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v ForwardingRulesValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v ForwardingRulesValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v ForwardingRulesValue) String() string {
return "ForwardingRulesValue"
}
func (v ForwardingRulesValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
var diags diag.Diagnostics
attributeTypes := map[string]attr.Type{
"entry_protocol": basetypes.StringType{},
}
if v.IsNull() {
return types.ObjectNull(attributeTypes), diags
}
if v.IsUnknown() {
return types.ObjectUnknown(attributeTypes), diags
}
objVal, diags := types.ObjectValue(
attributeTypes,
map[string]attr.Value{
"entry_protocol": v.EntryProtocol,
})
return objVal, diags
}
func (v ForwardingRulesValue) Equal(o attr.Value) bool {
other, ok := o.(ForwardingRulesValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.EntryProtocol.Equal(other.EntryProtocol) {
return false
}
return true
}
func (v ForwardingRulesValue) Type(ctx context.Context) attr.Type {
return ForwardingRulesType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v ForwardingRulesValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"entry_protocol": basetypes.StringType{},
}
}
var _ basetypes.ObjectTypable = HealthCheckType{}
type HealthCheckType struct {
basetypes.ObjectType
}
func (t HealthCheckType) Equal(o attr.Type) bool {
other, ok := o.(HealthCheckType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t HealthCheckType) String() string {
return "HealthCheckType"
}
func (t HealthCheckType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
attributes := in.Attributes()
pathAttribute, ok := attributes["path"]
if !ok {
diags.AddError(
"Attribute Missing",
`path is missing from object`)
return nil, diags
}
pathVal, ok := pathAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`path expected to be basetypes.StringValue, was: %T`, pathAttribute))
}
protocolAttribute, ok := attributes["protocol"]
if !ok {
diags.AddError(
"Attribute Missing",
`protocol is missing from object`)
return nil, diags
}
protocolVal, ok := protocolAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`protocol expected to be basetypes.StringValue, was: %T`, protocolAttribute))
}
if diags.HasError() {
return nil, diags
}
return HealthCheckValue{
Path: pathVal,
Protocol: protocolVal,
state: attr.ValueStateKnown,
}, diags
}
func NewHealthCheckValueNull() HealthCheckValue {
return HealthCheckValue{
state: attr.ValueStateNull,
}
}
func NewHealthCheckValueUnknown() HealthCheckValue {
return HealthCheckValue{
state: attr.ValueStateUnknown,
}
}
func NewHealthCheckValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (HealthCheckValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing HealthCheckValue Attribute Value",
"While creating a HealthCheckValue value, a missing attribute value was detected. "+
"A HealthCheckValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("HealthCheckValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid HealthCheckValue Attribute Type",
"While creating a HealthCheckValue value, an invalid attribute value was detected. "+
"A HealthCheckValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("HealthCheckValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("HealthCheckValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra HealthCheckValue Attribute Value",
"While creating a HealthCheckValue value, an extra attribute value was detected. "+
"A HealthCheckValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra HealthCheckValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewHealthCheckValueUnknown(), diags
}
pathAttribute, ok := attributes["path"]
if !ok {
diags.AddError(
"Attribute Missing",
`path is missing from object`)
return NewHealthCheckValueUnknown(), diags
}
pathVal, ok := pathAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`path expected to be basetypes.StringValue, was: %T`, pathAttribute))
}
protocolAttribute, ok := attributes["protocol"]
if !ok {
diags.AddError(
"Attribute Missing",
`protocol is missing from object`)
return NewHealthCheckValueUnknown(), diags
}
protocolVal, ok := protocolAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`protocol expected to be basetypes.StringValue, was: %T`, protocolAttribute))
}
if diags.HasError() {
return NewHealthCheckValueUnknown(), diags
}
return HealthCheckValue{
Path: pathVal,
Protocol: protocolVal,
state: attr.ValueStateKnown,
}, diags
}
func NewHealthCheckValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) HealthCheckValue {
object, diags := NewHealthCheckValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewHealthCheckValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t HealthCheckType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewHealthCheckValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewHealthCheckValueUnknown(), nil
}
if in.IsNull() {
return NewHealthCheckValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewHealthCheckValueMust(HealthCheckValue{}.AttributeTypes(ctx), attributes), nil
}
func (t HealthCheckType) ValueType(ctx context.Context) attr.Value {
return HealthCheckValue{}
}
var _ basetypes.ObjectValuable = HealthCheckValue{}
type HealthCheckValue struct {
Path basetypes.StringValue `tfsdk:"path"`
Protocol basetypes.StringValue `tfsdk:"protocol"`
state attr.ValueState
}
func (v HealthCheckValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 2)
var val tftypes.Value
var err error
attrTypes["path"] = basetypes.StringType{}.TerraformType(ctx)
attrTypes["protocol"] = basetypes.StringType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 2)
val, err = v.Path.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["path"] = val
val, err = v.Protocol.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["protocol"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v HealthCheckValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v HealthCheckValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v HealthCheckValue) String() string {
return "HealthCheckValue"
}
func (v HealthCheckValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
var diags diag.Diagnostics
attributeTypes := map[string]attr.Type{
"path": basetypes.StringType{},
"protocol": basetypes.StringType{},
}
if v.IsNull() {
return types.ObjectNull(attributeTypes), diags
}
if v.IsUnknown() {
return types.ObjectUnknown(attributeTypes), diags
}
objVal, diags := types.ObjectValue(
attributeTypes,
map[string]attr.Value{
"path": v.Path,
"protocol": v.Protocol,
})
return objVal, diags
}
func (v HealthCheckValue) Equal(o attr.Value) bool {
other, ok := o.(HealthCheckValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.Path.Equal(other.Path) {
return false
}
if !v.Protocol.Equal(other.Protocol) {
return false
}
return true
}
func (v HealthCheckValue) Type(ctx context.Context) attr.Type {
return HealthCheckType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v HealthCheckValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"path": basetypes.StringType{},
"protocol": basetypes.StringType{},
}
}
+2 -9
View File
@@ -37,12 +37,6 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
Description: "The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.",
MarkdownDescription: "The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.",
},
"password": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "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 (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.\n",
MarkdownDescription: "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 (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.\n",
},
"port_blocking": schema.BoolAttribute{
Optional: true,
Computed: true,
@@ -69,8 +63,8 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
ElementType: types.Int64Type,
Optional: true,
Computed: true,
Description: "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.",
MarkdownDescription: "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.",
Description: "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.\n",
MarkdownDescription: "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.\n",
},
"user_data": schema.StringAttribute{
Optional: true,
@@ -96,7 +90,6 @@ type ServerModel struct {
Id types.Int64 `tfsdk:"id"`
Image types.String `tfsdk:"image"`
Name types.String `tfsdk:"name"`
Password types.String `tfsdk:"password"`
PortBlocking types.Bool `tfsdk:"port_blocking"`
Region types.String `tfsdk:"region"`
Size types.String `tfsdk:"size"`
+119 -8
View File
@@ -3,6 +3,124 @@
"name": "binarylane"
},
"resources": [
{
"name": "load_balancer",
"schema": {
"attributes": [
{
"name": "forwarding_rules",
"list_nested": {
"computed_optional_required": "computed_optional",
"nested_object": {
"attributes": [
{
"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.",
"validators": [
{
"custom": {
"imports": [
{
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
}
],
"schema_definition": "stringvalidator.OneOf(\n\"http\",\n\"https\",\n)"
}
}
]
}
}
]
},
"description": "The rules that control which traffic the load balancer will forward to servers in the pool. Leave null to accept a default \"HTTP\" only forwarding rule."
}
},
{
"name": "health_check",
"single_nested": {
"computed_optional_required": "computed_optional",
"attributes": [
{
"name": "path",
"string": {
"computed_optional_required": "computed_optional",
"description": "Leave null to accept the default '/' path.",
"validators": [
{
"custom": {
"imports": [
{
"path": "regexp"
},
{
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
}
],
"schema_definition": "stringvalidator.RegexMatches(regexp.MustCompile(\"/[A-Za-z0-9/.?=\u0026+%_-]*\"), \"\")"
}
}
]
}
},
{
"name": "protocol",
"string": {
"computed_optional_required": "computed_optional",
"description": "Leave null to accept the default HTTP protocol."
}
}
],
"description": "The rules that determine which servers are considered 'healthy' and in the server pool for the load balancer. Leave this null to accept appropriate defaults based on the forwarding_rules."
}
},
{
"name": "name",
"string": {
"computed_optional_required": "required",
"description": "The hostname of the load balancer.",
"validators": [
{
"custom": {
"imports": [
{
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
}
],
"schema_definition": "stringvalidator.LengthAtLeast(1)"
}
}
]
}
},
{
"name": "region",
"string": {
"computed_optional_required": "computed_optional",
"description": "Leave null to create an anycast load balancer."
}
},
{
"name": "server_ids",
"list": {
"computed_optional_required": "computed_optional",
"element_type": {
"int64": {}
},
"description": "A list of server IDs to assign to this load balancer."
}
},
{
"name": "id",
"int64": {
"computed_optional_required": "computed_optional",
"description": "The ID of the load balancer to fetch."
}
}
]
}
},
{
"name": "server",
"schema": {
@@ -28,13 +146,6 @@
"description": "The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name."
}
},
{
"name": "password",
"string": {
"computed_optional_required": "computed_optional",
"description": "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 (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.\n"
}
},
{
"name": "port_blocking",
"bool": {
@@ -87,7 +198,7 @@
"element_type": {
"int64": {}
},
"description": "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."
"description": "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.\n"
}
},
{
+25 -15
View File
@@ -29,33 +29,43 @@ resources:
server_id: id
overrides:
backups:
type: bool
description: >
If true this will enable two daily backups for the server. `options.daily_backups` will override this
value if provided. Setting this to false has no effect.
optional: true
default:
static: false
computed: false
image:
type: string
description: The slug of the selected operating system.
example: 'ubuntu-24.04'
password:
type: string
ssh_keys:
description: >
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 (check server.password_change_supported via the
servers endpoint). If omitted and the server supports password change actions a random password will be
generated and emailed to the account email address.
sensitive: true
optional_required: optional
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.
ignores:
- ipv6
- licenses
- links
- server
- options
- password
load_balancer:
create:
path: /load_balancers
method: POST
read:
path: /load_balancers/{load_balancer_id}
method: GET
update:
path: /load_balancers/{load_balancer_id}
method: PUT
delete:
path: /load_balancers/{load_balancer_id}
method: DELETE
schema:
attributes:
aliases:
load_balancer_id: id
ignores:
- links
- load_balancer
server_firewall_rules:
create:
path: /servers/{server_id}/actions#ChangeAdvancedFirewallRules