mirror of
https://github.com/oscarhermoso/terraform-provider-binarylane
synced 2026-10-19 21:49:15 +11:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5f45788b2 | |||
| d0716f58af | |||
| 1f24f45af7 | |||
| 924e785de2 | |||
| f4c91d2f94 | |||
| f73528497f | |||
| f1b7b9853e | |||
| c8cbcd4f19 | |||
| f88bb2b1f6 | |||
| ec743c932a | |||
| 375fee8674 |
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
- package-ecosystem: 'gomod'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
@@ -5,6 +5,7 @@
|
||||
*.so
|
||||
*.dylib
|
||||
bin
|
||||
dist
|
||||
terraform-provider-binarylane
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
@@ -25,3 +26,8 @@ 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
|
||||
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
# Contributing
|
||||
|
||||
## Local development
|
||||
|
||||
### First time setup
|
||||
|
||||
Based on [this example from the terraform docs](https://developer.hashicorp.com/terraform/plugin/code-generation/workflow-example),
|
||||
|
||||
1. `go mod tidy`
|
||||
2. Run `go generate` to fetch/transform the OpenAPI spec in `internal/binarylane/openapi.json`
|
||||
3. Create or modify `~/.terraformrc` in your home directory
|
||||
|
||||
```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"
|
||||
}
|
||||
|
||||
direct {}
|
||||
}
|
||||
```
|
||||
|
||||
4. Build and test the provider
|
||||
|
||||
```sh
|
||||
go build -o bin/terraform-provider-binarylane
|
||||
go install
|
||||
cd examples/basic
|
||||
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)
|
||||
2. Update generated files
|
||||
|
||||
```sh
|
||||
go generate
|
||||
```
|
||||
|
||||
3. Scaffold any new resources and data sources
|
||||
|
||||
```sh
|
||||
tfplugingen-framework scaffold resource \
|
||||
--output-dir ./internal/provider \
|
||||
--name REPLACE_ME
|
||||
```
|
||||
|
||||
```sh
|
||||
tfplugingen-framework scaffold data-source \
|
||||
--output-dir ./internal/provider \
|
||||
--name REPLACE_ME
|
||||
```
|
||||
|
||||
4. Populate the template scaffolding:
|
||||
|
||||
#### Resource
|
||||
|
||||
Define the interfaces that the resource should implement:
|
||||
|
||||
```diff
|
||||
- var _ resource.Resource = (*exampleResource)(nil)
|
||||
+ var (
|
||||
+ _ resource.Resource = &exampleResource{}
|
||||
+ _ resource.ResourceWithConfigure = &exampleResource{}
|
||||
+ _ resource.ResourceWithImportState = &exampleResource{}
|
||||
+ )
|
||||
```
|
||||
|
||||
Add the API client to the resource:
|
||||
|
||||
```diff
|
||||
- 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 *exampleResource) 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
|
||||
+ }
|
||||
```
|
||||
|
||||
```diff
|
||||
+ 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`:
|
||||
|
||||
```diff
|
||||
return []func() resource.Resource{
|
||||
# ...
|
||||
+ 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"
|
||||
}
|
||||
```
|
||||
@@ -1,10 +1,9 @@
|
||||
# binary-lane-terraform-provider
|
||||
# terraform-provider-binarylane
|
||||
|
||||
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,56 +15,12 @@ resource "binarylane_server" "example" {
|
||||
If somehow you use this in production I would be pretty impressed.
|
||||
|
||||
- [x] Create/delete a server when runing locally
|
||||
- [ ] Publish to Terraform Registry (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
|
||||
|
||||
## Local development
|
||||
|
||||
|
||||
Based on [this example from the terraform docs](https://developer.hashicorp.com/terraform/plugin/code-generation/workflow-example),
|
||||
|
||||
1. `go mod tidy`
|
||||
2. Run `go generate` to fetch/transform the OpenAPI spec in `internal/binarylane/openapi.json`
|
||||
3. Make any changes to `provider_gen_config.yml` (see https://developer.hashicorp.com/terraform/plugin/code-generation/openapi-generator#generator-config), and run `go generate` again
|
||||
4. Scaffold any new resources and data sources
|
||||
|
||||
```sh
|
||||
tfplugingen-framework scaffold data-source \
|
||||
--name REPLACE_ME \
|
||||
--output-dir ./internal/provider
|
||||
```
|
||||
|
||||
```sh
|
||||
tfplugingen-framework scaffold data-source \
|
||||
--name REPLACE_ME \
|
||||
--output-dir ./internal/provider
|
||||
```
|
||||
|
||||
5. Create or modify `~/.terraformrc` in your home directory
|
||||
|
||||
```hcl
|
||||
provider_installation {
|
||||
|
||||
dev_overrides {
|
||||
# Example GOBIN path, will need to be replaced with your own GOBIN path. Default is $GOPATH/bin
|
||||
"hashicorp.com/oscarhermoso/binarylane" = "/home/oscarhermoso/Git/terraform-provider-binarylane/bin"
|
||||
}
|
||||
|
||||
direct {}
|
||||
}
|
||||
```
|
||||
|
||||
6. Build and test the provider
|
||||
|
||||
```sh
|
||||
go build -o bin/terraform-provider-binarylane
|
||||
go install
|
||||
cd examples/resources/binarylane_image
|
||||
terraform plan
|
||||
terraform apply
|
||||
```
|
||||
|
||||
## Notes
|
||||
## Parameters
|
||||
|
||||
Images:
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
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.
|
||||
- `user_data` (String) If provided this will be used to initialise the new server. This must be left null if the Image does not support UserData, see DistributionInfo.Features for more information.
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `image` (String) The slug of the selected operating system.
|
||||
- `region` (String) The slug of the selected region.
|
||||
- `size` (String) The slug of the selected size.
|
||||
@@ -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)
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
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
|
||||
|
||||
- `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)
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane Provider"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
|
||||
---
|
||||
|
||||
# binarylane Provider
|
||||
|
||||
+6
-442
@@ -1,6 +1,7 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_server Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Provides a Binary Lane Server resource. This can be used to create and delete servers.
|
||||
---
|
||||
@@ -22,454 +23,17 @@ 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.
|
||||
- `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.
|
||||
- `ipv6` (Boolean) If true this will enable IPv6 for this server.
|
||||
- `licenses` (Attributes List) The desired set of licenses. (see [below for nested schema](#nestedatt--licenses))
|
||||
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
|
||||
- `options1` (Attributes) This may be left null to accept all of the defaults for the selected size. (see [below for nested schema](#nestedatt--options1))
|
||||
- `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.
|
||||
- `ssh_keys` (List of String) 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.
|
||||
- `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.
|
||||
- `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
|
||||
|
||||
- `links` (Attributes) (see [below for nested schema](#nestedatt--links))
|
||||
- `server` (Attributes) (see [below for nested schema](#nestedatt--server))
|
||||
|
||||
<a id="nestedatt--licenses"></a>
|
||||
### Nested Schema for `licenses`
|
||||
|
||||
Required:
|
||||
|
||||
- `count` (Number) The number of licences.
|
||||
- `software_id` (Number) The ID of the software to license.
|
||||
|
||||
|
||||
<a id="nestedatt--options1"></a>
|
||||
### Nested Schema for `options1`
|
||||
|
||||
Optional:
|
||||
|
||||
- `daily_backups` (Number) Leave null to accept the default for the size if this is a new server or to keep the current value if this is a resize of an existing server.
|
||||
- `disk` (Number) The total storage in GB for this server.
|
||||
If specified this is the absolute value, not just the additional storage above what is included in the size.
|
||||
Leave null to accept the default for the size if this is a new server or a resize to a different base size, or to keep the current value if this a resize with the same base size but different options.
|
||||
|
||||
Valid values for sizes that do not provide a value for options.restricted_storage_values_gb:
|
||||
- must be a multiple of 5
|
||||
- > 60GB must be a multiple of 10
|
||||
- > 200GB must be a multiple of 100
|
||||
- `ipv4_addresses` (Number) The total count of IPv4 addresses for this server. If specified this is the absolute value, not just the additional IPv4 addresses above what is included in the size. Leave null to accept the default for the size if this is a new server or to keep the current value if this is a resize of an existing server. Must not exceed the size.ipv4_addresses_max value.
|
||||
- `memory` (Number) The total memory in MB for this server.
|
||||
If specified this is the absolute value, not just the additional memory above what is included in the size.
|
||||
Leave null to accept the default for the size if this is a new server or a resize to a different base size, or to keep the current value if this a resize with the same base size but different options.
|
||||
|
||||
Valid values:
|
||||
- must be a multiple of 128
|
||||
- > 2048MB must be a multiple of 1024
|
||||
- > 16384MB must be a multiple of 2048
|
||||
- > 24576MB must be a multiple of 4096
|
||||
- `monthly_backups` (Number) Leave null to accept the default for the size if this is a new server or to keep the current value if this is a resize of an existing server.
|
||||
- `offsite_backups` (Boolean) Leave null to accept the default for the size if this is a new server or to keep the current value if this is a resize of an existing server.
|
||||
- `transfer` (Number) The total transfer per month in TB for this server.
|
||||
If specified this is the absolute value, not just the additional transfer above what is included in the size.
|
||||
Leave null to accept the default for the size if this is a new server or a resize to a different base size, or to keep the current value if this a resize with the same base size but different options.
|
||||
|
||||
Valid values (when converted to GB by multiplying the value provided by 1024):
|
||||
- must be a multiple of 5GB
|
||||
- > 30GB must be a multiple of 10
|
||||
- > 200GB must be a multiple of 100
|
||||
- > 2000GB must be a multiple of 1000
|
||||
- `weekly_backups` (Number) Leave null to accept the default for the size if this is a new server or to keep the current value if this is a resize of an existing server.
|
||||
|
||||
|
||||
<a id="nestedatt--links"></a>
|
||||
### Nested Schema for `links`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `actions` (Attributes List) (see [below for nested schema](#nestedatt--links--actions))
|
||||
|
||||
<a id="nestedatt--links--actions"></a>
|
||||
### Nested Schema for `links.actions`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `href` (String)
|
||||
- `id` (Number)
|
||||
- `rel` (String)
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--server"></a>
|
||||
### Nested Schema for `server`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `advanced_features` (Attributes) The currently enabled advanced features, machine type and processor flags. (see [below for nested schema](#nestedatt--server--advanced_features))
|
||||
- `attached_backup` (Attributes) An object that provides details of any backup image currently attached to the server.. (see [below for nested schema](#nestedatt--server--attached_backup))
|
||||
- `backup_ids` (List of Number) A list of the currently existing backup image IDs for this server (if any).
|
||||
- `backup_settings` (Attributes) Detailed backup settings for the server. (see [below for nested schema](#nestedatt--server--backup_settings))
|
||||
- `cancelled_at` (String) If the server has been cancelled, this is the date and time in ISO8601 format of that cancellation.
|
||||
- `created_at` (String) The date and time in ISO8601 format of this server's initial creation.
|
||||
- `disk` (Number) The total disk in GB of this server.
|
||||
- `disks` (Attributes List) A list of the disks that are currently attached to the server. (see [below for nested schema](#nestedatt--server--disks))
|
||||
- `failover_ips` (List of String) A list of any assigned failover IP addresses for this server.
|
||||
- `features` (List of String) A list of the currently enabled features on this server.
|
||||
- `host` (Attributes) Summary information about the host of this server. (see [below for nested schema](#nestedatt--server--host))
|
||||
- `id` (Number) The ID of this server.
|
||||
- `image` (Attributes) The base image used to create this server. (see [below for nested schema](#nestedatt--server--image))
|
||||
- `kernel` (Attributes) The currently selected kernel for the server. (see [below for nested schema](#nestedatt--server--kernel))
|
||||
- `memory` (Number) The memory in MB of this server.
|
||||
- `name` (String) The hostname of this server.
|
||||
- `networks` (Attributes) A list of the networks of the server. (see [below for nested schema](#nestedatt--server--networks))
|
||||
- `next_backup_window` (Attributes) The details of the next scheduled backup, if any. (see [below for nested schema](#nestedatt--server--next_backup_window))
|
||||
- `partner_id` (Number) The server ID of the partner of this server, if one has been assigned.
|
||||
- `password_change_supported` (Boolean) If this is true the password_reset server action can be called to change a user's password. If this is false the password_reset server action will merely clear the root/administrator password allowing the password to be changed via the web console.
|
||||
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature.
|
||||
- `region` (Attributes) The region this server is allocated to. (see [below for nested schema](#nestedatt--server--region))
|
||||
- `selected_size_options` (Attributes) An object that details the selected options for the current size. (see [below for nested schema](#nestedatt--server--selected_size_options))
|
||||
- `size` (Attributes) The currently selected size for this server. (see [below for nested schema](#nestedatt--server--size))
|
||||
- `size_slug` (String) The slug of the currently selected size for this server.
|
||||
| - `status` (String) | Value | Description |
|
||||
| ------------------- | ------------------------------------------------------------------------------------ |
|
||||
| new | The server is currently in the process of building and is not yet available for use. |
|
||||
| active | The server is available for use. |
|
||||
| archive | The server is powered off due to cancellation or non payment. |
|
||||
| off | The server has been powered off, but may be powered back on. |
|
||||
- `vcpus` (Number) The number of virtual CPUs of this server.
|
||||
- `vpc_id` (Number) The VPC ID that this server is allocated to. If this value is null the server is in the default (public) network for the region.
|
||||
|
||||
<a id="nestedatt--server--advanced_features"></a>
|
||||
### Nested Schema for `server.advanced_features`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `enabled_advanced_features` (List of String) A list of the currently enabled advanced features for this server.
|
||||
- `machine_type` (String) The machine_type (corresponding to a KVM version) used for this server.
|
||||
A null value indicates automatic selection of the best KVM machine type supported by the host node.
|
||||
- `processor_model` (Number) The ID of the processor model (and therefore CPU flags) available for this server.
|
||||
A null value indicates automatic selection of the best processor model supported by the host node.
|
||||
This does not change the physical CPU, only the CPU flags available to the operating system.
|
||||
- `video_device` (String) Video Device
|
||||
|
||||
| Value | Description |
|
||||
| ------------ | ------------------------------------- |
|
||||
| cirrus-logic | Cirrus Logic GD5446 |
|
||||
| standard | Standard VGA with VESA 2.0 extensions |
|
||||
| virtio | Virtio VGA (800x600) |
|
||||
| virtio-wide | Virtio VGA (1600x900) |
|
||||
|
||||
|
||||
<a id="nestedatt--server--attached_backup"></a>
|
||||
### Nested Schema for `server.attached_backup`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `attached_at` (String) The date and time in ISO8601 format when this image was attached to the server.
|
||||
- `attachment_expires` (String) The date and time in ISO8601 format when the backup will be automatically detached unless it is manually detached earlier.
|
||||
- `disk_identifiers` (List of String) A list of the operating specific disk identifiers for the attached backup disks.
|
||||
- `id` (Number) The ID of the backup image.
|
||||
|
||||
|
||||
<a id="nestedatt--server--backup_settings"></a>
|
||||
### Nested Schema for `server.backup_settings`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `backup_day_of_month` (Number) If monthly backups are enabled the day of the month the monthly backup will occur.
|
||||
- `backup_day_of_week` (Number) If weekly backups are enabled the day of the week that the weekly backup will occur. Sunday is day 0.
|
||||
- `backup_hour_of_day` (Number) The hour of the day that backups will be scheduled. This is an approximate value.
|
||||
- `offsite_backup_settings` (Attributes) If offsite backups are enabled this details how they are stored and managed. (see [below for nested schema](#nestedatt--server--backup_settings--offsite_backup_settings))
|
||||
|
||||
<a id="nestedatt--server--backup_settings--offsite_backup_settings"></a>
|
||||
### Nested Schema for `server.backup_settings.offsite_backup_settings`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `manage_offsite_copies` (Boolean) This only has effect if a custom offsite location is being used: the internal offsite backup location always manages copies.
|
||||
If this is true old offsite backups will be removed once the replacement upload is complete.
|
||||
If this is false backups must be removed from the Amazon S3 bucket manually. Amazon will charge your S3 account at their standard rate for every backup stored.
|
||||
- `offsite_backup_location` (String) If a custom backup location is used, this is the provided location.
|
||||
- `use_custom_backup_location` (Boolean) If this is true a custom backup location will be used. If false our internally managed offsite backup location be used.
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--server--disks"></a>
|
||||
### Nested Schema for `server.disks`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String) A description of this disk.
|
||||
- `id` (Number) The ID of this disk.
|
||||
- `primary` (Boolean) A primary disk is treated differently from other disks.
|
||||
- `size_gigabytes` (Number) The size of the disk in GB.
|
||||
|
||||
|
||||
<a id="nestedatt--server--host"></a>
|
||||
### Nested Schema for `server.host`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `display_name` (String) The name for this host.
|
||||
- `status_page` (String) This is the URL of the status page of the host. This will normally only be set if the host is under maintenance.
|
||||
- `uptime_ms` (Number) The current uptime in milliseconds of this host.
|
||||
|
||||
|
||||
<a id="nestedatt--server--image"></a>
|
||||
### Nested Schema for `server.image`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `backup_info` (Attributes) If this image is a backup, this object will provide further information. (see [below for nested schema](#nestedatt--server--image--backup_info))
|
||||
- `created_at` (String) If this is a backup image this is the date and time in ISO8601 format when the image was created.
|
||||
- `description` (String) A description that may provide further details or warnings about the image.
|
||||
- `distribution` (String) If this is an operating system image, this is the name of the distribution. If this is a backup image, this is the name of the distribution the server is using.
|
||||
- `distribution_info` (Attributes) This object may provide further information about the distribution. (see [below for nested schema](#nestedatt--server--image--distribution_info))
|
||||
- `distribution_surcharges` (Attributes) If this is not null the use of this image may incur surcharges above the base cost of the server. All costs are in AU$. (see [below for nested schema](#nestedatt--server--image--distribution_surcharges))
|
||||
- `error_message` (String) If the image creation failed this may provide further information.
|
||||
- `full_name` (String) If this is an operating system image, this is the name and version of the distribution. If this is a backup image, this is the server hostname and label of the backup if it exists, otherwise it is the server hostname and UTC timestamp of the creation of the image.
|
||||
- `id` (Number) The ID of this image.
|
||||
- `min_disk_size` (Number) For a distribution image this is the minimum disk size in GB required to install the operating system. For a backup image this is the minimum total disk size in GB required to restore the backup.
|
||||
- `min_memory_megabytes` (Number) This is minimum memory in MB necessary to support this operating system (or the base operating system for a backup image).
|
||||
- `name` (String) If this is an operating system image, this is the name of the operating system version. If this is a backup image, this is the label of the backup if it exists, otherwise it is the UTC timestamp of the creation of the image.
|
||||
- `public` (Boolean) A public image is available to all users. A private image is available only to the account that created the image.
|
||||
- `regions` (List of String) The slugs of the regions where the image is available for use.
|
||||
- `size_gigabytes` (Number) For a distribution image this is the disk size used in GB by the operating system on initial install. For a backup image this is the size of the compressed backup image in GB.
|
||||
- `slug` (String) If this is an operating system image this is a slug which may be used as an alternative to the ID as a reference.
|
||||
| - `status` (String) | Value | Description |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| NEW | The image is new. |
|
||||
| available | The image is available for use. |
|
||||
| pending | The image is pending and is not yet available for use. |
|
||||
| deleted | The image has been deleted and is no longer available for use. |
|
||||
| - `type` (String) | Value | Description |
|
||||
| ----- | ----------- |
|
||||
| custom | An image uploaded by a user. |
|
||||
| snapshot | A snapshot. Snapshot creation is not currently supported so only distribution images will have this value. |
|
||||
| backup | A backup of a server. |
|
||||
|
||||
<a id="nestedatt--server--image--backup_info"></a>
|
||||
### Nested Schema for `server.image.backup_info`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `backup_disks` (Attributes List) A list of the individual disks that make up this backup. (see [below for nested schema](#nestedatt--server--image--backup_info--backup_disks))
|
||||
- `iso` (Boolean) If this is true the backup is an ISO image and cannot be restored. ISO images may only be attached for use as a boot disk or an additional disk.
|
||||
- `locked` (Boolean) If this is true the backup is locked and cannot be replaced.
|
||||
- `offsite` (Boolean) If this is true, an attempt to create an offsite copy was made. This does not mean that the offsite copy attempt was successful or that the copy still exists.
|
||||
- `server_id` (Number) The server ID that was used to create this backup.
|
||||
| - `type` (String) | Value | Description |
|
||||
| ----------------- | ---------------------------------------------------------------------------------- |
|
||||
| daily | A backup which is scheduled to be taken each day. |
|
||||
| weekly | A backup which is scheduled to be taken each week. |
|
||||
| monthly | A backup which is scheduled to be taken each month. |
|
||||
| temporary | A backup which is created on demand and only retained for a maximum of seven days. |
|
||||
|
||||
<a id="nestedatt--server--image--backup_info--backup_disks"></a>
|
||||
### Nested Schema for `server.image.backup_info.backup_disks`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String) A description of this disk.
|
||||
- `id` (Number) The ID of this backup disk.
|
||||
- `min_disk_size` (Number) This is the minimum disk size in GB required to restore this disk image.
|
||||
- `size_gigabytes` (Number) This is the compressed size of the disk image in GB.
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--server--image--distribution_info"></a>
|
||||
### Nested Schema for `server.image.distribution_info`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `features` (List of String) Features supported by this distribution.
|
||||
- `image_id` (Number) If this is a backup image, the operating system ID of the server at the time the backup was created.
|
||||
- `password_recovery` (String) Supported methods of password recovery.
|
||||
|
||||
| Value | Description |
|
||||
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| manual | Password must be reset manually using the recovery console and rescue disk. |
|
||||
| offline-clear | Password can be cleared for the admin/root user only. New password needs to be provided on login via the console (Requires restart). |
|
||||
| offline-change | Password can be reset and new credentials sent (Requires restart). |
|
||||
| online-change | Password may be reset without requiring a reboot via installed QEMU Guest Agent. |
|
||||
- `remote_access_user` (String) User name to use when connecting via remote access (RDP or SSH).
|
||||
|
||||
|
||||
<a id="nestedatt--server--image--distribution_surcharges"></a>
|
||||
### Nested Schema for `server.image.distribution_surcharges`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `surcharge_base_cost` (Number) The additional cost for using this operating system as the base image for a size.
|
||||
- `surcharge_min_vcpu` (Number) The minimum vcpu count for surcharge calculations.
|
||||
- `surcharge_per_memory_max_megabytes` (Number) The maximum memory in MB that counts towards the surcharge_per_memory_mb. Any memory above this cap does not attract the surcharge.
|
||||
- `surcharge_per_memory_megabyte` (Number) The additional cost per MB for using this operating system as the base image for a size.
|
||||
- `surcharge_per_vcpu` (Number) The additional cost per vcpu for using this operating system as the base image for a size.
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--server--kernel"></a>
|
||||
### Nested Schema for `server.kernel`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `id` (Number) The ID of this kernel.
|
||||
- `name` (String) This name of this kernel.
|
||||
- `version` (String) The version (if any) of this kernel.
|
||||
|
||||
|
||||
<a id="nestedatt--server--networks"></a>
|
||||
### Nested Schema for `server.networks`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `ipv6_reverse_nameservers` (List of String) Any configured IPv6 reverse nameservers for this server. Please see our documentation for how this interacts with IPv6 nameserver settings at the account level.
|
||||
- `port_blocking` (Boolean) Whether the default port blocking is enabled for this server.
|
||||
- `recent_ddos` (Boolean) If this is true this server has been the target of a recent DDOS attack. An email will have been sent to your email address when the DDOS was detected (and if it has ended, when it ended) with more details.
|
||||
- `separate_private_network_interface` (Boolean) Whether a separate private network interface is provided for the server's VPC traffic.
|
||||
- `source_and_destination_check` (Boolean) If enabled, this server is only able to send and receive data packets directly addressed to an IP address assigned to this server.
|
||||
- `v4` (Attributes List) A list of the IPv4 networks for this server. (see [below for nested schema](#nestedatt--server--networks--v4))
|
||||
- `v6` (Attributes List) A list of the IPv6 networks for this server. (see [below for nested schema](#nestedatt--server--networks--v6))
|
||||
|
||||
<a id="nestedatt--server--networks--v4"></a>
|
||||
### Nested Schema for `server.networks.v4`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `gateway` (String) The gateway for this network.
|
||||
- `ip_address` (String) The IP address for this network.
|
||||
- `nat_target` (String) If this is not null this property is the private IP address which receives packets from this network.
|
||||
- `netmask` (String)
|
||||
- `reverse_name` (String) The reverse name (if any) for this network.
|
||||
| - `type` (String) | Value | Description |
|
||||
| ----------------- | -------------------------------------------- |
|
||||
| private | A private (non internet accessible) network. |
|
||||
| public | A public (internet accessible) network. |
|
||||
|
||||
|
||||
<a id="nestedatt--server--networks--v6"></a>
|
||||
### Nested Schema for `server.networks.v6`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `gateway` (String) The gateway for this network.
|
||||
- `ip_address` (String) The IP address for this network.
|
||||
- `nat_target` (String) If this is not null this property is the private IP address which receives packets from this network.
|
||||
- `netmask` (String)
|
||||
- `reverse_name` (String) The reverse name (if any) for this network.
|
||||
| - `type` (String) | Value | Description |
|
||||
| ----------------- | -------------------------------------------- |
|
||||
| private | A private (non internet accessible) network. |
|
||||
| public | A public (internet accessible) network. |
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--server--next_backup_window"></a>
|
||||
### Nested Schema for `server.next_backup_window`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `end` (String) The expected latest date and time in ISO8601 format of the next scheduled backup.
|
||||
This is not a guarantee that the backup will have started or completed by this date and time.
|
||||
- `start` (String) The approximate earliest start date and time in ISO8601 format of the next scheduled backup.
|
||||
|
||||
|
||||
<a id="nestedatt--server--region"></a>
|
||||
### Nested Schema for `server.region`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `available` (Boolean) Whether this region is available for the allocation of new resources.
|
||||
- `features` (List of String) A list of features available for resources in this region.
|
||||
- `name` (String) The name of this region.
|
||||
- `name_servers` (List of String) A list of nameservers available for resources in this region.
|
||||
- `sizes` (List of String) The slugs of the sizes available in this region.
|
||||
- `slug` (String) The unique slug for this region.
|
||||
|
||||
|
||||
<a id="nestedatt--server--selected_size_options"></a>
|
||||
### Nested Schema for `server.selected_size_options`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `daily_backups` (Number) The number of retained daily backups. e.g. if this is '2' we will store two daily backups, so each daily backup will be retained for two days before being overwritten.
|
||||
- `disk` (Number) The total storage in GB for this server.
|
||||
- `ipv4_addresses` (Number) The total count of IPv4 addresses for this server.
|
||||
- `memory` (Number) The total memory in MB for this server.
|
||||
- `monthly_backups` (Number) The number of retained monthly backups. e.g. if this is '3' we will store three monthly backups, so each monthly backup will be retained for three months before being overwritten.
|
||||
- `offsite_backups` (Boolean) If this is true any daily, weekly or monthly backups will be duplicated to an off-site location.
|
||||
- `transfer` (Number) The total transfer per month in TB for this server.
|
||||
- `weekly_backups` (Number) The number of retained weekly backups. e.g. if this is '1' we will store one weekly backup, so that weekly backup will be retained for one week before being overwritten.
|
||||
|
||||
|
||||
<a id="nestedatt--server--size"></a>
|
||||
### Nested Schema for `server.size`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `available` (Boolean) If this is false the size is not available for new servers.
|
||||
- `cpu_description` (String) A description of the CPU provided in this size.
|
||||
- `description` (String) A description of this size.
|
||||
- `disk` (Number) The included storage for this size in GB.
|
||||
- `excess_transfer_cost_per_gigabyte` (Number) The excess charged for any transfer above the included data transfer in AU$ per GB.
|
||||
- `memory` (Number) The included memory for this size in MB.
|
||||
- `options2` (Attributes) Available add-ons (optional features not included in the base price) for the size. All costs are in AU$ per month (pro-rated). (see [below for nested schema](#nestedatt--server--size--options2))
|
||||
- `price_hourly` (Number) Hourly price in AU$.
|
||||
- `price_monthly` (Number) Monthly Price in AU$.
|
||||
- `regions` (List of String) A list of region slugs where this size is available regardless of stock.
|
||||
If this a response to a query that included a selected operating system this response will only include regions where that operating system is available on this size,
|
||||
otherwise not all regions listed will support all operating systems on this size.
|
||||
- `regions_out_of_stock` (List of String) A list of region slugs where the size is normally available but is currently not available due to lack of stock.
|
||||
- `size_type` (Attributes) The type of this size, generally used to differentiate sizes optimized for different usages. (see [below for nested schema](#nestedatt--server--size--size_type))
|
||||
- `slug` (String) The slug of this size.
|
||||
- `storage_description` (String) A description of the storage provided in this size.
|
||||
- `transfer` (Number) The included data transfer for this size in TB.
|
||||
- `vcpu_units` (String) This is the unit that the vcpus field counts, e.g. "core" or "thread".
|
||||
- `vcpus` (Number) The count of virtual CPUs for this size. See vcpu_units for a description of how each virtual CPU maps to the underlying hardware.
|
||||
|
||||
<a id="nestedatt--server--size--options2"></a>
|
||||
### Nested Schema for `server.size.options2`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `backups_cost_per_backup_per_gigabyte` (Number) The cost per GB of storage of each selected backup. See the API support document for how to calculate the final cost of backups based on the options selected.
|
||||
- `daily_backups` (Number) The number of daily backups included in the base size cost.
|
||||
- `discount_for_no_public_ipv4` (Number) This is the discount (if any) that is applied if no public IPv4 addresses are selected.
|
||||
- `disk_cost_per_additional_gigabyte` (Number) The additional cost per GB per month for additional storage space.
|
||||
- `disk_max` (Number) The maximum storage in GB permitted on this size.
|
||||
- `disk_min` (Number) The minimum storage in GB permitted on this size.
|
||||
- `ipv4_addresses_cost_per_address` (Number) The additional cost per public IPv4 address per month for additional IPv4 addresses.
|
||||
- `ipv4_addresses_max` (Number) The maximum number of IPv4 addresses permitted on this size.
|
||||
- `memory_cost_per_additional_megabyte` (Number) The additional cost per MB per month for additional memory.
|
||||
- `memory_max` (Number) The maximum memory in MB permitted on this size.
|
||||
- `monthly_backups` (Number) The number of monthly backups included in the base size cost.
|
||||
- `offsite_backup_frequency_cost` (Attributes) The additional cost per GB of storage for enabling offsite backups based on highest frequency of backups currently enabled. All costs are in AU$. (see [below for nested schema](#nestedatt--server--size--options2--offsite_backup_frequency_cost))
|
||||
- `offsite_backups_cost_per_gigabyte` (Number) The additional cost per GB of storage for enabling offsite backups. See the API support document for how to calculate the final cost of backups based on the options selected.
|
||||
- `restricted_disk_values` (List of Number) If this is null the normal valid values in the documentation for SizeOptionsRequest are used, otherwise only these values (in GB) are permitted.
|
||||
- `transfer_cost_per_additional_gigabyte` (Number) The additional cost per GB per month for additional included transfer.
|
||||
- `transfer_max` (Number) The maximum transfer in TB permitted for this size.
|
||||
- `weekly_backups` (Number) The number of weekly backups included in the base size cost.
|
||||
|
||||
<a id="nestedatt--server--size--options2--offsite_backup_frequency_cost"></a>
|
||||
### Nested Schema for `server.size.options2.offsite_backup_frequency_cost`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `daily_per_gigabyte` (Number) The additional cost per GB per month for enabling daily offsite backups. Only the highest value of the daily, weekly and monthly is applied. See the API support document for how to calculate the final cost of backups based on the options selected.
|
||||
- `monthly_per_gigabyte` (Number) The additional cost per GB per month for enabling monthly offsite backups. Only the highest value of the daily, weekly and monthly is applied. See the API support document for how to calculate the final cost of backups based on the options selected.
|
||||
- `weekly_per_gigabyte` (Number) The additional cost per GB per month for enabling weekly offsite backups. Only the highest value of the daily, weekly and monthly is applied. See the API support document for how to calculate the final cost of backups based on the options selected.
|
||||
|
||||
|
||||
|
||||
<a id="nestedatt--server--size--size_type"></a>
|
||||
### Nested Schema for `server.size.size_type`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String) The description of this size type, generally used to indicate the intended use case for sizes of this type.
|
||||
- `name` (String) The name of this size type.
|
||||
- `slug` (String) The slug of this size type.
|
||||
- `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,39 @@
|
||||
---
|
||||
# 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))
|
||||
|
||||
### Optional
|
||||
|
||||
- `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,26 @@
|
||||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "binarylane_ssh_key Resource - terraform-provider-binarylane"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
TODO
|
||||
---
|
||||
|
||||
# binarylane_ssh_key (Resource)
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `name` (String) A name to help you identify the key.
|
||||
- `public_key` (String) The public key in OpenSSH "authorized_keys" format.
|
||||
|
||||
### 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.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
# 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
|
||||
|
||||
- `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.
|
||||
@@ -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.
|
||||
@@ -1,19 +1,20 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "hashicorp.com/oscarhermoso/binarylane"
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {
|
||||
# api_url = "" # Defaults to "https://api.binarylane.com"
|
||||
# api_token = "" # Recommend setting with environment variable BINARYLANE_API_TOKEN
|
||||
}
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "example"
|
||||
name = "tf-example"
|
||||
region = "per" # or "syd", "mel", "bne", "sin"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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,126 @@
|
||||
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([ # agents
|
||||
for _, agent in binarylane_server.agent : agent.private_ipv4_addresses
|
||||
])
|
||||
destination_addresses = flatten([ # server_ips
|
||||
for _, server in binarylane_server.server : server.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
|
||||
}
|
||||
@@ -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
|
||||
Generated
+24
@@ -0,0 +1,24 @@
|
||||
# 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",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# examples/ssh
|
||||
|
||||
## Generate SSH key pair
|
||||
|
||||
https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
|
||||
|
||||
## Testing
|
||||
|
||||
```sh
|
||||
ssh-keygen -t ed25519 -C "test@company.internal" \
|
||||
-f ./id_ed25519 -N ""
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal
|
||||
@@ -0,0 +1,27 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
binarylane = {
|
||||
source = "oscarhermoso/binarylane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "binarylane" {}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
resource "binarylane_server" "example" {
|
||||
name = "tf-example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8,3 +8,4 @@ package main
|
||||
//go:generate mkdir -p internal/resources internal/data_sources
|
||||
//go:generate go run github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework generate resources --input=./provider_code_spec.json --output=./internal/resources --package=resources
|
||||
//go:generate go run github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework generate data-sources --input=./provider_code_spec.json --output=./internal/data_sources --package=data_sources
|
||||
//go:generate tfplugindocs generate
|
||||
|
||||
@@ -98,17 +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/appengine v1.6.8 // 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/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
|
||||
)
|
||||
|
||||
@@ -317,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=
|
||||
@@ -333,8 +333,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.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=
|
||||
@@ -393,10 +393,10 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-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/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=
|
||||
@@ -405,8 +405,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.34.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=
|
||||
|
||||
@@ -101,23 +101,15 @@ type ClientInterface interface {
|
||||
PostAccountKeys(ctx context.Context, body PostAccountKeysJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// DeleteAccountKeysKeyId request
|
||||
DeleteAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
DeleteAccountKeysKeyId(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// GetAccountKeysKeyId request
|
||||
GetAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
GetAccountKeysKeyId(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// PutAccountKeysKeyIdWithBody request with any body
|
||||
PutAccountKeysKeyIdWithBody(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
PutAccountKeysKeyIdWithBody(ctx context.Context, keyId int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
PutAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
PutAccountKeysKeyId(ctx context.Context, keyId int, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// GetActions request
|
||||
GetActions(ctx context.Context, params *GetActionsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
@@ -623,9 +615,7 @@ func (c *Client) PostAccountKeys(ctx context.Context, body PostAccountKeysJSONRe
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) DeleteAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
func (c *Client) DeleteAccountKeysKeyId(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewDeleteAccountKeysKeyIdRequest(c.Server, keyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -637,9 +627,7 @@ func (c *Client) DeleteAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) GetAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
func (c *Client) GetAccountKeysKeyId(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewGetAccountKeysKeyIdRequest(c.Server, keyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -651,9 +639,7 @@ func (c *Client) GetAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) PutAccountKeysKeyIdWithBody(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
func (c *Client) PutAccountKeysKeyIdWithBody(ctx context.Context, keyId int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewPutAccountKeysKeyIdRequestWithBody(c.Server, keyId, contentType, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -665,9 +651,7 @@ func (c *Client) PutAccountKeysKeyIdWithBody(ctx context.Context, keyId struct {
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) PutAccountKeysKeyId(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
func (c *Client) PutAccountKeysKeyId(ctx context.Context, keyId int, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewPutAccountKeysKeyIdRequest(c.Server, keyId, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2856,9 +2840,7 @@ func NewPostAccountKeysRequestWithBody(server string, contentType string, body i
|
||||
}
|
||||
|
||||
// NewDeleteAccountKeysKeyIdRequest generates requests for DeleteAccountKeysKeyId
|
||||
func NewDeleteAccountKeysKeyIdRequest(server string, keyId struct {
|
||||
union json.RawMessage
|
||||
}) (*http.Request, error) {
|
||||
func NewDeleteAccountKeysKeyIdRequest(server string, keyId int) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
var pathParam0 string
|
||||
@@ -2892,9 +2874,7 @@ func NewDeleteAccountKeysKeyIdRequest(server string, keyId struct {
|
||||
}
|
||||
|
||||
// NewGetAccountKeysKeyIdRequest generates requests for GetAccountKeysKeyId
|
||||
func NewGetAccountKeysKeyIdRequest(server string, keyId struct {
|
||||
union json.RawMessage
|
||||
}) (*http.Request, error) {
|
||||
func NewGetAccountKeysKeyIdRequest(server string, keyId int) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
var pathParam0 string
|
||||
@@ -2928,9 +2908,7 @@ func NewGetAccountKeysKeyIdRequest(server string, keyId struct {
|
||||
}
|
||||
|
||||
// NewPutAccountKeysKeyIdRequest calls the generic PutAccountKeysKeyId builder with application/json body
|
||||
func NewPutAccountKeysKeyIdRequest(server string, keyId struct {
|
||||
union json.RawMessage
|
||||
}, body PutAccountKeysKeyIdJSONRequestBody) (*http.Request, error) {
|
||||
func NewPutAccountKeysKeyIdRequest(server string, keyId int, body PutAccountKeysKeyIdJSONRequestBody) (*http.Request, error) {
|
||||
var bodyReader io.Reader
|
||||
buf, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
@@ -2941,9 +2919,7 @@ func NewPutAccountKeysKeyIdRequest(server string, keyId struct {
|
||||
}
|
||||
|
||||
// NewPutAccountKeysKeyIdRequestWithBody generates requests for PutAccountKeysKeyId with any type of body
|
||||
func NewPutAccountKeysKeyIdRequestWithBody(server string, keyId struct {
|
||||
union json.RawMessage
|
||||
}, contentType string, body io.Reader) (*http.Request, error) {
|
||||
func NewPutAccountKeysKeyIdRequestWithBody(server string, keyId int, contentType string, body io.Reader) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
var pathParam0 string
|
||||
@@ -8539,23 +8515,15 @@ type ClientWithResponsesInterface interface {
|
||||
PostAccountKeysWithResponse(ctx context.Context, body PostAccountKeysJSONRequestBody, reqEditors ...RequestEditorFn) (*PostAccountKeysResponse, error)
|
||||
|
||||
// DeleteAccountKeysKeyIdWithResponse request
|
||||
DeleteAccountKeysKeyIdWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*DeleteAccountKeysKeyIdResponse, error)
|
||||
DeleteAccountKeysKeyIdWithResponse(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*DeleteAccountKeysKeyIdResponse, error)
|
||||
|
||||
// GetAccountKeysKeyIdWithResponse request
|
||||
GetAccountKeysKeyIdWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*GetAccountKeysKeyIdResponse, error)
|
||||
GetAccountKeysKeyIdWithResponse(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*GetAccountKeysKeyIdResponse, error)
|
||||
|
||||
// PutAccountKeysKeyIdWithBodyWithResponse request with any body
|
||||
PutAccountKeysKeyIdWithBodyWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error)
|
||||
PutAccountKeysKeyIdWithBodyWithResponse(ctx context.Context, keyId int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error)
|
||||
|
||||
PutAccountKeysKeyIdWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error)
|
||||
PutAccountKeysKeyIdWithResponse(ctx context.Context, keyId int, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error)
|
||||
|
||||
// GetActionsWithResponse request
|
||||
GetActionsWithResponse(ctx context.Context, params *GetActionsParams, reqEditors ...RequestEditorFn) (*GetActionsResponse, error)
|
||||
@@ -11819,9 +11787,7 @@ func (c *ClientWithResponses) PostAccountKeysWithResponse(ctx context.Context, b
|
||||
}
|
||||
|
||||
// DeleteAccountKeysKeyIdWithResponse request returning *DeleteAccountKeysKeyIdResponse
|
||||
func (c *ClientWithResponses) DeleteAccountKeysKeyIdWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*DeleteAccountKeysKeyIdResponse, error) {
|
||||
func (c *ClientWithResponses) DeleteAccountKeysKeyIdWithResponse(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*DeleteAccountKeysKeyIdResponse, error) {
|
||||
rsp, err := c.DeleteAccountKeysKeyId(ctx, keyId, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -11830,9 +11796,7 @@ func (c *ClientWithResponses) DeleteAccountKeysKeyIdWithResponse(ctx context.Con
|
||||
}
|
||||
|
||||
// GetAccountKeysKeyIdWithResponse request returning *GetAccountKeysKeyIdResponse
|
||||
func (c *ClientWithResponses) GetAccountKeysKeyIdWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, reqEditors ...RequestEditorFn) (*GetAccountKeysKeyIdResponse, error) {
|
||||
func (c *ClientWithResponses) GetAccountKeysKeyIdWithResponse(ctx context.Context, keyId int, reqEditors ...RequestEditorFn) (*GetAccountKeysKeyIdResponse, error) {
|
||||
rsp, err := c.GetAccountKeysKeyId(ctx, keyId, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -11841,9 +11805,7 @@ func (c *ClientWithResponses) GetAccountKeysKeyIdWithResponse(ctx context.Contex
|
||||
}
|
||||
|
||||
// PutAccountKeysKeyIdWithBodyWithResponse request with arbitrary body returning *PutAccountKeysKeyIdResponse
|
||||
func (c *ClientWithResponses) PutAccountKeysKeyIdWithBodyWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error) {
|
||||
func (c *ClientWithResponses) PutAccountKeysKeyIdWithBodyWithResponse(ctx context.Context, keyId int, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error) {
|
||||
rsp, err := c.PutAccountKeysKeyIdWithBody(ctx, keyId, contentType, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -11851,9 +11813,7 @@ func (c *ClientWithResponses) PutAccountKeysKeyIdWithBodyWithResponse(ctx contex
|
||||
return ParsePutAccountKeysKeyIdResponse(rsp)
|
||||
}
|
||||
|
||||
func (c *ClientWithResponses) PutAccountKeysKeyIdWithResponse(ctx context.Context, keyId struct {
|
||||
union json.RawMessage
|
||||
}, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error) {
|
||||
func (c *ClientWithResponses) PutAccountKeysKeyIdWithResponse(ctx context.Context, keyId int, body PutAccountKeysKeyIdJSONRequestBody, reqEditors ...RequestEditorFn) (*PutAccountKeysKeyIdResponse, error) {
|
||||
rsp, err := c.PutAccountKeysKeyId(ctx, keyId, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -4,11 +4,33 @@ 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
|
||||
# Terraform can't hanlde oneOf types, so we need to remove them and add a type: "string"
|
||||
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 '.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
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1594,15 +1594,8 @@
|
||||
"description": "The ID or fingerprint of the SSH Key to fetch.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"example": 5
|
||||
"example": 5,
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1652,15 +1645,8 @@
|
||||
"description": "The ID or fingerprint of the SSH Key to update.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"example": 5
|
||||
"example": 5,
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1733,15 +1719,8 @@
|
||||
"description": "The ID or fingerprint of the SSH Key to delete.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"example": 5
|
||||
"example": 5,
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -8832,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,
|
||||
@@ -8841,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",
|
||||
@@ -8850,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": [
|
||||
@@ -8859,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": [
|
||||
@@ -8868,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,
|
||||
@@ -8876,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12786,18 +12783,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
|
||||
@@ -2349,13 +2349,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.
|
||||
@@ -3142,27 +3142,9 @@ type GetAccountKeysParams struct {
|
||||
// PostAccountKeysJSONBody defines parameters for PostAccountKeys.
|
||||
type PostAccountKeysJSONBody = SshKeyRequest
|
||||
|
||||
// DeleteAccountKeysKeyIdParamsKeyId0 defines parameters for DeleteAccountKeysKeyId.
|
||||
type DeleteAccountKeysKeyIdParamsKeyId0 = int
|
||||
|
||||
// DeleteAccountKeysKeyIdParamsKeyId1 defines parameters for DeleteAccountKeysKeyId.
|
||||
type DeleteAccountKeysKeyIdParamsKeyId1 = string
|
||||
|
||||
// GetAccountKeysKeyIdParamsKeyId0 defines parameters for GetAccountKeysKeyId.
|
||||
type GetAccountKeysKeyIdParamsKeyId0 = int
|
||||
|
||||
// GetAccountKeysKeyIdParamsKeyId1 defines parameters for GetAccountKeysKeyId.
|
||||
type GetAccountKeysKeyIdParamsKeyId1 = string
|
||||
|
||||
// PutAccountKeysKeyIdJSONBody defines parameters for PutAccountKeysKeyId.
|
||||
type PutAccountKeysKeyIdJSONBody = UpdateSshKeyRequest
|
||||
|
||||
// PutAccountKeysKeyIdParamsKeyId0 defines parameters for PutAccountKeysKeyId.
|
||||
type PutAccountKeysKeyIdParamsKeyId0 = int
|
||||
|
||||
// PutAccountKeysKeyIdParamsKeyId1 defines parameters for PutAccountKeysKeyId.
|
||||
type PutAccountKeysKeyIdParamsKeyId1 = string
|
||||
|
||||
// GetActionsParams defines parameters for GetActions.
|
||||
type GetActionsParams struct {
|
||||
// Page The selected page. Page numbering starts at 1
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -141,11 +141,21 @@ func (p *binarylaneProvider) Metadata(_ context.Context, _ provider.MetadataRequ
|
||||
}
|
||||
|
||||
func (p *binarylaneProvider) DataSources(_ context.Context) []func() datasource.DataSource {
|
||||
return []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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
||||
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
||||
)
|
||||
@@ -13,37 +8,18 @@ import (
|
||||
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")()),
|
||||
}
|
||||
)
|
||||
|
||||
const TestAccNamePrefix = "tf-acc"
|
||||
|
||||
func TestAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("BINARYLANE_API_TOKEN"); v == "" {
|
||||
t.Fatal("BINARYLANE_API_TOKEN must be set for acceptance tests")
|
||||
}
|
||||
|
||||
if v := os.Getenv("BINARYLANE_ACC_TEST_PROJECT"); v == "" {
|
||||
t.Fatal("BINARYLANE_ACC_TEST_PROJECT must be set for acceptance tests")
|
||||
}
|
||||
|
||||
if v := os.Getenv("BINARYLANE_ACC_TEST_LOCATION"); v == "" {
|
||||
t.Fatal("BINARYLANE_ACC_TEST_LOCATION must be set for acceptance tests")
|
||||
}
|
||||
}
|
||||
|
||||
func GetRandomResourceName(resType string) string {
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyz")
|
||||
b := make([]rune, 8)
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return fmt.Sprintf("%s-%s-%s", TestAccNamePrefix, resType, string(b))
|
||||
}
|
||||
|
||||
@@ -2,22 +2,45 @@ 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/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
|
||||
}
|
||||
|
||||
type serverDataSourceModel struct {
|
||||
Id types.String `tfsdk:"id"`
|
||||
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) {
|
||||
@@ -25,17 +48,25 @@ func (d *serverDataSource) Metadata(ctx context.Context, req datasource.Metadata
|
||||
}
|
||||
|
||||
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 serverDataSourceModel
|
||||
var data resources.ServerModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
@@ -44,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,292 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/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"
|
||||
}
|
||||
|
||||
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 (r *serverFirewallRulesResource) 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("server_id"), id)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
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"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -6,10 +6,16 @@ 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/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/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 +36,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 +52,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),
|
||||
)
|
||||
|
||||
@@ -61,51 +75,128 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
|
||||
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
|
||||
}
|
||||
|
||||
// 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,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true, // "read only" with computed=true and required/optional=false
|
||||
}
|
||||
|
||||
privateIpv4AddressesDescription := "The private IPv4 addresses assigned to the server."
|
||||
resp.Schema.Attributes["private_ipv4_addresses"] = &schema.ListAttribute{
|
||||
Description: privateIpv4AddressesDescription,
|
||||
MarkdownDescription: privateIpv4AddressesDescription,
|
||||
ElementType: types.StringType,
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true, // "read only" with computed=true and required/optional=false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var plan resources.ServerModel
|
||||
var data serverModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
diags := req.Plan.Get(ctx, &plan)
|
||||
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 server: name=%s", plan.Name.ValueString()))
|
||||
tflog.Debug(ctx, fmt.Sprintf("Creating server: name=%s", data.Name.ValueString()))
|
||||
|
||||
body := binarylane.CreateServerRequest{
|
||||
Name: plan.Name.ValueStringPointer(),
|
||||
Image: plan.Image.ValueString(),
|
||||
Region: plan.Region.ValueString(),
|
||||
Size: plan.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(),
|
||||
Options: &binarylane.SizeOptionsRequest{
|
||||
Ipv4Addresses: data.PublicIpv4Count.ValueInt32Pointer(),
|
||||
},
|
||||
}
|
||||
|
||||
if plan.Password.IsNull() {
|
||||
plan.Password = types.StringUnknown()
|
||||
if data.Password.IsNull() {
|
||||
data.Password = types.StringNull()
|
||||
} else {
|
||||
body.Password = plan.Password.ValueStringPointer()
|
||||
body.Password = data.Password.ValueStringPointer()
|
||||
}
|
||||
|
||||
serverResp, err := r.bc.client.PostServersWithResponse(ctx, body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating server: name=%s", plan.Name.ValueString()),
|
||||
fmt.Sprintf("Error creating server: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
@@ -114,28 +205,88 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server",
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", serverResp.Status(), plan.Name.ValueString(), serverResp.Body))
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
assignInt64(serverResp.JSON200.Server.Id, &plan.Id)
|
||||
assignStr(serverResp.JSON200.Server.Name, &plan.Name)
|
||||
assignStr(serverResp.JSON200.Server.Image.Slug, &plan.Image)
|
||||
assignStr(serverResp.JSON200.Server.Region.Slug, &plan.Region)
|
||||
assignStr(serverResp.JSON200.Server.Size.Slug, &plan.Size)
|
||||
plan.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
// assignBool(&serverResp.JSON200.Server.Networks.PortBlocking, &plan.PortBlocking)
|
||||
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)
|
||||
|
||||
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, &plan)...)
|
||||
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 +295,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
|
||||
@@ -157,13 +308,16 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res
|
||||
|
||||
if serverResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server",
|
||||
fmt.Sprintf("Received %s reading server: name=%s. Details: %s", serverResp.Status(), data.Id.String(), serverResp.Body))
|
||||
"Unexpected HTTP status code reading server",
|
||||
fmt.Sprintf("Received %s reading server: id=%s, name=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), data.Id.String(), serverResp.Body))
|
||||
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 +328,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)...)
|
||||
@@ -184,13 +338,14 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
// TODO
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
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)...)
|
||||
@@ -207,7 +362,7 @@ func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest,
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
delResp, err := r.bc.client.DeleteServersServerIdWithResponse(ctx, data.Id.ValueInt64(), ¶ms)
|
||||
serverResp, err := r.bc.client.DeleteServersServerIdWithResponse(ctx, data.Id.ValueInt64(), ¶ms)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting server: name=%s, server_id=%s", data.Name.ValueString(), data.Id.String()),
|
||||
@@ -216,10 +371,15 @@ func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest,
|
||||
return
|
||||
}
|
||||
|
||||
if delResp.StatusCode() != http.StatusNoContent {
|
||||
if serverResp.StatusCode() != http.StatusNoContent {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting server",
|
||||
fmt.Sprintf("Received %s deleting server: name=%s, server_id=%s. Details: %s", delResp.Status(), data.Name.ValueString(), data.Id.String(), delResp.Body))
|
||||
fmt.Sprintf("Received %s deleting server: name=%s, server_id=%s. Details: %s", serverResp.Status(), data.Name.ValueString(), data.Id.String(), serverResp.Body))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// func (r *serverResource) 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)
|
||||
// }
|
||||
|
||||
@@ -1,78 +1,75 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccOrderResource(t *testing.T) {
|
||||
func TestServerResource(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" "example" {
|
||||
name = "example"
|
||||
region = "per"
|
||||
image = "ubuntu-24.04"
|
||||
size = "std-min"
|
||||
resource "binarylane_vpc" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify number of items
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.#", "1"),
|
||||
// Verify first order item
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.quantity", "2"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.id", "1"),
|
||||
// Verify first coffee item has Computed attributes filled.
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.description", ""),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.image", "/hashicorp.png"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.name", "HCP Aeropress"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.price", "200"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.teaser", "Automation in a cup"),
|
||||
// Verify dynamic values have any value set in the state.
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "id"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "last_updated"),
|
||||
),
|
||||
},
|
||||
// ImportState testing
|
||||
{
|
||||
ResourceName: "binarylane_server.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
// The last_updated attribute does not exist in the HashiCups
|
||||
// API, therefore there is no value for it during import.
|
||||
ImportStateVerifyIgnore: []string{"last_updated"},
|
||||
},
|
||||
// Update and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
items = [
|
||||
{
|
||||
coffee = {
|
||||
id = 2
|
||||
}
|
||||
quantity = 2
|
||||
},
|
||||
]
|
||||
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
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify first order item updated
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.quantity", "2"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.id", "2"),
|
||||
// Verify first coffee item has Computed attributes updated.
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.description", ""),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.image", "/packer.png"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.name", "Packer Spiced Latte"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.price", "350"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.teaser", "Packed with goodness to spice up your images"),
|
||||
// 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"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrSet("data.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"),
|
||||
),
|
||||
},
|
||||
// Delete testing automatically occurs in TestCase
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &sshKeyDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &sshKeyDataSource{}
|
||||
)
|
||||
|
||||
func NewSshKeyDataSource() datasource.DataSource {
|
||||
return &sshKeyDataSource{}
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
var data resources.SshKeyModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
sshResp, err := d.bc.client.GetAccountKeysKeyIdWithResponse(ctx, int(data.Id.ValueInt64()))
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading SSH Key: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading SSH Key",
|
||||
fmt.Sprintf("Received %s reading SSH Key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Example data value setting
|
||||
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)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &sshKeyResource{}
|
||||
_ resource.ResourceWithConfigure = &sshKeyResource{}
|
||||
// _ resource.ResourceWithImportState = &serverResource{}
|
||||
)
|
||||
|
||||
func NewSshKeyResource() resource.Resource {
|
||||
return &sshKeyResource{}
|
||||
}
|
||||
|
||||
type sshKeyResource struct {
|
||||
bc *BinarylaneClient
|
||||
}
|
||||
|
||||
func (d *sshKeyResource) 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 *sshKeyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_ssh_key"
|
||||
}
|
||||
|
||||
func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = resources.SshKeyResourceSchema(ctx)
|
||||
resp.Schema.Description = "TODO"
|
||||
|
||||
// Overrides
|
||||
default_ := resp.Schema.Attributes["default"]
|
||||
resp.Schema.Attributes["default"] = schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
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) {
|
||||
var data resources.SshKeyModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create API call logic
|
||||
sshResp, err := r.bc.client.PostAccountKeysWithResponse(ctx, binarylane.SshKeyRequest{
|
||||
Name: data.Name.ValueString(),
|
||||
Default: data.Default.ValueBoolPointer(),
|
||||
PublicKey: data.PublicKey.ValueString(),
|
||||
})
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating SSH Key: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server",
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Set data values
|
||||
data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data resources.SshKeyModel
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Read API call logic
|
||||
sshResp, err := r.bc.client.GetAccountKeysKeyIdWithResponse(ctx, int(data.Id.ValueInt64()))
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error creating SSH Key: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if sshResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code creating server",
|
||||
fmt.Sprintf("Received %s creating new server: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Set data values
|
||||
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) // don't set or it will force replacement every time
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
func (r *sshKeyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data resources.SshKeyModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Update API call logic
|
||||
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)...)
|
||||
}
|
||||
|
||||
func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data resources.SshKeyModel
|
||||
|
||||
// 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
|
||||
sshResp, err := r.bc.client.DeleteAccountKeysKeyIdWithResponse(ctx, int(data.Id.ValueInt64()))
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error deleting SSH Key: name=%s", data.Name.ValueString()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if sshResp.StatusCode() != http.StatusNoContent {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code deleting SSH Key",
|
||||
fmt.Sprintf("Received %s deleting SSH Key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body),
|
||||
)
|
||||
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,67 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestSshKeyResource(t *testing.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 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"
|
||||
}
|
||||
|
||||
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", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"),
|
||||
resource.TestCheckResourceAttr("binarylane_ssh_key.test", "default", "false"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_ssh_key.test", "id"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "public_key", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"),
|
||||
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
|
||||
},
|
||||
})
|
||||
}
|
||||
+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,219 @@
|
||||
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/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"
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
// 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 SshKeyResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"default": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "Optional: If true this will be added to all new server installations (if we support SSH Key injection for the server's operating system).",
|
||||
MarkdownDescription: "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": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID or fingerprint of the SSH Key to fetch.",
|
||||
MarkdownDescription: "The ID or fingerprint of the SSH Key to fetch.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "A name to help you identify the key.",
|
||||
MarkdownDescription: "A name to help you identify the key.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"public_key": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The public key in OpenSSH \"authorized_keys\" format.",
|
||||
MarkdownDescription: "The public key in OpenSSH \"authorized_keys\" format.",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type SshKeyModel struct {
|
||||
Default types.Bool `tfsdk:"default"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
PublicKey types.String `tfsdk:"public_key"`
|
||||
}
|
||||
@@ -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{},
|
||||
}
|
||||
}
|
||||
+298
-1443
File diff suppressed because it is too large
Load Diff
+77
-26
@@ -27,11 +27,13 @@ resources:
|
||||
attributes:
|
||||
aliases:
|
||||
server_id: id
|
||||
ipv4_addresses: options.ipv4_addresses
|
||||
overrides:
|
||||
backups:
|
||||
type: bool
|
||||
description: >
|
||||
If true this will enable two daily backups for the server. Options.daily_backups will override this value if provided. Setting this to false has no effect.
|
||||
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 +41,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: >
|
||||
@@ -49,33 +51,82 @@ resources:
|
||||
generated and emailed to the account email address.
|
||||
sensitive: true
|
||||
optional_required: optional
|
||||
ignores:
|
||||
- ipv6
|
||||
- licenses
|
||||
- links
|
||||
- options
|
||||
- server
|
||||
- ssh_keys
|
||||
- vpc_id
|
||||
- user_data
|
||||
- port_blocking
|
||||
|
||||
data_sources:
|
||||
server:
|
||||
read:
|
||||
path: /servers/{server_id}
|
||||
method: GET
|
||||
schema:
|
||||
attributes:
|
||||
aliases:
|
||||
server_id: id
|
||||
ignores:
|
||||
- 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
|
||||
method: POST
|
||||
read:
|
||||
path: /account/keys/{key_id}
|
||||
method: GET
|
||||
delete:
|
||||
path: /account/keys/{key_id}
|
||||
method: DELETE
|
||||
schema:
|
||||
attributes:
|
||||
aliases:
|
||||
key_id: id
|
||||
ignores:
|
||||
- ssh_key
|
||||
vpc:
|
||||
create:
|
||||
path: /vpcs
|
||||
method: POST
|
||||
read:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: GET
|
||||
update:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: PATCH
|
||||
delete:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: DELETE
|
||||
schema:
|
||||
attributes:
|
||||
aliases:
|
||||
vpc_id: id
|
||||
ignores:
|
||||
- route_entries
|
||||
- vpc
|
||||
vpc_route_entries:
|
||||
create:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: PATCH
|
||||
read:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: GET
|
||||
update:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: PATCH
|
||||
delete:
|
||||
path: /vpcs/{vpc_id}
|
||||
method: DELETE
|
||||
schema:
|
||||
ignores:
|
||||
- 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