mirror of
https://github.com/oscarhermoso/terraform-provider-binarylane
synced 2026-10-19 21:49:15 +11:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cafba5330 | |||
| 63632d3abb | |||
| d5f45788b2 | |||
| d0716f58af | |||
| 1f24f45af7 | |||
| 924e785de2 | |||
| f4c91d2f94 | |||
| f73528497f | |||
| f1b7b9853e | |||
| c8cbcd4f19 |
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
- package-ecosystem: 'gomod'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
+3
-4
@@ -27,7 +27,6 @@ go.work.sum
|
||||
# Misc
|
||||
tmp
|
||||
|
||||
# Normally, you wouldn't want to exclude lock files, but in this case, we're
|
||||
# using Terraform's lock file to store the provider version, so we need to
|
||||
# ignore it.
|
||||
terraform.lock.hcl
|
||||
# Normally, you wouldn't want to exclude lock files, but our examples are tested
|
||||
# with a locally built provider and won't be useful to others.
|
||||
.terraform.lock.hcl
|
||||
|
||||
+109
-24
@@ -12,7 +12,6 @@ Based on [this example from the terraform docs](https://developer.hashicorp.com/
|
||||
|
||||
```hcl
|
||||
provider_installation {
|
||||
|
||||
dev_overrides {
|
||||
# Example GOBIN path, will need to be replaced with your own GOBIN path. Default is $GOPATH/bin
|
||||
"oscarhermoso/binarylane" = "/home/oscarhermoso/Git/terraform-provider-binarylane/bin"
|
||||
@@ -32,6 +31,13 @@ terraform plan
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
|
||||
```sh
|
||||
TF_ACC=1 go test -v ./...
|
||||
```
|
||||
|
||||
### Adding resources/data sources to the provider
|
||||
|
||||
1. Make any changes to `provider_gen_config.yml` (see https://developer.hashicorp.com/terraform/plugin/code-generation/openapi-generator#generator-config)
|
||||
@@ -57,31 +63,42 @@ tfplugingen-framework scaffold data-source \
|
||||
|
||||
4. Populate the template scaffolding:
|
||||
|
||||
Define the interfaces that the resource/data source should implement:
|
||||
#### Resource
|
||||
|
||||
Define the interfaces that the resource should implement:
|
||||
|
||||
```diff
|
||||
- var _ resource.Resource = (*replaceMeResource)(nil)
|
||||
+ // Ensure the implementation satisfies the expected interfaces.
|
||||
- var _ resource.Resource = (*exampleResource)(nil)
|
||||
+ var (
|
||||
+ _ resource.Resource = &replaceMeResource{}
|
||||
+ _ resource.ResourceWithConfigure = &replaceMeResource{}
|
||||
+ // _ resource.ResourceWithImportState = &replaceMeResource{}
|
||||
+ _ resource.Resource = &exampleResource{}
|
||||
+ _ resource.ResourceWithConfigure = &exampleResource{}
|
||||
+ _ resource.ResourceWithImportState = &exampleResource{}
|
||||
+ )
|
||||
```
|
||||
|
||||
Pass the API client to the resource:
|
||||
Add the API client to the resource:
|
||||
|
||||
```diff
|
||||
- type replaceMeResource struct{}
|
||||
+ type replaceMeResource struct {
|
||||
- type exampleResource struct{}
|
||||
+ type exampleResource struct {
|
||||
+ bc *BinarylaneClient
|
||||
+ }
|
||||
```
|
||||
|
||||
Extend the resource model from the generated `resources.*Model`.
|
||||
|
||||
```diff
|
||||
type exampleResourceModel struct {
|
||||
- Id types.String `tfsdk:"id"`
|
||||
+ resources.ExampleModel
|
||||
+ // Add any additional fields here
|
||||
}
|
||||
```
|
||||
|
||||
Add a `Configure` method to the resource:
|
||||
|
||||
```diff
|
||||
+ func (d *replaceMeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
+ func (d *exampleResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
+ if req.ProviderData == nil {
|
||||
+ return
|
||||
+ }
|
||||
@@ -89,7 +106,7 @@ Add a `Configure` method to the resource:
|
||||
+ bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
+ if !ok {
|
||||
+ resp.Diagnostics.AddError(
|
||||
+ "Unexpected Data Source Configure Type",
|
||||
+ "Unexpected Resource Configure Type",
|
||||
+ fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
+ )
|
||||
|
||||
@@ -100,18 +117,12 @@ Add a `Configure` method to the resource:
|
||||
+ }
|
||||
```
|
||||
|
||||
Delete the local `*ResourceModel` struct, and import the generated `resources.*Model` instead.
|
||||
|
||||
```diff
|
||||
- type replaceMeResourceModel struct {
|
||||
- Id types.String `tfsdk:"id"`
|
||||
- }
|
||||
```
|
||||
|
||||
```diff
|
||||
func (r *replaceMeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
- var data replaceMeResourceModel
|
||||
+ var data resources.ReplaceMeModel
|
||||
+ func (r *exampleResource) ImportState(
|
||||
+ ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
|
||||
+ ) {
|
||||
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
|
||||
+ }
|
||||
```
|
||||
|
||||
Add the new data source/resource to `provider.go`:
|
||||
@@ -119,6 +130,80 @@ Add the new data source/resource to `provider.go`:
|
||||
```diff
|
||||
return []func() resource.Resource{
|
||||
# ...
|
||||
+ NewReplaceMeResource,
|
||||
+ NewExampleResource,
|
||||
}
|
||||
```
|
||||
|
||||
#### Data Source
|
||||
|
||||
Define the interfaces that the data source should implement:
|
||||
|
||||
```diff
|
||||
- var _ resource.Resource = (*exampleDataSource)(nil)
|
||||
+ var (
|
||||
+ _ datasource.DataSource = &exampleDataSource{}
|
||||
+ _ datasource.DataSourceWithConfigure = &exampleDataSource{}
|
||||
+ )
|
||||
|
||||
Add the API client to the data source:
|
||||
|
||||
```diff
|
||||
- type exampleResource struct{}
|
||||
+ type exampleResource struct {
|
||||
+ bc *BinarylaneClient
|
||||
+ }
|
||||
```
|
||||
|
||||
Derive the data source model from the generated `resources.*Model`.
|
||||
|
||||
```diff
|
||||
type exampleDataSourceModel struct {
|
||||
- Id types.String `tfsdk:"id"`
|
||||
+ resources.ExampleModel
|
||||
}
|
||||
```
|
||||
|
||||
Add a `Configure` method to the data source.
|
||||
|
||||
```diff
|
||||
+ func (d *exampleDataSource) 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
|
||||
+ }
|
||||
```
|
||||
|
||||
Use the generated schema to define the data source schema.
|
||||
|
||||
```diff
|
||||
func (d *exampleDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
- resp.Schema = schema.Schema{
|
||||
- Attributes: map[string]schema.Attribute{
|
||||
- "id": schema.StringAttribute{
|
||||
- Computed: true,
|
||||
- },
|
||||
- },
|
||||
- }
|
||||
+ ds, err := convertResourceSchemaToDataSourceSchema(resources.ExampleResourceSchema(ctx))
|
||||
+ 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"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -4,7 +4,6 @@ See the examples in the [examples directory](./examples/basic/main.tf).
|
||||
|
||||
```terraform
|
||||
resource "binarylane_server" "example" {
|
||||
name = "example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
@@ -16,8 +15,9 @@ resource "binarylane_server" "example" {
|
||||
If somehow you use this in production I would be pretty impressed.
|
||||
|
||||
- [x] Create/delete a server when runing locally
|
||||
- [x] [Publish to Terraform Registry](https://registry.terraform.io/providers/oscarhermoso/binarylane/latest/docs/resources/server)
|
||||
- [ ] (and maybe OpenTofu?)
|
||||
- [x] Publish
|
||||
- [x] [Terraform Registry](https://registry.terraform.io/providers/oscarhermoso/binarylane/latest/docs/resources/server)
|
||||
- [x] OpenTofu
|
||||
- [ ] Deploy the rest of the owl
|
||||
|
||||
## Parameters
|
||||
|
||||
@@ -3,18 +3,34 @@
|
||||
page_title: "binarylane_server Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_server (Data Source)
|
||||
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `id` (Number) The ID of the server to fetch.
|
||||
|
||||
### Optional
|
||||
|
||||
- `backups` (Boolean) If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.
|
||||
- `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.
|
||||
- `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.
|
||||
- `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
|
||||
|
||||
- `id` (String) The ID of this resource.
|
||||
- `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.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_server_firewall_rules Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_server_firewall_rules (Data Source)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Optional
|
||||
|
||||
- `server_id` (Number) The ID of the server for which firewall rules should be listed.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `firewall_rules` (List of Object) A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules. (see [below for nested schema](#nestedatt--firewall_rules))
|
||||
|
||||
<a id="nestedatt--firewall_rules"></a>
|
||||
### Nested Schema for `firewall_rules`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `action` (String)
|
||||
- `description` (String)
|
||||
- `destination_addresses` (List of String)
|
||||
- `destination_ports` (List of String)
|
||||
- `protocol` (String)
|
||||
- `source_addresses` (List of String)
|
||||
@@ -3,18 +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
|
||||
|
||||
- `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
|
||||
|
||||
- `id` (String) The ID of this resource.
|
||||
- `name` (String) A name to help you identify the key.
|
||||
- `public_key` (String) The public key in OpenSSH "authorized_keys" format.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_vpc Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_vpc (Data Source)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Optional
|
||||
|
||||
- `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
|
||||
|
||||
- `name` (String) A name to help identify this VPC.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_vpc_route_entries Data Source - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_vpc_route_entries (Data Source)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `vpc_id` (Number) The target vpc id.
|
||||
|
||||
### Optional
|
||||
|
||||
- `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:
|
||||
|
||||
- `description` (String)
|
||||
- `destination` (String)
|
||||
- `router` (String)
|
||||
@@ -23,7 +23,18 @@ Provides a Binary Lane Server resource. This can be used to create and delete se
|
||||
|
||||
### Optional
|
||||
|
||||
- `backups` (Boolean) If true this will enable two daily backups for the server. Options.daily_backups will override this value if provided. Setting this to false has no effect.
|
||||
- `id` (Number) The ID of the server to fetch.
|
||||
- `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.
|
||||
- `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.
|
||||
- `user_data` (String) If provided this will be used to initialise the new server. This must be left null if the Image does not support UserData, see DistributionInfo.Features for more information.
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
- `wait_for_create` (Number) The number of seconds to wait for the server to be created, after which, a timeout error will be reported. If `wait_seconds` is left empty or set to 0, Terraform will succeed without waiting for the server creation to complete.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (Number) The ID of the server to fetch.
|
||||
- `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.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_server_firewall_rules Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_server_firewall_rules (Resource)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `firewall_rules` (Attributes List) A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules. (see [below for nested schema](#nestedatt--firewall_rules))
|
||||
- `server_id` (Number) The ID of the server for which firewall rules should be listed.
|
||||
|
||||
<a id="nestedatt--firewall_rules"></a>
|
||||
### Nested Schema for `firewall_rules`
|
||||
|
||||
Required:
|
||||
|
||||
- `action` (String) The action to take when there is a match on this rule.
|
||||
- `destination_addresses` (List of String) The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
- `protocol` (String) The protocol to match for this rule.
|
||||
- `source_addresses` (List of String) The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
|
||||
Optional:
|
||||
|
||||
- `description` (String) A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. "Block access to RDP" or "Allow access from HQ".
|
||||
- `destination_ports` (List of String) The destination ports to match for this rule. Leave null or empty to match on all ports.
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_vpc Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_vpc (Resource)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) A name to help identify this VPC.
|
||||
|
||||
### Optional
|
||||
|
||||
- `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
|
||||
|
||||
- `id` (Number) The target vpc id.
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_vpc_route_entries Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_vpc_route_entries (Resource)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `vpc_id` (Number) The target vpc id.
|
||||
|
||||
### Optional
|
||||
|
||||
- `route_entries` (Attributes List) 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`
|
||||
|
||||
Required:
|
||||
|
||||
- `destination` (String) The destination address for this route entry. This may be in CIDR format.
|
||||
- `router` (String) The server that will receive traffic sent to the destination property in this VPC.
|
||||
|
||||
Optional:
|
||||
|
||||
- `description` (String) An optional description for the route.
|
||||
@@ -0,0 +1,10 @@
|
||||
# examples
|
||||
|
||||
In order of increasing complexity, these examples show how to use the provider.
|
||||
|
||||
- [./basic](./basic/main.tf) - A basic example that creates a server.
|
||||
- [./ssh](./ssh/main.tf) - Create a server and registers an SSH key as a global default.
|
||||
- [./cloud-init](./cloud-init/main.tf) - Create a server with a cloud-init configuration.
|
||||
- [./nix](./nix/main.tf) - WIP - Create a server with a NixOS configuration.
|
||||
- [./vpc](./vpc/main.tf) - WIP - Create a server with a Virtual Private Cloud.
|
||||
- [./k3s](./k3s/main.tf) - WIP - Create Kubernetes cluster using k3s.
|
||||
Generated
-24
@@ -1,24 +0,0 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/oscarhermoso/binarylane" {
|
||||
version = "0.1.0"
|
||||
hashes = [
|
||||
"h1:M/GVWGdH+W26pw8o6O2zvzorVZvi0hZbaKCT8scoqT8=",
|
||||
"zh:07c855b20ad789e78602918ce1565b5e1ac411df92b1f9f4a654ef3da77bcd1e",
|
||||
"zh:09530d6532dcff2ccdf48de853d7144f0c66d7be203883461b256f52e719c06d",
|
||||
"zh:2c118c8c7572f60739b4f0ac5cecbadd56636fa63f7f4e7f0c6cdec427fbd609",
|
||||
"zh:3a51045b18ec6f7bd0ff642370f45b7ba936afa9d0e79ff52481a447adb99b66",
|
||||
"zh:64608398b9da27f5b1efa2d164fc20e4cb924ebef6cdf80da7b4329f344dbf13",
|
||||
"zh:a9808dacc075dd53774fe559862f22b9ab02a22b789dde59cff0ea3b42f37c73",
|
||||
"zh:b22dc91a867a470da75f8887f432938cf5db314943c1f90203eff39ce8e24e85",
|
||||
"zh:c110e831eafd8a138f9af9efc1730b8c3295cec54561918676744b0f6968498c",
|
||||
"zh:cd4a98509c740beff50979c1e50c00e5b3915bf35185c89b9f306a2432b54a2c",
|
||||
"zh:cf3b046ec96b5140fb6dacceead8d9ed723debcf580462ab93a5ed1578eaeab8",
|
||||
"zh:d395d9a1d681980f0068e2146a4961e0a39c572ffdf131d5d5c1f3a91296a5e8",
|
||||
"zh:eaf6c069dc35c9a6c97502b1b283d4a815730f70b0cfb637d26e29f2a70c2a51",
|
||||
"zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597",
|
||||
"zh:f31a253fc4c7f0d37ec2bfe2ca2e40c7c51a0db355039d56123160c97bdf7c90",
|
||||
"zh:fdbadc51fc6c9ac6f2a68d5846fdb743be7b0344307c9dbf6d90252753894633",
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,6 @@ terraform {
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
|
||||
@@ -17,5 +16,5 @@ resource "binarylane_server" "example" {
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
|
||||
# password will be generated automatically and mailed to the account holder
|
||||
# Password will be generated automatically and mailed to the account holder
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# Misc
|
||||
|
||||
# Don't commit private keys
|
||||
id_ed25519
|
||||
@@ -0,0 +1,47 @@
|
||||
# examples/cloud-init
|
||||
|
||||
Binary lane servers initialize cloud-init configurations to the server upon creation.
|
||||
|
||||
If you want to deploy multiple parts of a cloud-init configuration, you can use the [cloudinit_config](https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs/data-sources/config) data source.
|
||||
|
||||
```terraform
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com.au/v2"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
|
||||
data "cloudinit_config" "example" {
|
||||
gzip = false
|
||||
base64_encode = false
|
||||
|
||||
part {
|
||||
filename = "hello-script.sh"
|
||||
content_type = "text/x-shellscript"
|
||||
|
||||
content = file("./hello-script.sh")
|
||||
}
|
||||
|
||||
part {
|
||||
filename = "cloud-config.yaml"
|
||||
content_type = "text/cloud-config"
|
||||
|
||||
content = file("./cloud-config.yml")
|
||||
}
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-nix-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
user_data = data.cloudinit_config.example.rendered
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,22 @@
|
||||
#cloud-config
|
||||
bootcmd:
|
||||
# By default, the provider won't wait for the server installation to complete.
|
||||
#
|
||||
# If you want Terraform to wait until the server has fully provisioned, set
|
||||
# the `wait_for_create` attribute on the `binarylane_server` resource.
|
||||
#
|
||||
# If you have additional installation steps that need to be awaited, define
|
||||
# these in the `bootcmd` section and they will also be awaited.
|
||||
#
|
||||
- [cloud-init-per, once, 'example-installing', sh, -c, 'sleep 60'] # pretend this is a slow installation step
|
||||
- [
|
||||
cloud-init-per,
|
||||
once,
|
||||
'example-done',
|
||||
sh,
|
||||
-c,
|
||||
'echo Finished installing, Terraform will now proceed:`date` >> /var/tmp/user_data.log',
|
||||
]
|
||||
|
||||
runcmd:
|
||||
- [sh, -c, 'sleep 99999'] # The provider won't wait for `runcmd` to complete
|
||||
@@ -0,0 +1,18 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-cloud-init-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
wait_for_create = 300 # 5 mins
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
@@ -0,0 +1,7 @@
|
||||
# WIP - examples/k8s
|
||||
|
||||
This WIP example shows how to create a Kubernetes cluster on Binary Lane.
|
||||
|
||||
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
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash -x
|
||||
exec >/tmp/k3s-agent-join-debug.log 2>&1
|
||||
|
||||
export K3S_TOKEN="${cluster_token}"
|
||||
export K3S_URL="https://${cluster_server}:6443"
|
||||
|
||||
curl -sfL https://get.k3s.io | sh -s - agent \
|
||||
--node-name="$(hostname -f)"
|
||||
|
||||
unset K3S_TOKEN
|
||||
unset K3S_URL
|
||||
|
||||
echo "K3s Node Join Completed"
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash -x
|
||||
exec >/tmp/k3s-server-install-debug.log 2>&1
|
||||
|
||||
export INSTALL_K3S_NAME="${cluster_id}"
|
||||
export K3S_TOKEN="${cluster_token}"
|
||||
export K3S_KUBECONFIG_MODE="644"
|
||||
|
||||
curl -sfL https://get.k3s.io | sh -s - server \
|
||||
--node-name="$(hostname -f)"
|
||||
# --disable-cloud-controller \
|
||||
# --disable servicelb \
|
||||
# --disable local-storage \
|
||||
# --disable traefik \
|
||||
|
||||
unset INSTALL_K3S_NAME
|
||||
unset K3S_TOKEN
|
||||
unset K3S_KUBECONFIG_MODE
|
||||
|
||||
echo "Installing Helm"
|
||||
curl -sfL https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
|
||||
|
||||
echo "K3s Setup Completed"
|
||||
@@ -0,0 +1,122 @@
|
||||
locals {
|
||||
cluster_id = "tf-example-k8s"
|
||||
custom_domain = "tf-example-k8s.internal"
|
||||
}
|
||||
|
||||
resource "random_password" "binarylane" {
|
||||
length = 18
|
||||
}
|
||||
|
||||
resource "random_password" "cluster_token" {
|
||||
length = 64
|
||||
special = false
|
||||
}
|
||||
|
||||
# Virtual Private Cloud
|
||||
# ---------------------
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = local.cluster_id
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
# k3s Servers
|
||||
# -----------
|
||||
data "cloudinit_config" "server" {
|
||||
gzip = false
|
||||
base64_encode = false
|
||||
|
||||
part {
|
||||
content_type = "text/x-shellscript"
|
||||
content = templatefile("${path.module}/k3s-server-install.sh", {
|
||||
cluster_id = local.cluster_id,
|
||||
cluster_token = random_password.cluster_token.result,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resource "binarylane_server" "server" {
|
||||
count = 1
|
||||
|
||||
name = "${local.cluster_id}-server-${count.index + 1}"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
password = random_password.binarylane.result
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
user_data = sensitive(data.cloudinit_config.server.rendered)
|
||||
}
|
||||
|
||||
# k3s Agents
|
||||
# ----------
|
||||
data "cloudinit_config" "agent" {
|
||||
gzip = false
|
||||
base64_encode = false
|
||||
|
||||
part {
|
||||
content_type = "text/x-shellscript"
|
||||
content = templatefile("${path.module}/k3s-agent-join.sh", {
|
||||
cluster_token = random_password.cluster_token.result,
|
||||
cluster_server = binarylane_server.server.0.private_ipv4_addresses.0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resource "binarylane_server" "agent" {
|
||||
count = 2
|
||||
|
||||
name = "${local.cluster_id}-agent-${count.index + 1}"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
password = random_password.binarylane.result
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
user_data = sensitive(data.cloudinit_config.agent.rendered)
|
||||
}
|
||||
|
||||
# Virtual Private Cloud Routing
|
||||
# -----------------------------
|
||||
resource "binarylane_vpc_route_entries" "example" {
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
route_entries = [
|
||||
# {
|
||||
# description = "NAT"
|
||||
# destination = "0.0.0.0/0"
|
||||
# router = binarylane_server.gateway.private_ipv4_addresses.0
|
||||
# }
|
||||
]
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "example" {
|
||||
for_each = { for server in flatten([binarylane_server.server, binarylane_server.agent]) : server.id => 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"
|
||||
},
|
||||
{
|
||||
description = "Flannel VXLAN"
|
||||
protocol = "udp"
|
||||
source_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_ports = ["8472"]
|
||||
action = "accept"
|
||||
},
|
||||
{
|
||||
description = "Kubelet metrics"
|
||||
protocol = "tcp"
|
||||
source_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_addresses = [binarylane_vpc.example.ip_range]
|
||||
destination_ports = ["10250"]
|
||||
action = "accept"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {}
|
||||
@@ -0,0 +1,42 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# Misc
|
||||
|
||||
# Don't commit private keys
|
||||
id_ed25519
|
||||
@@ -0,0 +1,3 @@
|
||||
# examples/nix
|
||||
|
||||
WIP
|
||||
@@ -0,0 +1,14 @@
|
||||
#cloud-config
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/canonical/cloud-init/main/cloudinit/config/schemas/schema-cloud-config-v1.json
|
||||
|
||||
#cloud-config
|
||||
write_files:
|
||||
- path: /etc/nixos/host.nix
|
||||
permissions: '0644'
|
||||
content: |
|
||||
{pkgs, ...}:
|
||||
{
|
||||
environment.systemPackages = with pkgs; [ vim ];
|
||||
}
|
||||
bootcmd:
|
||||
- cloud-init-per once "infect" curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIXOS_IMPORT=./host.nix NIX_CHANNEL=nixos-23.05 bash 2>&1 | tee /var/tmp/infect.log
|
||||
@@ -0,0 +1,18 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-nix-example"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
user_data = file("./cloud-config.yml")
|
||||
wait_for_create = 300 # 5 mins
|
||||
}
|
||||
Generated
-24
@@ -1,24 +0,0 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/oscarhermoso/binarylane" {
|
||||
version = "0.1.1"
|
||||
hashes = [
|
||||
"h1:GvTza7TvwgMCegUJ/q+kmteckZ8Sc/lqsIUK2UCxxAU=",
|
||||
"zh:025de19988848b2e6b8dacd61b7e99274641f32ea9a9e1a67f7dad006630916f",
|
||||
"zh:0b5ce83392db3e7693a8c9a71b9c1eb60983c43f758f8a2623a6b64778e683b9",
|
||||
"zh:0bf621429caa7c4468e2996f6c517178761b0492f8611603efd912797ef4da7c",
|
||||
"zh:1c4db19c124efecde6910a7e9ecbdb5f3f9042685457a3828ca0c5a43da9f43d",
|
||||
"zh:30fe222bc7cdfadfe3872d443a8f6db957711fa3961f62601a9b974bda995637",
|
||||
"zh:3b21a79e2d69d76364a043319bd54c86080eee3016f11b73f821f22629e8b0ef",
|
||||
"zh:3bbc5ea741c85267ff25bd6db1c0863548f2da2c2277bfbf30955080282fa4f0",
|
||||
"zh:414887df5094afe07ce13d521c57e161301437f8891876d9d480409ffc51e5e2",
|
||||
"zh:594827a9be3de7ab4fe634be3d933c5c5a0de665293feab34037da6af5355bf6",
|
||||
"zh:7d73d2ca49b8a6d6f8fe23cf0adfc1fae17fcbb2b1f0a6fb7eef0b3cca5995e1",
|
||||
"zh:ad0b99751f3ee17630edeed6bec065defcbb08abd377b09169a7826d0f5896ef",
|
||||
"zh:c4c1ed1f41a238b50a802efc2e8fd025d5e3a1ebd0a6a0b3d84cde1b0a7eda4a",
|
||||
"zh:cfb3b91d255258583d41382f3adea82af76afff7ba607daff739b951a4a27012",
|
||||
"zh:d865a4a7886c8354c0636d005904ef090dfad2ea2f5ef27b1ecad2e2dcd9964b",
|
||||
"zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597",
|
||||
]
|
||||
}
|
||||
+24
-12
@@ -6,25 +6,37 @@ terraform {
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
provider "binarylane" {}
|
||||
|
||||
/**
|
||||
* This example uses the public key in the git repository, but your SSH key will
|
||||
* be different. You can use the following command to generate a new key pair:
|
||||
*
|
||||
* ssh-keygen -t ed25519 -C "your_email@example.com"
|
||||
*
|
||||
* Then, update the "public_key" attribute to point to the location of your
|
||||
* generated public key.
|
||||
*/
|
||||
resource "binarylane_ssh_key" "example" {
|
||||
name = "test-user"
|
||||
public_key = file("./id_ed25519.pub") # Likely "~/.ssh/id_ed25519.pub"
|
||||
default = true # Currently, the server will only register SSH keys that are added as global defaults
|
||||
}
|
||||
name = "tf-example-ssh"
|
||||
|
||||
public_key = file("./id_ed25519.pub") # Change to "~/.ssh/id_ed25519.pub"
|
||||
|
||||
# You also have the option to register an SSH key as a global default, for any
|
||||
# new VM but it is not the recommended approach.
|
||||
# default = false
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example"
|
||||
name = "tf-example-ssh"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
ssh_keys = [
|
||||
binarylane_ssh_key.example.id
|
||||
]
|
||||
|
||||
depends_on = [binarylane_ssh_key.example] # Wait for SSH key to be created as a global default
|
||||
|
||||
# TODO: Extend provider with "ssh_key_id" option for explicit registraton of keys
|
||||
# If you are using a global default SSH key, you need to explicitly wait for
|
||||
# it to be created before creating the server.
|
||||
# depends_on = [binarylane_ssh_key.example]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
@@ -0,0 +1,60 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {}
|
||||
|
||||
resource "binarylane_vpc" "example" {
|
||||
name = "tf-example"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
# Web & NAT server
|
||||
resource "binarylane_server" "web" {
|
||||
name = "tf-vpc-example-web"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
# Database server
|
||||
resource "binarylane_server" "db" {
|
||||
name = "tf-vpc-example-db"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 0
|
||||
}
|
||||
|
||||
# VPN server
|
||||
resource "binarylane_server" "vpn" {
|
||||
name = "tf-vpc-example-vpn"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
public_ipv4_count = 1
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "example" {
|
||||
vpc_id = binarylane_vpc.example.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "NAT"
|
||||
destination = "0.0.0.0/0"
|
||||
router = binarylane_server.web.private_ipv4_addresses.0
|
||||
},
|
||||
{
|
||||
description = "VPN"
|
||||
destination = "192.168.1.0/24"
|
||||
router = binarylane_server.vpn.private_ipv4_addresses.0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -9,7 +9,9 @@ require (
|
||||
github.com/hashicorp/terraform-plugin-docs v0.19.4
|
||||
github.com/hashicorp/terraform-plugin-framework v1.11.0
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.23.0
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.10.0
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
)
|
||||
@@ -21,6 +23,7 @@ require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
|
||||
github.com/agext/levenshtein v1.2.2 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
@@ -37,11 +40,13 @@ require (
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/cli v1.1.6 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
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
|
||||
@@ -49,10 +54,12 @@ require (
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.8.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.21.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.22.1 // indirect
|
||||
github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1 // indirect
|
||||
github.com/hashicorp/terraform-plugin-go v0.23.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
@@ -66,6 +73,8 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/mitchellh/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
|
||||
@@ -76,6 +85,7 @@ require (
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
|
||||
@@ -88,16 +98,17 @@ require (
|
||||
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/exp v0.0.0-20240103183307-be819d1f06fc // indirect
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/net v0.27.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/tools v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.34.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/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -16,8 +16,11 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
|
||||
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||
@@ -77,6 +80,7 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
@@ -109,6 +113,8 @@ github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuD
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI=
|
||||
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
@@ -125,6 +131,10 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hc-install v0.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI=
|
||||
github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
|
||||
github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
|
||||
github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec=
|
||||
@@ -145,6 +155,10 @@ github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/12
|
||||
github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ=
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.10.0 h1:2+tmRNhvnfE4Bs8rB6v58S/VpqzGC6RCh9Y8ujdn+aw=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.10.0/go.mod h1:iWRW3+loP33WMch2P/TEyCxxct/ZEcCGMquSLSCVsrc=
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
|
||||
@@ -195,6 +209,10 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
@@ -260,6 +278,9 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
@@ -285,6 +306,8 @@ github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUei
|
||||
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
|
||||
github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
|
||||
github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw=
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -294,8 +317,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
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/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
|
||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
@@ -310,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.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
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/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=
|
||||
@@ -349,9 +372,11 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
|
||||
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=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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=
|
||||
@@ -365,10 +390,13 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
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/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=
|
||||
@@ -377,9 +405,10 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
|
||||
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -4,16 +4,37 @@ OPENAPI_FILE=$(dirname "$0")/openapi.json
|
||||
|
||||
# Fetch the latest OpenAPI spec
|
||||
curl https://api.binarylane.com.au/reference/openapi.json --output $OPENAPI_FILE
|
||||
# Remove the /v2 prefix from the paths
|
||||
cat <<<$(jq '.paths |= with_entries(.key |= sub("/v2/"; "/"))' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
# Set the base URL with a /v2 suffix
|
||||
|
||||
# Move the /v2 prefix to the base URL and remove it from the paths
|
||||
cat <<<$(jq '.servers[0].url = "https://api.binarylane.com.au/v2"' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths |= with_entries(.key |= sub("/v2/"; "/"))' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
# Terraform can't handle oneOf types, so we need to replace them with basic types
|
||||
cat <<<$(jq '.components.schemas.CreateServerRequest.properties.image |= del(.oneOf) + {type:"string"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.CreateServerRequest.properties.ssh_keys.items |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].delete.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].put.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].get.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].delete.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.paths["/account/keys/{key_id}"].delete.parameters[0].schema |= del(.oneOf) + {type:"integer"}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
# 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
|
||||
|
||||
## RouteEntryRequest
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.destination += {"x-oapi-codegen-extra-tags": {"tfsdk": "destination"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.RouteEntryRequest.properties.router += {"x-oapi-codegen-extra-tags": {"tfsdk": "router"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
## AdvancedFirewallRule
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.protocol += {"x-oapi-codegen-extra-tags": {"tfsdk": "protocol"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.AdvancedFirewallRule.properties.action += {"x-oapi-codegen-extra-tags": {"tfsdk": "action"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
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
|
||||
|
||||
@@ -8811,7 +8811,10 @@
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
"x-cli-format": 1
|
||||
"x-cli-format": 1,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "source_addresses"
|
||||
}
|
||||
},
|
||||
"destination_addresses": {
|
||||
"minItems": 1,
|
||||
@@ -8820,7 +8823,10 @@
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
"x-cli-format": 2
|
||||
"x-cli-format": 2,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "destination_addresses"
|
||||
}
|
||||
},
|
||||
"destination_ports": {
|
||||
"type": "array",
|
||||
@@ -8829,7 +8835,10 @@
|
||||
},
|
||||
"description": "The destination ports to match for this rule. Leave null or empty to match on all ports.",
|
||||
"nullable": true,
|
||||
"x-cli-format": 4
|
||||
"x-cli-format": 4,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "destination_ports"
|
||||
}
|
||||
},
|
||||
"protocol": {
|
||||
"allOf": [
|
||||
@@ -8838,7 +8847,10 @@
|
||||
}
|
||||
],
|
||||
"description": "The protocol to match for this rule.",
|
||||
"x-cli-format": 3
|
||||
"x-cli-format": 3,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "protocol"
|
||||
}
|
||||
},
|
||||
"action": {
|
||||
"allOf": [
|
||||
@@ -8847,7 +8859,10 @@
|
||||
}
|
||||
],
|
||||
"description": "The action to take when there is a match on this rule.",
|
||||
"x-cli-format": 5
|
||||
"x-cli-format": 5,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "action"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"maxLength": 250,
|
||||
@@ -8855,7 +8870,10 @@
|
||||
"type": "string",
|
||||
"description": "A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. \"Block access to RDP\" or \"Allow access from HQ\".",
|
||||
"nullable": true,
|
||||
"x-cli-format": 6
|
||||
"x-cli-format": 6,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "description"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10082,17 +10100,10 @@
|
||||
"ssh_keys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"example": 5
|
||||
"example": 5,
|
||||
"type": "integer"
|
||||
},
|
||||
"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.",
|
||||
"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.",
|
||||
"nullable": true
|
||||
},
|
||||
"options": {
|
||||
@@ -12765,18 +12776,27 @@
|
||||
"router": {
|
||||
"minLength": 1,
|
||||
"type": "string",
|
||||
"description": "The server that will receive traffic sent to the destination property in this VPC."
|
||||
"description": "The server that will receive traffic sent to the destination property in this VPC.",
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "router"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"minLength": 1,
|
||||
"type": "string",
|
||||
"description": "The destination address for this route entry. This may be in CIDR format."
|
||||
"description": "The destination address for this route entry. This may be in CIDR format.",
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "destination"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"maxLength": 250,
|
||||
"type": "string",
|
||||
"description": "An optional description for the route.",
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "description"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -593,22 +593,22 @@ type AdvancedFeature string
|
||||
// AdvancedFirewallRule defines model for AdvancedFirewallRule.
|
||||
type AdvancedFirewallRule struct {
|
||||
// Action The action to take when there is a match on this rule.
|
||||
Action AdvancedFirewallRuleAction `json:"action"`
|
||||
Action AdvancedFirewallRuleAction `json:"action" tfsdk:"action"`
|
||||
|
||||
// Description A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. "Block access to RDP" or "Allow access from HQ".
|
||||
Description *string `json:"description"`
|
||||
Description *string `json:"description" tfsdk:"description"`
|
||||
|
||||
// DestinationAddresses The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
DestinationAddresses []string `json:"destination_addresses"`
|
||||
DestinationAddresses []string `json:"destination_addresses" tfsdk:"destination_addresses"`
|
||||
|
||||
// DestinationPorts The destination ports to match for this rule. Leave null or empty to match on all ports.
|
||||
DestinationPorts *[]string `json:"destination_ports"`
|
||||
DestinationPorts *[]string `json:"destination_ports" tfsdk:"destination_ports"`
|
||||
|
||||
// Protocol The protocol to match for this rule.
|
||||
Protocol AdvancedFirewallRuleProtocol `json:"protocol"`
|
||||
Protocol AdvancedFirewallRuleProtocol `json:"protocol" tfsdk:"protocol"`
|
||||
|
||||
// SourceAddresses The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.
|
||||
SourceAddresses []string `json:"source_addresses"`
|
||||
SourceAddresses []string `json:"source_addresses" tfsdk:"source_addresses"`
|
||||
}
|
||||
|
||||
// AdvancedFirewallRuleAction
|
||||
@@ -1184,8 +1184,8 @@ type CreateServerRequest struct {
|
||||
// Size The slug of the selected size.
|
||||
Size string `json:"size"`
|
||||
|
||||
// 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 *[]CreateServerRequest_SshKeys_Item `json:"ssh_keys"`
|
||||
// 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 *[]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.
|
||||
UserData *string `json:"user_data"`
|
||||
@@ -1194,17 +1194,6 @@ type CreateServerRequest struct {
|
||||
VpcId *int64 `json:"vpc_id"`
|
||||
}
|
||||
|
||||
// CreateServerRequestSshKeys0 defines model for .
|
||||
type CreateServerRequestSshKeys0 = int
|
||||
|
||||
// CreateServerRequestSshKeys1 defines model for .
|
||||
type CreateServerRequestSshKeys1 = string
|
||||
|
||||
// CreateServerRequest_SshKeys_Item defines model for CreateServerRequest.ssh_keys.Item.
|
||||
type CreateServerRequest_SshKeys_Item struct {
|
||||
union json.RawMessage
|
||||
}
|
||||
|
||||
// CreateServerResponse defines model for CreateServerResponse.
|
||||
type CreateServerResponse struct {
|
||||
Links *ActionsLinks `json:"links,omitempty"`
|
||||
@@ -2349,13 +2338,13 @@ type RouteEntry struct {
|
||||
// RouteEntryRequest defines model for RouteEntryRequest.
|
||||
type RouteEntryRequest struct {
|
||||
// Description An optional description for the route.
|
||||
Description *string `json:"description"`
|
||||
Description *string `json:"description" tfsdk:"description"`
|
||||
|
||||
// Destination The destination address for this route entry. This may be in CIDR format.
|
||||
Destination string `json:"destination"`
|
||||
Destination string `json:"destination" tfsdk:"destination"`
|
||||
|
||||
// Router The server that will receive traffic sent to the destination property in this VPC.
|
||||
Router string `json:"router"`
|
||||
Router string `json:"router" tfsdk:"router"`
|
||||
}
|
||||
|
||||
// Sample defines model for Sample.
|
||||
@@ -4020,68 +4009,6 @@ func (t *ChangeImage_Image) UnmarshalJSON(b []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// AsCreateServerRequestSshKeys0 returns the union data inside the CreateServerRequest_SshKeys_Item as a CreateServerRequestSshKeys0
|
||||
func (t CreateServerRequest_SshKeys_Item) AsCreateServerRequestSshKeys0() (CreateServerRequestSshKeys0, error) {
|
||||
var body CreateServerRequestSshKeys0
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromCreateServerRequestSshKeys0 overwrites any union data inside the CreateServerRequest_SshKeys_Item as the provided CreateServerRequestSshKeys0
|
||||
func (t *CreateServerRequest_SshKeys_Item) FromCreateServerRequestSshKeys0(v CreateServerRequestSshKeys0) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeCreateServerRequestSshKeys0 performs a merge with any union data inside the CreateServerRequest_SshKeys_Item, using the provided CreateServerRequestSshKeys0
|
||||
func (t *CreateServerRequest_SshKeys_Item) MergeCreateServerRequestSshKeys0(v CreateServerRequestSshKeys0) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
// AsCreateServerRequestSshKeys1 returns the union data inside the CreateServerRequest_SshKeys_Item as a CreateServerRequestSshKeys1
|
||||
func (t CreateServerRequest_SshKeys_Item) AsCreateServerRequestSshKeys1() (CreateServerRequestSshKeys1, error) {
|
||||
var body CreateServerRequestSshKeys1
|
||||
err := json.Unmarshal(t.union, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
// FromCreateServerRequestSshKeys1 overwrites any union data inside the CreateServerRequest_SshKeys_Item as the provided CreateServerRequestSshKeys1
|
||||
func (t *CreateServerRequest_SshKeys_Item) FromCreateServerRequestSshKeys1(v CreateServerRequestSshKeys1) error {
|
||||
b, err := json.Marshal(v)
|
||||
t.union = b
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeCreateServerRequestSshKeys1 performs a merge with any union data inside the CreateServerRequest_SshKeys_Item, using the provided CreateServerRequestSshKeys1
|
||||
func (t *CreateServerRequest_SshKeys_Item) MergeCreateServerRequestSshKeys1(v CreateServerRequestSshKeys1) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
merged, err := runtime.JSONMerge(t.union, b)
|
||||
t.union = merged
|
||||
return err
|
||||
}
|
||||
|
||||
func (t CreateServerRequest_SshKeys_Item) MarshalJSON() ([]byte, error) {
|
||||
b, err := t.union.MarshalJSON()
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (t *CreateServerRequest_SshKeys_Item) UnmarshalJSON(b []byte) error {
|
||||
err := t.union.UnmarshalJSON(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// AsImageOptionsSshKeys0 returns the union data inside the ImageOptions_SshKeys_Item as a ImageOptionsSshKeys0
|
||||
func (t ImageOptions_SshKeys_Item) AsImageOptionsSshKeys0() (ImageOptionsSshKeys0, error) {
|
||||
var body ImageOptionsSshKeys0
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package data_sources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func ServerDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.Int64Attribute{
|
||||
Required: true,
|
||||
Description: "The ID of the server to fetch.",
|
||||
MarkdownDescription: "The ID of the server to fetch.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ServerModel struct {
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package data_sources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func SshKeyDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.Int64Attribute{
|
||||
Required: true,
|
||||
Description: "The ID or fingerprint of the SSH Key to fetch.",
|
||||
MarkdownDescription: "The ID or fingerprint of the SSH Key to fetch.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type SshKeyModel struct {
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
}
|
||||
@@ -143,13 +143,19 @@ func (p *binarylaneProvider) Metadata(_ context.Context, _ provider.MetadataRequ
|
||||
func (p *binarylaneProvider) DataSources(_ context.Context) []func() datasource.DataSource {
|
||||
return []func() datasource.DataSource{
|
||||
NewServerDataSource,
|
||||
NewServerFirewallRulesDataSource,
|
||||
NewSshKeyDataSource,
|
||||
NewVpcDataSource,
|
||||
NewVpcRouteEntriesDataSource,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *binarylaneProvider) Resources(_ context.Context) []func() resource.Resource {
|
||||
return []func() resource.Resource{
|
||||
NewServerResource,
|
||||
NewServerFirewallRulesResource,
|
||||
NewSshKeyResource,
|
||||
NewVpcResource,
|
||||
NewVpcRouteEntriesResource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
||||
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
||||
)
|
||||
|
||||
const (
|
||||
providerConfig = `
|
||||
provider "binarylane" {
|
||||
# api_token = "test123"
|
||||
# api_endpoint = "https://test.binarylane.internal.au/v2"
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
var (
|
||||
// testAccProtoV6ProviderFactories are used to instantiate a provider during
|
||||
// acceptance testing. The factory function will be invoked for every Terraform
|
||||
// CLI command executed to create a provider server to which the CLI can
|
||||
// reattach.
|
||||
testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
|
||||
"binarylane": providerserver.NewProtocol6WithError(New("test")()),
|
||||
}
|
||||
)
|
||||
@@ -2,36 +2,71 @@ package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"terraform-provider-binarylane/internal/data_sources"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
d_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
var _ datasource.DataSource = (*serverDataSource)(nil)
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &serverDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &serverDataSource{}
|
||||
)
|
||||
|
||||
func NewServerDataSource() datasource.DataSource {
|
||||
return &serverDataSource{}
|
||||
}
|
||||
|
||||
type serverDataSource struct{}
|
||||
type serverDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
func (d *serverDataSource) 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 *serverDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_server"
|
||||
}
|
||||
|
||||
func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.ServerResourceSchema(ctx))
|
||||
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"
|
||||
|
||||
// Overrides
|
||||
id := resp.Schema.Attributes["id"]
|
||||
resp.Schema.Attributes["id"] = d_schema.Int64Attribute{
|
||||
Description: id.GetDescription(),
|
||||
MarkdownDescription: id.GetMarkdownDescription(),
|
||||
Required: true, // ID is required to find the server
|
||||
}
|
||||
}
|
||||
|
||||
func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data data_sources.ServerModel
|
||||
var data resources.ServerModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
@@ -40,10 +75,51 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
// TODO: Use go routines here to make requests concurrently
|
||||
|
||||
// Example data value setting
|
||||
// data.Id = types.StringValue("example-id")
|
||||
// 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()),
|
||||
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),
|
||||
)
|
||||
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)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &serverFirewallRulesDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &serverFirewallRulesDataSource{}
|
||||
)
|
||||
|
||||
func NewServerFirewallRulesDataSource() datasource.DataSource {
|
||||
return &serverFirewallRulesDataSource{}
|
||||
}
|
||||
|
||||
type serverFirewallRulesDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type serverFirewallRulesDataSourceModel struct {
|
||||
resources.ServerFirewallRulesModel
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_server_firewall_rules"
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.ServerFirewallRulesResourceSchema(ctx))
|
||||
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 *serverFirewallRulesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data serverFirewallRulesDataSourceModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()))
|
||||
fwResp, err := d.bc.client.GetServersServerIdAdvancedFirewallRulesWithResponse(ctx, data.ServerId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if fwResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading server firewall rules",
|
||||
fmt.Sprintf("Received %s reading server firewall rules: server_id=%d. Details: %s", fwResp.Status(), data.ServerId.ValueInt64(), fwResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
firewallRulesValue, diags := GetFirewallRulesState(ctx, &fwResp.JSON200.FirewallRules)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.FirewallRules = firewallRulesValue
|
||||
|
||||
// Save into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesDataSource) 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
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"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/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"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-log/tflog"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &serverFirewallRulesResource{}
|
||||
_ resource.ResourceWithConfigure = &serverFirewallRulesResource{}
|
||||
_ resource.ResourceWithImportState = &serverFirewallRulesResource{}
|
||||
)
|
||||
|
||||
func NewServerFirewallRulesResource() resource.Resource {
|
||||
return &serverFirewallRulesResource{}
|
||||
}
|
||||
|
||||
type serverFirewallRulesResource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type serverFirewallRulesResourceModel struct {
|
||||
resources.ServerFirewallRulesModel
|
||||
}
|
||||
|
||||
func (d *serverFirewallRulesResource) 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 *serverFirewallRulesResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_server_firewall_rules"
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.ServerFirewallRulesResourceSchema(ctx)
|
||||
resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
serverId := resp.Schema.Attributes["server_id"]
|
||||
resp.Schema.Attributes["server_id"] = &schema.Int64Attribute{
|
||||
Description: serverId.GetDescription(),
|
||||
MarkdownDescription: serverId.GetMarkdownDescription(),
|
||||
Required: true, // Server ID is required to define the firewall rules
|
||||
Validators: []validator.Int64{
|
||||
int64validator.AtLeast(1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
firewallRules := []binarylane.AdvancedFirewallRule{}
|
||||
diags := data.FirewallRules.ElementsAs(ctx, &firewallRules, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if len(firewallRules) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Creating server firewall rules: server_id=%s", data.ServerId.String()))
|
||||
serverResp, err := r.bc.client.PostServersServerIdActionsChangeAdvancedFirewallRulesWithResponse(
|
||||
ctx,
|
||||
data.ServerId.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeAdvancedFirewallRulesJSONRequestBody{
|
||||
Type: "change_advanced_firewall_rules",
|
||||
FirewallRules: firewallRules,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating server firewall rules: server_id=%s", data.ServerId.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server firewall rules",
|
||||
fmt.Sprintf("Received %s creating server firewall rules: server_id=%s. Details: %s", serverResp.Status(), data.ServerId.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// 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
|
||||
tflog.Debug(ctx, fmt.Sprintf("Reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()))
|
||||
fwResp, err := r.bc.client.GetServersServerIdAdvancedFirewallRulesWithResponse(ctx, data.ServerId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server firewall rules: server_id=%d", data.ServerId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if fwResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading server firewall rules",
|
||||
fmt.Sprintf("Received %s reading server firewall rules: server_id=%d. Details: %s", fwResp.Status(), data.ServerId.ValueInt64(), fwResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
firewallRulesValue, diags := GetFirewallRulesState(ctx, &fwResp.JSON200.FirewallRules)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.FirewallRules = firewallRulesValue
|
||||
|
||||
// Save into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
firewallRules := []binarylane.AdvancedFirewallRule{}
|
||||
diags := data.FirewallRules.ElementsAs(ctx, &firewallRules, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if len(firewallRules) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Updating server firewall rules: server_id=%s", data.ServerId.String()))
|
||||
serverResp, err := r.bc.client.PostServersServerIdActionsChangeAdvancedFirewallRulesWithResponse(
|
||||
ctx,
|
||||
data.ServerId.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeAdvancedFirewallRulesJSONRequestBody{
|
||||
Type: "change_advanced_firewall_rules",
|
||||
FirewallRules: firewallRules,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error updating server firewall rules: server_id=%s", data.ServerId.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code updating server firewall rules",
|
||||
fmt.Sprintf("Received %s updating server firewall rules: server_id=%s. Details: %s", serverResp.Status(), data.ServerId.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data serverFirewallRulesResourceModel
|
||||
|
||||
// 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 server firewall rules: server_id=%s", data.ServerId.String()))
|
||||
serverResp, err := r.bc.client.PostServersServerIdActionsChangeAdvancedFirewallRulesWithResponse(
|
||||
ctx,
|
||||
data.ServerId.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeAdvancedFirewallRulesJSONRequestBody{
|
||||
Type: "change_advanced_firewall_rules",
|
||||
FirewallRules: []binarylane.AdvancedFirewallRule{},
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting server firewall rules: server_id=%s", data.ServerId.String()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting server firewall rules",
|
||||
fmt.Sprintf("Received %s deleting server firewall rules: server_id=%s. Details: %s", serverResp.Status(), data.ServerId.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetFirewallRulesState(ctx context.Context, firewallRules *[]binarylane.AdvancedFirewallRule) (basetypes.ListValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
firewallRulesValue := resources.FirewallRulesValue{}
|
||||
var firewallRulesValues []resources.FirewallRulesValue
|
||||
|
||||
if *firewallRules == nil || len(*firewallRules) == 0 {
|
||||
firewallRulesValues = []resources.FirewallRulesValue{}
|
||||
} else {
|
||||
for _, rule := range *firewallRules {
|
||||
destinationAddresses, diag := types.ListValueFrom(ctx, types.StringType, rule.DestinationAddresses)
|
||||
diags.Append(diag...)
|
||||
|
||||
var destinationPorts basetypes.ListValue
|
||||
if rule.DestinationPorts != nil {
|
||||
destinationPorts, diag = types.ListValueFrom(ctx, types.StringType, rule.DestinationPorts)
|
||||
diags.Append(diag...)
|
||||
}
|
||||
|
||||
sourceAddresses, diag := types.ListValueFrom(ctx, types.StringType, rule.SourceAddresses)
|
||||
diags.Append(diag...)
|
||||
|
||||
if diags.HasError() {
|
||||
return basetypes.NewListUnknown(firewallRulesValue.Type(ctx)), diags
|
||||
}
|
||||
|
||||
ruleValue := resources.NewFirewallRulesValueMust(firewallRulesValue.AttributeTypes(ctx), map[string]attr.Value{
|
||||
"description": types.StringPointerValue(rule.Description),
|
||||
"action": types.StringValue(string(rule.Action)),
|
||||
"protocol": types.StringValue(string(rule.Protocol)),
|
||||
"destination_addresses": destinationAddresses,
|
||||
"destination_ports": destinationPorts,
|
||||
"source_addresses": sourceAddresses,
|
||||
})
|
||||
firewallRulesValues = append(firewallRulesValues, ruleValue)
|
||||
}
|
||||
}
|
||||
|
||||
firewallRulesListValue, diag := types.ListValueFrom(ctx, firewallRulesValue.Type(ctx), firewallRulesValues)
|
||||
diags.Append(diag...)
|
||||
if diags.HasError() {
|
||||
return basetypes.NewListUnknown(firewallRulesValue.Type(ctx)), diags
|
||||
}
|
||||
|
||||
return firewallRulesListValue, diags
|
||||
}
|
||||
|
||||
func (r *serverFirewallRulesResource) ImportState(
|
||||
ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
|
||||
) {
|
||||
id, err := strconv.ParseInt(req.ID, 10, 32)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error importing server firewall rules",
|
||||
"Could not import server firewall rules, unexpected error (ID should be an integer): "+err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("server_id"), id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
)
|
||||
|
||||
func TestServerFirewallRulesResource(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
pw_bytes := make([]byte, 12)
|
||||
rand.Read(pw_bytes)
|
||||
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" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server.test.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow SSH"
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["22"]
|
||||
action = "accept"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
data "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server_firewall_rules.test.server_id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "server_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.description", "Allow SSH"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.0", "22"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server_firewall_rules.test", "server_id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.description", "Allow SSH"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.0"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.0", "22"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
),
|
||||
},
|
||||
// ImportState testing
|
||||
// {
|
||||
// ResourceName: "binarylane_server.test",
|
||||
// ImportState: true,
|
||||
// ImportStateVerify: true,
|
||||
// },
|
||||
{
|
||||
ResourceName: "binarylane_server_firewall_rules.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIdentifierAttribute: "server_id",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
resourceState := s.RootModule().Resources["binarylane_server_firewall_rules.test"]
|
||||
return resourceState.Primary.Attributes["server_id"], nil
|
||||
},
|
||||
},
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
wait_for_create = 60 # Must wait for the server to be ready before creating firewall rules
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server.test.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow HTTP" # Updated description
|
||||
protocol = "tcp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
destination_ports = ["80"] # Updated port
|
||||
action = "accept"
|
||||
},
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values updated
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "server_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.description", "Allow HTTP"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.0", "80"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
),
|
||||
},
|
||||
// Delete testing automatically occurs in TestCase
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -6,10 +6,19 @@ import (
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
"time"
|
||||
|
||||
"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/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/listplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
@@ -30,6 +39,14 @@ type serverResource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
func (d *serverResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
@@ -38,7 +55,7 @@ func (d *serverResource) Configure(_ context.Context, req resource.ConfigureRequ
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
)
|
||||
|
||||
@@ -57,27 +74,124 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
resp.Schema.Description = "Provides a Binary Lane Server resource. This can be used to create and delete servers."
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
pw := resp.Schema.Attributes["password"]
|
||||
resp.Schema.Attributes["password"] = &schema.StringAttribute{
|
||||
Description: pw.GetDescription(),
|
||||
MarkdownDescription: pw.GetMarkdownDescription(),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Sensitive: true, // Add sensitive to password
|
||||
Optional: pw.IsOptional(),
|
||||
Computed: false, // Computed must be false to allow server to be created without password
|
||||
Sensitive: true, // Mark password as sensitive
|
||||
}
|
||||
|
||||
backups := resp.Schema.Attributes["backups"]
|
||||
resp.Schema.Attributes["backups"] = &schema.BoolAttribute{
|
||||
Description: backups.GetDescription(),
|
||||
MarkdownDescription: backups.GetMarkdownDescription(),
|
||||
Optional: backups.IsOptional(),
|
||||
Computed: backups.IsComputed(),
|
||||
Default: booldefault.StaticBool(false), // Add default to backups
|
||||
}
|
||||
|
||||
user_data := resp.Schema.Attributes["user_data"]
|
||||
resp.Schema.Attributes["user_data"] = &schema.StringAttribute{
|
||||
Description: user_data.GetDescription(),
|
||||
MarkdownDescription: user_data.GetMarkdownDescription(),
|
||||
Optional: true, // Optional as not all servers have an initialization script
|
||||
Computed: false, // User defined
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(), // If user changes init script, assume they want to replace server
|
||||
},
|
||||
}
|
||||
|
||||
vpcId := resp.Schema.Attributes["vpc_id"]
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
Description: vpcId.GetDescription(),
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Optional: vpcId.IsOptional(),
|
||||
Computed: false, // VPC ID is not computed, defined at creation
|
||||
}
|
||||
|
||||
portBlocking := resp.Schema.Attributes["port_blocking"]
|
||||
resp.Schema.Attributes["port_blocking"] = &schema.BoolAttribute{
|
||||
Description: portBlocking.GetDescription(),
|
||||
MarkdownDescription: portBlocking.GetMarkdownDescription(),
|
||||
Optional: portBlocking.IsOptional(),
|
||||
Computed: portBlocking.IsComputed(),
|
||||
Default: booldefault.StaticBool(true), // Add default to port_blocking
|
||||
}
|
||||
|
||||
sshKeys := resp.Schema.Attributes["ssh_keys"]
|
||||
resp.Schema.Attributes["ssh_keys"] = &schema.ListAttribute{
|
||||
ElementType: types.Int64Type,
|
||||
Description: sshKeys.GetMarkdownDescription(),
|
||||
MarkdownDescription: sshKeys.GetDescription(),
|
||||
Optional: sshKeys.IsOptional(),
|
||||
Computed: false, // SSH keys are not computed, defined at creation
|
||||
PlanModifiers: []planmodifier.List{listplanmodifier.RequiresReplace()}, // Cannot update SSH keys with API
|
||||
Validators: []validator.List{
|
||||
listvalidator.ValueInt64sAre(int64validator.AtLeast(1)),
|
||||
},
|
||||
}
|
||||
|
||||
// Additional attributes
|
||||
waitDescription := "The number of seconds to wait for the server to be created, after which, a timeout error will " +
|
||||
"be reported. If `wait_seconds` is left empty or set to 0, Terraform will succeed without waiting for the " +
|
||||
"server creation to complete."
|
||||
resp.Schema.Attributes["wait_for_create"] = &schema.Int32Attribute{
|
||||
Description: waitDescription,
|
||||
MarkdownDescription: waitDescription,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Default: booldefault.StaticBool(false), // Add default to backups
|
||||
Default: int32default.StaticInt32(0),
|
||||
}
|
||||
|
||||
publicIpv4CountDescription := "The number of public IPv4 addresses to assign to the server. If this is not provided, the " +
|
||||
"server will be created with the default number of public IPv4 addresses."
|
||||
resp.Schema.Attributes["public_ipv4_count"] = &schema.Int32Attribute{
|
||||
Description: publicIpv4CountDescription,
|
||||
MarkdownDescription: publicIpv4CountDescription,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Validators: []validator.Int32{
|
||||
int32validator.AtLeast(0),
|
||||
},
|
||||
}
|
||||
|
||||
publicIpv4AddressesDescription := "The public IPv4 addresses assigned to the server."
|
||||
resp.Schema.Attributes["public_ipv4_addresses"] = &schema.ListAttribute{
|
||||
Description: publicIpv4AddressesDescription,
|
||||
MarkdownDescription: publicIpv4AddressesDescription,
|
||||
ElementType: types.StringType,
|
||||
// read only
|
||||
Optional: false,
|
||||
Required: false,
|
||||
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,
|
||||
// read only
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data resources.ServerModel
|
||||
var data serverModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
diags := req.Plan.Get(ctx, &data)
|
||||
@@ -89,11 +203,24 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
// Create API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Creating server: name=%s", data.Name.ValueString()))
|
||||
|
||||
sshKeys := []int{}
|
||||
diags = data.SshKeys.ElementsAs(ctx, &sshKeys, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
body := binarylane.CreateServerRequest{
|
||||
Name: data.Name.ValueStringPointer(),
|
||||
Image: data.Image.ValueString(),
|
||||
Region: data.Region.ValueString(),
|
||||
Size: data.Size.ValueString(),
|
||||
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,
|
||||
Options: &binarylane.SizeOptionsRequest{
|
||||
Ipv4Addresses: data.PublicIpv4Count.ValueInt32Pointer(),
|
||||
},
|
||||
}
|
||||
|
||||
if data.Password.IsNull() {
|
||||
@@ -118,24 +245,84 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
return
|
||||
}
|
||||
|
||||
assignInt64(serverResp.JSON200.Server.Id, &data.Id)
|
||||
assignStr(serverResp.JSON200.Server.Name, &data.Name)
|
||||
assignStr(serverResp.JSON200.Server.Image.Slug, &data.Image)
|
||||
assignStr(serverResp.JSON200.Server.Region.Slug, &data.Region)
|
||||
assignStr(serverResp.JSON200.Server.Size.Slug, &data.Size)
|
||||
data.Id = types.Int64Value(*serverResp.JSON200.Server.Id)
|
||||
data.Name = types.StringValue(*serverResp.JSON200.Server.Name)
|
||||
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
|
||||
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
|
||||
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
// assignBool(&serverResp.JSON200.Server.Networks.PortBlocking, &plan.PortBlocking)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
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)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
if data.WaitForCreateSeconds <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for server to be ready
|
||||
var createActionId int64
|
||||
for _, action := range *serverResp.JSON200.Links.Actions {
|
||||
if *action.Rel == "create" {
|
||||
createActionId = *action.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
if createActionId == 0 {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unable to wait for server to be created, links.actions with rel=create missing from response",
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
timeLimit := time.Now().Add(time.Duration(data.WaitForCreateSeconds) * time.Second)
|
||||
for {
|
||||
tflog.Info(ctx, "Waiting for server to be ready...")
|
||||
|
||||
readyResp, err := r.bc.client.GetServersServerIdActionsActionIdWithResponse(ctx, data.Id.ValueInt64(), createActionId)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for server to be ready", err.Error())
|
||||
return
|
||||
}
|
||||
if readyResp.StatusCode() == http.StatusOK && readyResp.JSON200.Action.CompletedAt != nil {
|
||||
tflog.Info(ctx, "Server is ready")
|
||||
break
|
||||
}
|
||||
if time.Now().After(timeLimit) {
|
||||
resp.Diagnostics.AddError(
|
||||
"Timed out waiting for server to be ready",
|
||||
fmt.Sprintf(
|
||||
"Timed out waiting for server to be created, as `wait_for_create` was surpassed without "+
|
||||
"recieving a `completed_at` in response: name=%s, status=%s, details: %s",
|
||||
data.Name.ValueString(), readyResp.Status(), readyResp.Body,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("Waiting for server to be ready: name=%s, status=%s, details: %s", data.Name.ValueString(), readyResp.Status(), readyResp.Body))
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data resources.ServerModel
|
||||
var data serverModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
@@ -144,12 +331,12 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Reading server: name=%s", data.Id.String()))
|
||||
tflog.Debug(ctx, fmt.Sprintf("Reading server: id=%s, name=%s", data.Id.String(), data.Name.ValueString()))
|
||||
|
||||
serverResp, err := r.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: id=%s, name=%s", data.Id.String(), data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
@@ -158,12 +345,15 @@ 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: name=%s. Details: %s", serverResp.Status(), data.Id.String(), serverResp.Body))
|
||||
fmt.Sprintf("Received %s reading server: id=%s, name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), data.Id.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
assignInt64(serverResp.JSON200.Server.Id, &data.Id)
|
||||
assignStr(serverResp.JSON200.Server.Name, &data.Name)
|
||||
data.Id = types.Int64Value(*serverResp.JSON200.Server.Id)
|
||||
data.Name = types.StringValue(*serverResp.JSON200.Server.Name)
|
||||
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
|
||||
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
@@ -174,7 +364,7 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
}
|
||||
|
||||
func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data resources.ServerModel
|
||||
var data serverModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
@@ -191,7 +381,7 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
}
|
||||
|
||||
func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data resources.ServerModel
|
||||
var data serverModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
@@ -224,3 +414,20 @@ func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest,
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// func (r *serverResource) ImportState(
|
||||
// ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
|
||||
// ) {
|
||||
// id, err := strconv.ParseInt(req.ID, 10, 32)
|
||||
|
||||
// if err != nil {
|
||||
// resp.Diagnostics.AddError(
|
||||
// "Error importing server",
|
||||
// "Could not import server, unexpected error (ID should be an integer): "+err.Error(),
|
||||
// )
|
||||
// return
|
||||
// }
|
||||
|
||||
// diags := resp.State.SetAttribute(ctx, path.Root("id"), int32(id))
|
||||
// resp.Diagnostics.Append(diags...)
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestServerResource(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
pw_bytes := make([]byte, 12)
|
||||
_, err := rand.Read(pw_bytes)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to generate password: %s", err)
|
||||
return
|
||||
}
|
||||
password := base64.URLEncoding.EncodeToString(pw_bytes)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
public_key = "` + GeneratePublicKey(t) + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_ssh_key" "unused" {
|
||||
name = "tf-test-server-resource-unused"
|
||||
public_key = "` + GeneratePublicKey(t) + `"
|
||||
default = true
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
public_ipv4_count = 0
|
||||
user_data = <<EOT
|
||||
#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
EOT
|
||||
ssh_keys = [binarylane_ssh_key.test.id]
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "region", "per"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-12"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", `#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
`),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "0"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "private_ipv4_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "port_blocking", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"), // Only the test SSH key should be registered
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.test", "id"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "name", "tf-test-server-resource"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "region", "per"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "debian-12"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "size", "std-min"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -7,11 +7,14 @@ import (
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
var _ datasource.DataSource = (*sshKeyDataSource)(nil)
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &sshKeyDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &sshKeyDataSource{}
|
||||
)
|
||||
|
||||
func NewSshKeyDataSource() datasource.DataSource {
|
||||
return &sshKeyDataSource{}
|
||||
@@ -21,18 +24,36 @@ type sshKeyDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
func (d *sshKeyDataSource) 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 *sshKeyDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_ssh_key"
|
||||
}
|
||||
|
||||
func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.SshKeyResourceSchema(ctx))
|
||||
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 *sshKeyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"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/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
@@ -36,7 +38,7 @@ func (d *sshKeyResource) Configure(_ context.Context, req resource.ConfigureRequ
|
||||
bc, ok := req.ProviderData.(BinarylaneClient)
|
||||
if !ok {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected Data Source Configure Type",
|
||||
"Unexpected Resource Configure Type",
|
||||
fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData),
|
||||
)
|
||||
|
||||
@@ -55,14 +57,22 @@ func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest,
|
||||
resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
defaultAttr := resp.Schema.Attributes["default"]
|
||||
default_ := resp.Schema.Attributes["default"]
|
||||
resp.Schema.Attributes["default"] = schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: defaultAttr.GetDescription(),
|
||||
MarkdownDescription: defaultAttr.GetMarkdownDescription(),
|
||||
Description: default_.GetDescription(),
|
||||
MarkdownDescription: default_.GetMarkdownDescription(),
|
||||
Default: booldefault.StaticBool(false), // Add default to backups
|
||||
}
|
||||
|
||||
public_key := resp.Schema.Attributes["public_key"]
|
||||
resp.Schema.Attributes["public_key"] = schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: public_key.GetDescription(),
|
||||
MarkdownDescription: public_key.GetMarkdownDescription(),
|
||||
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
@@ -113,6 +123,8 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
data.PublicKey = types.StringValue(data.PublicKey.ValueString())
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
@@ -138,7 +150,7 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
|
||||
data.Default = types.BoolValue(*sshResp.JSON200.SshKey.Default)
|
||||
data.Name = types.StringValue(*sshResp.JSON200.SshKey.Name)
|
||||
data.PublicKey = types.StringValue(*sshResp.JSON200.SshKey.PublicKey)
|
||||
// data.PublicKey = types.StringValue(*sshResp.JSON200.SshKey.PublicKey) // don't set or it will force replacement every time
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
@@ -149,13 +161,29 @@ func (r *sshKeyResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
// TODO
|
||||
sshResp, err := r.bc.client.PutAccountKeysKeyIdWithResponse(ctx, int(data.Id.ValueInt64()), binarylane.UpdateSshKeyRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
Default: data.Default.ValueBoolPointer(),
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error updating SSH Key: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code updating SSH Key",
|
||||
fmt.Sprintf("Received %s updating new SSH Key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
@@ -188,3 +216,8 @@ func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest,
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestSshKeyResource(t *testing.T) {
|
||||
publicKey := GeneratePublicKey(t)
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_ssh_key" "test" {
|
||||
name = "tf_ssh_key_resource_test"
|
||||
public_key = "` + publicKey + `"
|
||||
}
|
||||
|
||||
data "binarylane_ssh_key" "test" {
|
||||
depends_on = [binarylane_ssh_key.test]
|
||||
|
||||
id = binarylane_ssh_key.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "public_key", publicKey),
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "default", "false"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_ssh_key.test", "id"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_ssh_key.test", "public_key"), // Ideally would check this is identical, but whitespace is not preserved
|
||||
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "false"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_ssh_key.test", "id"),
|
||||
),
|
||||
},
|
||||
// ImportState testing
|
||||
// TODO
|
||||
// {
|
||||
// ResourceName: "binarylane_ssh_key.test",
|
||||
// ImportState: true,
|
||||
// ImportStateVerify: true,
|
||||
// ImportStateVerifyIgnore: []string{}, // nothing to ignore
|
||||
// },
|
||||
// TODO: Update and Read testing
|
||||
// {
|
||||
// Config: providerConfig + `
|
||||
// resource "binarylane_ssh_key" "test" {
|
||||
// name = "tf_ssh_key_resource_test"
|
||||
// public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"
|
||||
// default = true
|
||||
// }
|
||||
// `,
|
||||
// Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// // Verify resource values
|
||||
// resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
// resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "true"),
|
||||
// ),
|
||||
// },
|
||||
// Delete testing automatically occurs in TestCase
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func GeneratePublicKey(t *testing.T) string {
|
||||
t.Helper()
|
||||
|
||||
_, pub, err := ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", err)
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(pub)
|
||||
return fmt.Sprintf("ssh-ed25519 %s test@company.internal", encoded)
|
||||
}
|
||||
+104
-27
@@ -1,36 +1,113 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
)
|
||||
|
||||
func assignStr(source *string, target *basetypes.StringValue) {
|
||||
if source != nil {
|
||||
*target = types.StringValue(*source)
|
||||
func convertResourceSchemaToDataSourceSchema(rs r_schema.Schema) (*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),
|
||||
Description: rs.GetDescription(),
|
||||
MarkdownDescription: rs.GetMarkdownDescription(),
|
||||
DeprecationMessage: rs.GetDeprecationMessage(),
|
||||
}
|
||||
}
|
||||
|
||||
// func assignInt(source *int, target *basetypes.Int64Value) {
|
||||
// if source != nil {
|
||||
// *target = types.Int64Value(int64(*source))
|
||||
// }
|
||||
// }
|
||||
|
||||
func assignInt64(source *int64, target *basetypes.Int64Value) {
|
||||
if source != nil {
|
||||
*target = types.Int64Value(int64(*source))
|
||||
for name, attr := range rs.Attributes {
|
||||
switch attr.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(),
|
||||
}
|
||||
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(),
|
||||
}
|
||||
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(),
|
||||
}
|
||||
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(),
|
||||
}
|
||||
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(),
|
||||
}
|
||||
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(),
|
||||
}
|
||||
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(),
|
||||
}
|
||||
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(),
|
||||
}
|
||||
default:
|
||||
// Feel free to to raise a PR and remove this hack
|
||||
if strings.HasPrefix(attr.GetType().String(), "types.ListType") {
|
||||
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(),
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("failed to convert resource schema attribute to data source schema attribute: name=%s, type=%s", name, attr.GetType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &ds, nil
|
||||
}
|
||||
|
||||
// func assignFloat(source *float32, target *basetypes.Float64Value) {
|
||||
// if source != nil {
|
||||
// *target = types.Float64Value(float64(*source))
|
||||
// }
|
||||
// }
|
||||
|
||||
// func assignBool(source *bool, target *basetypes.BoolValue) {
|
||||
// if source != nil {
|
||||
// *target = types.BoolValue(bool(*source))
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &sshKeyDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &sshKeyDataSource{}
|
||||
)
|
||||
|
||||
func NewVpcDataSource() datasource.DataSource {
|
||||
return &vpcDataSource{}
|
||||
}
|
||||
|
||||
type vpcDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type vpcDataSourceModel struct {
|
||||
resources.VpcModel
|
||||
}
|
||||
|
||||
func (d *vpcDataSource) 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 *vpcDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_vpc"
|
||||
}
|
||||
|
||||
func (d *vpcDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.VpcResourceSchema(ctx))
|
||||
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 *vpcDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data vpcDataSourceModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
diags := req.Config.Get(ctx, &data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
vpcResp, err := d.bc.client.GetVpcsVpcIdWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading VPC: id=%d", data.Id.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading VPC",
|
||||
fmt.Sprintf("Received %s reading VPC: id=%d. Details: %s", vpcResp.Status(), data.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
data.IpRange = types.StringValue(*vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"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/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &vpcResource{}
|
||||
_ resource.ResourceWithConfigure = &vpcResource{}
|
||||
_ resource.ResourceWithImportState = &vpcResource{}
|
||||
)
|
||||
|
||||
func NewVpcResource() resource.Resource {
|
||||
return &vpcResource{}
|
||||
}
|
||||
|
||||
type vpcResource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type vpcResourceModel struct {
|
||||
resources.VpcModel
|
||||
}
|
||||
|
||||
func (r *vpcResource) 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
|
||||
}
|
||||
|
||||
r.bc = &bc
|
||||
}
|
||||
|
||||
func (r *vpcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_vpc"
|
||||
}
|
||||
|
||||
func (r *vpcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.VpcResourceSchema(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,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *vpcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data vpcResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
diags := req.Plan.Get(ctx, &data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
tflog.Debug(ctx, fmt.Sprintf("Creating VPC: name=%s", data.Name.ValueString()))
|
||||
|
||||
body := binarylane.CreateVpcRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
IpRange: data.IpRange.ValueStringPointer(),
|
||||
}
|
||||
|
||||
vpcResp, err := r.bc.client.PostVpcsWithResponse(ctx, body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating VPC: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating VPC",
|
||||
fmt.Sprintf("Received %s creating new VPC: name=%s. Details: %s", vpcResp.Status(), data.Name.ValueString(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
data.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *vpcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data vpcResourceModel
|
||||
|
||||
// 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
|
||||
vpcResp, err := r.bc.client.GetVpcsVpcIdWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading VPC: id=%d", data.Id.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading VPC",
|
||||
fmt.Sprintf("Received %s reading VPC: id=%d. Details: %s", vpcResp.Status(), data.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
data.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
data.IpRange = types.StringValue(*vpcResp.JSON200.Vpc.IpRange)
|
||||
data.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *vpcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var plan, state vpcResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
patchRequest := binarylane.PatchVpcRequest{}
|
||||
|
||||
if !plan.Name.Equal(state.Name) {
|
||||
patchRequest.Name = plan.Name.ValueStringPointer()
|
||||
}
|
||||
|
||||
if reflect.ValueOf(plan).NumField() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Updating VPC: id=%d", state.Id.ValueInt64()))
|
||||
vpcResp, err := r.bc.client.PatchVpcsVpcIdWithResponse(ctx, state.Id.ValueInt64(), patchRequest)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error updating VPC: id=%d", state.Id.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code updating VPC",
|
||||
fmt.Sprintf("Received %s updating VPC: id=%d. Details: %s", vpcResp.Status(), state.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
state.Id = types.Int64Value(*vpcResp.JSON200.Vpc.Id)
|
||||
state.Name = types.StringValue(*vpcResp.JSON200.Vpc.Name)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *vpcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data vpcResourceModel
|
||||
|
||||
// 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 VPC: id=%d", data.Id.ValueInt64()))
|
||||
vpcResp, err := r.bc.client.DeleteVpcsVpcIdWithResponse(ctx, data.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting VPC: id=%d", data.Id.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if vpcResp.StatusCode() != http.StatusNoContent {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting VPC",
|
||||
fmt.Sprintf("Received %s deleting VPC: id=%d. Details: %s", vpcResp.Status(), data.Id.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *vpcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
id, err := strconv.ParseInt(req.ID, 10, 64)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error importing VPC",
|
||||
fmt.Sprintf("Could not import VPC, unable to parse ID (should be an integer): %s ", err.Error()),
|
||||
)
|
||||
return
|
||||
}
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("id"), id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
)
|
||||
|
||||
func TestVpcResource(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-vpc"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
data "binarylane_vpc" "test" {
|
||||
depends_on = [binarylane_vpc.test]
|
||||
|
||||
id = binarylane_vpc.test.id
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
data "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc_route_entries.test.vpc_id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify binarylane_vpc resource values
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "name", "tf-test-vpc"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "ip_range", "10.240.0.0/16"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc.test", "id"),
|
||||
// Verify binarylane_vpc data source values
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc.test", "name", "tf-test-vpc"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc.test", "ip_range", "10.240.0.0/16"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_vpc.test", "id"),
|
||||
// Verify binarylane_vpc_route_entries resource values
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc_route_entries.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.destination", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.router", "10.240.0.1"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.description", "test"),
|
||||
// Verify binarylane_vpc_route_entries data source values
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_vpc_route_entries.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.0.destination", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.0.router", "10.240.0.1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_vpc_route_entries.test", "route_entries.0.description", "test"),
|
||||
),
|
||||
},
|
||||
// ImportState testing
|
||||
{
|
||||
ResourceName: "binarylane_vpc.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
{
|
||||
ResourceName: "binarylane_vpc_route_entries.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIdentifierAttribute: "vpc_id",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
resourceState := s.RootModule().Resources["binarylane_vpc_route_entries.test"]
|
||||
return resourceState.Primary.Attributes["vpc_id"], nil
|
||||
},
|
||||
},
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-vpc-renamed"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc_route_entries" "test" {
|
||||
vpc_id = binarylane_vpc.test.id
|
||||
route_entries = [
|
||||
{
|
||||
description = "test-renamed"
|
||||
destination = "0.0.0.0/0"
|
||||
router = "10.240.0.2"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify binarylane_vpc resource values updated
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "name", "tf-test-vpc-renamed"), // Updated name
|
||||
resource.TestCheckResourceAttr("binarylane_vpc.test", "ip_range", "10.240.0.0/16"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc.test", "id"),
|
||||
// Verify binarylane_vpc_route_entries resource values updated
|
||||
resource.TestCheckResourceAttrSet("binarylane_vpc_route_entries.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.destination", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.router", "10.240.0.2"), // Updated router
|
||||
resource.TestCheckResourceAttr("binarylane_vpc_route_entries.test", "route_entries.0.description", "test-renamed"), // Updated description
|
||||
),
|
||||
},
|
||||
// Delete testing automatically occurs in TestCase
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &vpcRouteEntriesDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &vpcRouteEntriesDataSource{}
|
||||
)
|
||||
|
||||
func NewVpcRouteEntriesDataSource() datasource.DataSource {
|
||||
return &vpcRouteEntriesDataSource{}
|
||||
}
|
||||
|
||||
type vpcRouteEntriesDataSource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type vpcRouteEntriesDataSourceModel struct {
|
||||
resources.VpcRouteEntriesModel
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_vpc_route_entries"
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
ds, err := convertResourceSchemaToDataSourceSchema(resources.VpcRouteEntriesResourceSchema(ctx))
|
||||
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"
|
||||
|
||||
// Overrides
|
||||
vpcId := resp.Schema.Attributes["vpc_id"]
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
Description: vpcId.GetDescription(),
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Required: true, // VPC ID is required to define the route entries
|
||||
}
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data vpcRouteEntriesDataSourceModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
vpcResp, err := d.bc.client.GetVpcsVpcIdWithResponse(ctx, data.VpcId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading VPC: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading VPC",
|
||||
fmt.Sprintf("Received %s reading VPC: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(routeEntriesDiags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.RouteEntries = routeEntries
|
||||
|
||||
// Example data value setting
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesDataSource) 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
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"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/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &vpcRouteEntriesResource{}
|
||||
_ resource.ResourceWithConfigure = &vpcRouteEntriesResource{}
|
||||
_ resource.ResourceWithImportState = &vpcRouteEntriesResource{}
|
||||
)
|
||||
|
||||
func NewVpcRouteEntriesResource() resource.Resource {
|
||||
return &vpcRouteEntriesResource{}
|
||||
}
|
||||
|
||||
type vpcRouteEntriesResource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
type vpcRouteEntriesResourceModel struct {
|
||||
resources.VpcRouteEntriesModel
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_vpc_route_entries"
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.VpcRouteEntriesResourceSchema(ctx)
|
||||
resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
vpcId := resp.Schema.Attributes["vpc_id"]
|
||||
resp.Schema.Attributes["vpc_id"] = &schema.Int64Attribute{
|
||||
Description: vpcId.GetDescription(),
|
||||
MarkdownDescription: vpcId.GetMarkdownDescription(),
|
||||
Required: true, // VPC ID is required to define the route entries
|
||||
Validators: []validator.Int64{
|
||||
int64validator.AtLeast(1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data vpcRouteEntriesResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
routeEntries := []binarylane.RouteEntryRequest{}
|
||||
diags := data.VpcRouteEntriesModel.RouteEntries.ElementsAs(ctx, &routeEntries, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
vpcResp, err := r.bc.client.PatchVpcsVpcIdWithResponse(ctx, data.VpcId.ValueInt64(), binarylane.PatchVpcRequest{
|
||||
RouteEntries: &routeEntries,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating VPC route entries: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating VPC route entries",
|
||||
fmt.Sprintf("Received %s creating VPC route entries: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.RouteEntries = routeEntriesState
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data vpcRouteEntriesResourceModel
|
||||
|
||||
// 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
|
||||
vpcResp, err := r.bc.client.GetVpcsVpcIdWithResponse(ctx, data.VpcId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading VPC route entries: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading VPC route entries",
|
||||
fmt.Sprintf("Received %s reading VPC route entries: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
routeEntries, routeEntriesDiags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(routeEntriesDiags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.RouteEntries = routeEntries
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var plan, state vpcRouteEntriesResourceModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
routeEntries := []binarylane.RouteEntryRequest{}
|
||||
diags := plan.VpcRouteEntriesModel.RouteEntries.ElementsAs(ctx, &routeEntries, true)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
vpcResp, err := r.bc.client.PatchVpcsVpcIdWithResponse(ctx, state.VpcId.ValueInt64(), binarylane.PatchVpcRequest{
|
||||
RouteEntries: &routeEntries,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error updating VPC route entries: vpc_id=%d", state.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code updating VPC route entries",
|
||||
fmt.Sprintf("Received %s updating VPC route entries: vpc_id=%d. Details: %s", vpcResp.Status(), state.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
routeEntriesState, diags := GetRouteEntriesState(ctx, vpcResp.JSON200.Vpc.RouteEntries)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
state.RouteEntries = routeEntriesState
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data vpcRouteEntriesResourceModel
|
||||
|
||||
// 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
|
||||
var routeEntries []binarylane.RouteEntryRequest
|
||||
vpcResp, err := r.bc.client.PatchVpcsVpcIdWithResponse(ctx, data.VpcId.ValueInt64(), binarylane.PatchVpcRequest{
|
||||
RouteEntries: &routeEntries,
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting VPC route entries: vpc_id=%d", data.VpcId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if vpcResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting VPC route entries",
|
||||
fmt.Sprintf("Received %s deleting VPC route entries: vpc_id=%d. Details: %s", vpcResp.Status(), data.VpcId.ValueInt64(), vpcResp.Body))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (d *vpcRouteEntriesResource) 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 GetRouteEntriesState(ctx context.Context, routeEntries *[]binarylane.RouteEntry) (basetypes.ListValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
routeEntriesValue := resources.RouteEntriesValue{}
|
||||
var routeEntriesValues []resources.RouteEntriesValue
|
||||
|
||||
if *routeEntries == nil || len(*routeEntries) == 0 {
|
||||
routeEntriesValues = []resources.RouteEntriesValue{}
|
||||
} else {
|
||||
for _, route := range *routeEntries {
|
||||
r := resources.NewRouteEntriesValueMust(routeEntriesValue.AttributeTypes(ctx), map[string]attr.Value{
|
||||
"description": types.StringValue(*route.Description),
|
||||
"destination": types.StringValue(*route.Destination),
|
||||
"router": types.StringValue(*route.Router),
|
||||
})
|
||||
routeEntriesValues = append(routeEntriesValues, r)
|
||||
}
|
||||
}
|
||||
|
||||
routeEntriesListValue, diag := types.ListValueFrom(ctx, routeEntriesValue.Type(ctx), routeEntriesValues)
|
||||
diags.Append(diag...)
|
||||
if diags.HasError() {
|
||||
return basetypes.NewListUnknown(routeEntriesValue.Type(ctx)), diags
|
||||
}
|
||||
|
||||
return routeEntriesListValue, diags
|
||||
}
|
||||
|
||||
func (r *vpcRouteEntriesResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
id, err := strconv.ParseInt(req.ID, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error importing VPC route entries",
|
||||
"Could not import VPC route entries, unexpected error (ID should be an integer): "+err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
diags := resp.State.SetAttribute(ctx, path.Root("vpc_id"), id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
@@ -0,0 +1,789 @@
|
||||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func ServerFirewallRulesResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"firewall_rules": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"action": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The action to take when there is a match on this rule.",
|
||||
MarkdownDescription: "The action to take when there is a match on this rule.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"drop",
|
||||
"accept",
|
||||
),
|
||||
},
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. \"Block access to RDP\" or \"Allow access from HQ\".",
|
||||
MarkdownDescription: "A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. \"Block access to RDP\" or \"Allow access from HQ\".",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthBetween(0, 250),
|
||||
},
|
||||
},
|
||||
"destination_addresses": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Required: true,
|
||||
Description: "The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
MarkdownDescription: "The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
Validators: []validator.List{
|
||||
listvalidator.SizeAtLeast(1),
|
||||
},
|
||||
},
|
||||
"destination_ports": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The destination ports to match for this rule. Leave null or empty to match on all ports.",
|
||||
MarkdownDescription: "The destination ports to match for this rule. Leave null or empty to match on all ports.",
|
||||
},
|
||||
"protocol": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The protocol to match for this rule.",
|
||||
MarkdownDescription: "The protocol to match for this rule.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"all",
|
||||
"icmp",
|
||||
"tcp",
|
||||
"udp",
|
||||
),
|
||||
},
|
||||
},
|
||||
"source_addresses": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Required: true,
|
||||
Description: "The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
MarkdownDescription: "The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
Validators: []validator.List{
|
||||
listvalidator.SizeAtLeast(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
CustomType: FirewallRulesType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: FirewallRulesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: true,
|
||||
Description: "A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules.",
|
||||
MarkdownDescription: "A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules.",
|
||||
},
|
||||
"server_id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the server for which firewall rules should be listed.",
|
||||
MarkdownDescription: "The ID of the server for which firewall rules should be listed.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ServerFirewallRulesModel struct {
|
||||
FirewallRules types.List `tfsdk:"firewall_rules"`
|
||||
ServerId types.Int64 `tfsdk:"server_id"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = FirewallRulesType{}
|
||||
|
||||
type FirewallRulesType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(FirewallRulesType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) String() string {
|
||||
return "FirewallRulesType"
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
actionAttribute, ok := attributes["action"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`action is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
actionVal, ok := actionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`action expected to be basetypes.StringValue, was: %T`, actionAttribute))
|
||||
}
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
destinationAddressesAttribute, ok := attributes["destination_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_addresses is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
destinationAddressesVal, ok := destinationAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_addresses expected to be basetypes.ListValue, was: %T`, destinationAddressesAttribute))
|
||||
}
|
||||
|
||||
destinationPortsAttribute, ok := attributes["destination_ports"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_ports is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
destinationPortsVal, ok := destinationPortsAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_ports expected to be basetypes.ListValue, was: %T`, destinationPortsAttribute))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
sourceAddressesAttribute, ok := attributes["source_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`source_addresses is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
sourceAddressesVal, ok := sourceAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`source_addresses expected to be basetypes.ListValue, was: %T`, sourceAddressesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return FirewallRulesValue{
|
||||
Action: actionVal,
|
||||
Description: descriptionVal,
|
||||
DestinationAddresses: destinationAddressesVal,
|
||||
DestinationPorts: destinationPortsVal,
|
||||
Protocol: protocolVal,
|
||||
SourceAddresses: sourceAddressesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewFirewallRulesValueNull() FirewallRulesValue {
|
||||
return FirewallRulesValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewFirewallRulesValueUnknown() FirewallRulesValue {
|
||||
return FirewallRulesValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewFirewallRulesValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (FirewallRulesValue, 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 FirewallRulesValue Attribute Value",
|
||||
"While creating a FirewallRulesValue value, a missing attribute value was detected. "+
|
||||
"A FirewallRulesValue 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("FirewallRulesValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid FirewallRulesValue Attribute Type",
|
||||
"While creating a FirewallRulesValue value, an invalid attribute value was detected. "+
|
||||
"A FirewallRulesValue 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("FirewallRulesValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("FirewallRulesValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra FirewallRulesValue Attribute Value",
|
||||
"While creating a FirewallRulesValue value, an extra attribute value was detected. "+
|
||||
"A FirewallRulesValue 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 FirewallRulesValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
actionAttribute, ok := attributes["action"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`action is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
actionVal, ok := actionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`action expected to be basetypes.StringValue, was: %T`, actionAttribute))
|
||||
}
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
destinationAddressesAttribute, ok := attributes["destination_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_addresses is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
destinationAddressesVal, ok := destinationAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_addresses expected to be basetypes.ListValue, was: %T`, destinationAddressesAttribute))
|
||||
}
|
||||
|
||||
destinationPortsAttribute, ok := attributes["destination_ports"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination_ports is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
destinationPortsVal, ok := destinationPortsAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination_ports expected to be basetypes.ListValue, was: %T`, destinationPortsAttribute))
|
||||
}
|
||||
|
||||
protocolAttribute, ok := attributes["protocol"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`protocol is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
protocolVal, ok := protocolAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`protocol expected to be basetypes.StringValue, was: %T`, protocolAttribute))
|
||||
}
|
||||
|
||||
sourceAddressesAttribute, ok := attributes["source_addresses"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`source_addresses is missing from object`)
|
||||
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
sourceAddressesVal, ok := sourceAddressesAttribute.(basetypes.ListValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`source_addresses expected to be basetypes.ListValue, was: %T`, sourceAddressesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewFirewallRulesValueUnknown(), diags
|
||||
}
|
||||
|
||||
return FirewallRulesValue{
|
||||
Action: actionVal,
|
||||
Description: descriptionVal,
|
||||
DestinationAddresses: destinationAddressesVal,
|
||||
DestinationPorts: destinationPortsVal,
|
||||
Protocol: protocolVal,
|
||||
SourceAddresses: sourceAddressesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewFirewallRulesValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) FirewallRulesValue {
|
||||
object, diags := NewFirewallRulesValue(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("NewFirewallRulesValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewFirewallRulesValueNull(), 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 NewFirewallRulesValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewFirewallRulesValueNull(), 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 NewFirewallRulesValueMust(FirewallRulesValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t FirewallRulesType) ValueType(ctx context.Context) attr.Value {
|
||||
return FirewallRulesValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = FirewallRulesValue{}
|
||||
|
||||
type FirewallRulesValue struct {
|
||||
Action basetypes.StringValue `tfsdk:"action"`
|
||||
Description basetypes.StringValue `tfsdk:"description"`
|
||||
DestinationAddresses basetypes.ListValue `tfsdk:"destination_addresses"`
|
||||
DestinationPorts basetypes.ListValue `tfsdk:"destination_ports"`
|
||||
Protocol basetypes.StringValue `tfsdk:"protocol"`
|
||||
SourceAddresses basetypes.ListValue `tfsdk:"source_addresses"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 6)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["action"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["destination_addresses"] = basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
}.TerraformType(ctx)
|
||||
attrTypes["destination_ports"] = basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
}.TerraformType(ctx)
|
||||
attrTypes["protocol"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["source_addresses"] = basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 6)
|
||||
|
||||
val, err = v.Action.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["action"] = val
|
||||
|
||||
val, err = v.Description.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["description"] = val
|
||||
|
||||
val, err = v.DestinationAddresses.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["destination_addresses"] = val
|
||||
|
||||
val, err = v.DestinationPorts.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["destination_ports"] = val
|
||||
|
||||
val, err = v.Protocol.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["protocol"] = val
|
||||
|
||||
val, err = v.SourceAddresses.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["source_addresses"] = 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 FirewallRulesValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) String() string {
|
||||
return "FirewallRulesValue"
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
destinationAddressesVal, d := types.ListValue(types.StringType, v.DestinationAddresses.Elements())
|
||||
|
||||
diags.Append(d...)
|
||||
|
||||
if d.HasError() {
|
||||
return types.ObjectUnknown(map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}), diags
|
||||
}
|
||||
|
||||
destinationPortsVal, d := types.ListValue(types.StringType, v.DestinationPorts.Elements())
|
||||
|
||||
diags.Append(d...)
|
||||
|
||||
if d.HasError() {
|
||||
return types.ObjectUnknown(map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}), diags
|
||||
}
|
||||
|
||||
sourceAddressesVal, d := types.ListValue(types.StringType, v.SourceAddresses.Elements())
|
||||
|
||||
diags.Append(d...)
|
||||
|
||||
if d.HasError() {
|
||||
return types.ObjectUnknown(map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}), diags
|
||||
}
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.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{
|
||||
"action": v.Action,
|
||||
"description": v.Description,
|
||||
"destination_addresses": destinationAddressesVal,
|
||||
"destination_ports": destinationPortsVal,
|
||||
"protocol": v.Protocol,
|
||||
"source_addresses": sourceAddressesVal,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(FirewallRulesValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.Action.Equal(other.Action) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Description.Equal(other.Description) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.DestinationAddresses.Equal(other.DestinationAddresses) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.DestinationPorts.Equal(other.DestinationPorts) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Protocol.Equal(other.Protocol) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.SourceAddresses.Equal(other.SourceAddresses) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) Type(ctx context.Context) attr.Type {
|
||||
return FirewallRulesType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v FirewallRulesValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"action": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"destination_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"destination_ports": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
"protocol": basetypes.StringType{},
|
||||
"source_addresses": basetypes.ListType{
|
||||
ElemType: types.StringType,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
"backups": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If true this will enable two daily backups for the server. Options.daily_backups will override this value if provided. Setting this to false has no effect.\n",
|
||||
MarkdownDescription: "If true this will enable two daily backups for the server. Options.daily_backups will override this value if provided. Setting this to false has no effect.\n",
|
||||
Description: "If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.\n",
|
||||
MarkdownDescription: "If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.\n",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
@@ -43,6 +43,12 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
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,
|
||||
Description: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.",
|
||||
MarkdownDescription: "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The slug of the selected region.",
|
||||
@@ -59,16 +65,42 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"ssh_keys": schema.ListAttribute{
|
||||
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.",
|
||||
},
|
||||
"user_data": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "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.",
|
||||
MarkdownDescription: "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.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtMost(65536),
|
||||
},
|
||||
},
|
||||
"vpc_id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Leave null to use default (public) network for the selected region.",
|
||||
MarkdownDescription: "Leave null to use default (public) network for the selected region.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ServerModel struct {
|
||||
Backups types.Bool `tfsdk:"backups"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Password types.String `tfsdk:"password"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Size types.String `tfsdk:"size"`
|
||||
Backups types.Bool `tfsdk:"backups"`
|
||||
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"`
|
||||
SshKeys types.List `tfsdk:"ssh_keys"`
|
||||
UserData types.String `tfsdk:"user_data"`
|
||||
VpcId types.Int64 `tfsdk:"vpc_id"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func VpcResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The target vpc id.",
|
||||
MarkdownDescription: "The target vpc id.",
|
||||
},
|
||||
"ip_range": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "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.",
|
||||
MarkdownDescription: "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": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "A name to help identify this VPC.",
|
||||
MarkdownDescription: "A name to help identify this VPC.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthBetween(0, 250),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type VpcModel struct {
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
IpRange types.String `tfsdk:"ip_range"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func VpcRouteEntriesResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"route_entries": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"description": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "An optional description for the route.",
|
||||
MarkdownDescription: "An optional description for the route.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtMost(250),
|
||||
},
|
||||
},
|
||||
"destination": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The destination address for this route entry. This may be in CIDR format.",
|
||||
MarkdownDescription: "The destination address for this route entry. This may be in CIDR format.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"router": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The server that will receive traffic sent to the destination property in this VPC.",
|
||||
MarkdownDescription: "The server that will receive traffic sent to the destination property in this VPC.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
CustomType: RouteEntriesType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: RouteEntriesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "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.",
|
||||
MarkdownDescription: "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.",
|
||||
},
|
||||
"vpc_id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The target vpc id.",
|
||||
MarkdownDescription: "The target vpc id.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type VpcRouteEntriesModel struct {
|
||||
RouteEntries types.List `tfsdk:"route_entries"`
|
||||
VpcId types.Int64 `tfsdk:"vpc_id"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = RouteEntriesType{}
|
||||
|
||||
type RouteEntriesType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t RouteEntriesType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(RouteEntriesType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t RouteEntriesType) String() string {
|
||||
return "RouteEntriesType"
|
||||
}
|
||||
|
||||
func (t RouteEntriesType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
destinationAttribute, ok := attributes["destination"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
destinationVal, ok := destinationAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination expected to be basetypes.StringValue, was: %T`, destinationAttribute))
|
||||
}
|
||||
|
||||
routerAttribute, ok := attributes["router"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`router is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
routerVal, ok := routerAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`router expected to be basetypes.StringValue, was: %T`, routerAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return RouteEntriesValue{
|
||||
Description: descriptionVal,
|
||||
Destination: destinationVal,
|
||||
Router: routerVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewRouteEntriesValueNull() RouteEntriesValue {
|
||||
return RouteEntriesValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouteEntriesValueUnknown() RouteEntriesValue {
|
||||
return RouteEntriesValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouteEntriesValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (RouteEntriesValue, 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 RouteEntriesValue Attribute Value",
|
||||
"While creating a RouteEntriesValue value, a missing attribute value was detected. "+
|
||||
"A RouteEntriesValue 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("RouteEntriesValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid RouteEntriesValue Attribute Type",
|
||||
"While creating a RouteEntriesValue value, an invalid attribute value was detected. "+
|
||||
"A RouteEntriesValue 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("RouteEntriesValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("RouteEntriesValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra RouteEntriesValue Attribute Value",
|
||||
"While creating a RouteEntriesValue value, an extra attribute value was detected. "+
|
||||
"A RouteEntriesValue 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 RouteEntriesValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewRouteEntriesValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return NewRouteEntriesValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
destinationAttribute, ok := attributes["destination"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`destination is missing from object`)
|
||||
|
||||
return NewRouteEntriesValueUnknown(), diags
|
||||
}
|
||||
|
||||
destinationVal, ok := destinationAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`destination expected to be basetypes.StringValue, was: %T`, destinationAttribute))
|
||||
}
|
||||
|
||||
routerAttribute, ok := attributes["router"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`router is missing from object`)
|
||||
|
||||
return NewRouteEntriesValueUnknown(), diags
|
||||
}
|
||||
|
||||
routerVal, ok := routerAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`router expected to be basetypes.StringValue, was: %T`, routerAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewRouteEntriesValueUnknown(), diags
|
||||
}
|
||||
|
||||
return RouteEntriesValue{
|
||||
Description: descriptionVal,
|
||||
Destination: destinationVal,
|
||||
Router: routerVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewRouteEntriesValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) RouteEntriesValue {
|
||||
object, diags := NewRouteEntriesValue(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("NewRouteEntriesValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t RouteEntriesType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewRouteEntriesValueNull(), 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 NewRouteEntriesValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewRouteEntriesValueNull(), 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 NewRouteEntriesValueMust(RouteEntriesValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t RouteEntriesType) ValueType(ctx context.Context) attr.Value {
|
||||
return RouteEntriesValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = RouteEntriesValue{}
|
||||
|
||||
type RouteEntriesValue struct {
|
||||
Description basetypes.StringValue `tfsdk:"description"`
|
||||
Destination basetypes.StringValue `tfsdk:"destination"`
|
||||
Router basetypes.StringValue `tfsdk:"router"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v RouteEntriesValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 3)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["destination"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["router"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 3)
|
||||
|
||||
val, err = v.Description.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["description"] = val
|
||||
|
||||
val, err = v.Destination.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["destination"] = val
|
||||
|
||||
val, err = v.Router.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["router"] = 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 RouteEntriesValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v RouteEntriesValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v RouteEntriesValue) String() string {
|
||||
return "RouteEntriesValue"
|
||||
}
|
||||
|
||||
func (v RouteEntriesValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"description": basetypes.StringType{},
|
||||
"destination": basetypes.StringType{},
|
||||
"router": 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{
|
||||
"description": v.Description,
|
||||
"destination": v.Destination,
|
||||
"router": v.Router,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v RouteEntriesValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(RouteEntriesValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.Description.Equal(other.Description) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Destination.Equal(other.Destination) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Router.Equal(other.Router) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v RouteEntriesValue) Type(ctx context.Context) attr.Type {
|
||||
return RouteEntriesType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v RouteEntriesValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"description": basetypes.StringType{},
|
||||
"destination": basetypes.StringType{},
|
||||
"router": basetypes.StringType{},
|
||||
}
|
||||
}
|
||||
+302
-31
@@ -1,34 +1,4 @@
|
||||
{
|
||||
"datasources": [
|
||||
{
|
||||
"name": "server",
|
||||
"schema": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The ID of the server to fetch."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ssh_key",
|
||||
"schema": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The ID or fingerprint of the SSH Key to fetch."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"provider": {
|
||||
"name": "binarylane"
|
||||
},
|
||||
@@ -41,7 +11,7 @@
|
||||
"name": "backups",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If true this will enable two daily backups for the server. Options.daily_backups will override this value if provided. Setting this to false has no effect.\n"
|
||||
"description": "If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -65,6 +35,13 @@
|
||||
"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": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "region",
|
||||
"string": {
|
||||
@@ -103,6 +80,42 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ssh_keys",
|
||||
"list": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"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."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "user_data",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "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.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtMost(65536)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vpc_id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "Leave null to use default (public) network for the selected region."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
@@ -113,6 +126,142 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server_firewall_rules",
|
||||
"schema": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "firewall_rules",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "required",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "action",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The action to take when there is a match on this rule.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.OneOf(\n\"drop\",\n\"accept\",\n)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "A description to assist in identifying this rule. Commonly used to record the reason for the rule or the intent behind it, e.g. \"Block access to RDP\" or \"Allow access from HQ\".",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthBetween(0, 250)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "destination_addresses",
|
||||
"list": {
|
||||
"computed_optional_required": "required",
|
||||
"element_type": {
|
||||
"string": {}
|
||||
},
|
||||
"description": "The destination addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "listvalidator.SizeAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "destination_ports",
|
||||
"list": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"element_type": {
|
||||
"string": {}
|
||||
},
|
||||
"description": "The destination ports to match for this rule. Leave null or empty to match on all ports."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "protocol",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The protocol to match for this rule.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.OneOf(\n\"all\",\n\"icmp\",\n\"tcp\",\n\"udp\",\n)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "source_addresses",
|
||||
"list": {
|
||||
"computed_optional_required": "required",
|
||||
"element_type": {
|
||||
"string": {}
|
||||
},
|
||||
"description": "The source addresses to match for this rule. Each address may be an individual IPv4 address or a range in IPv4 CIDR notation.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "listvalidator.SizeAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "A list of rules for the server. NB: that any existing rules that are not included will be removed. Submit an empty list to clear all rules."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server_id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "The ID of the server for which firewall rules should be listed."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ssh_key",
|
||||
"schema": {
|
||||
@@ -171,6 +320,128 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vpc",
|
||||
"schema": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "ip_range",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "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": "name",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "A name to help identify this VPC.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthBetween(0, 250)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "The target vpc id."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vpc_route_entries",
|
||||
"schema": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "route_entries",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "description",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "An optional description for the route.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtMost(250)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "destination",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The destination address for this route entry. This may be in CIDR format.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "router",
|
||||
"string": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The server that will receive traffic sent to the destination property in this VPC.",
|
||||
"validators": [
|
||||
{
|
||||
"custom": {
|
||||
"imports": [
|
||||
{
|
||||
"path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
}
|
||||
],
|
||||
"schema_definition": "stringvalidator.LengthAtLeast(1)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "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."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vpc_id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "The target vpc id."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "0.1"
|
||||
|
||||
+50
-27
@@ -31,7 +31,8 @@ resources:
|
||||
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.
|
||||
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
|
||||
@@ -39,7 +40,7 @@ resources:
|
||||
image:
|
||||
type: string
|
||||
description: The slug of the selected operating system.
|
||||
example: "ubuntu-24.04"
|
||||
example: 'ubuntu-24.04'
|
||||
password:
|
||||
type: string
|
||||
description: >
|
||||
@@ -53,12 +54,25 @@ resources:
|
||||
- ipv6
|
||||
- licenses
|
||||
- links
|
||||
- options
|
||||
- server
|
||||
- ssh_keys
|
||||
- vpc_id
|
||||
- user_data
|
||||
- port_blocking
|
||||
- options
|
||||
server_firewall_rules:
|
||||
create:
|
||||
path: /servers/{server_id}/actions#ChangeAdvancedFirewallRules
|
||||
method: POST
|
||||
read:
|
||||
path: /servers/{server_id}/advanced_firewall_rules
|
||||
method: GET
|
||||
update:
|
||||
path: /servers/{server_id}/actions#ChangeAdvancedFirewallRules
|
||||
method: POST
|
||||
delete:
|
||||
path: /servers/{server_id}/actions#ChangeAdvancedFirewallRules
|
||||
method: POST
|
||||
schema:
|
||||
ignores:
|
||||
- type
|
||||
- action
|
||||
ssh_key:
|
||||
create:
|
||||
path: /account/keys
|
||||
@@ -75,33 +89,42 @@ resources:
|
||||
key_id: id
|
||||
ignores:
|
||||
- ssh_key
|
||||
|
||||
data_sources:
|
||||
server:
|
||||
vpc:
|
||||
create:
|
||||
path: /vpcs
|
||||
method: POST
|
||||
read:
|
||||
path: /servers/{server_id}
|
||||
path: /vpcs/{vpc_id}
|
||||
method: GET
|
||||
update:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: PATCH
|
||||
delete:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: DELETE
|
||||
schema:
|
||||
attributes:
|
||||
aliases:
|
||||
server_id: id
|
||||
vpc_id: id
|
||||
ignores:
|
||||
- ipv6
|
||||
- licenses
|
||||
- links
|
||||
- options
|
||||
- server
|
||||
- ssh_keys
|
||||
- vpc_id
|
||||
- user_data
|
||||
- port_blocking
|
||||
ssh_key:
|
||||
- route_entries
|
||||
- vpc
|
||||
vpc_route_entries:
|
||||
create:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: PATCH
|
||||
read:
|
||||
path: /account/keys/{key_id}
|
||||
path: /vpcs/{vpc_id}
|
||||
method: GET
|
||||
update:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: PATCH
|
||||
delete:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: DELETE
|
||||
schema:
|
||||
attributes:
|
||||
aliases:
|
||||
key_id: id
|
||||
ignores:
|
||||
- ssh_key
|
||||
- name
|
||||
- vpc
|
||||
# data_sources:
|
||||
# Currently, don't need to generate data sources as they use the same schema as resources
|
||||
|
||||
Reference in New Issue
Block a user