mirror of
https://github.com/oscarhermoso/terraform-provider-binarylane
synced 2026-10-21 06:06:07 +11:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 32e9056957 | |||
| 325b2e96ea |
@@ -23,7 +23,7 @@ jobs:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
# Allow goreleaser to access older tag information.
|
||||
fetch-depth: 0
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||
with:
|
||||
args: release --clean
|
||||
env:
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
group: test
|
||||
cancel-in-progress: true # would be nice if GH offered cancel-pending instead, https://github.com/orgs/community/discussions/63136
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
# name: Unit Tests
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v5
|
||||
# - uses: actions/checkout@v4
|
||||
# - uses: actions/setup-go@v5
|
||||
# with:
|
||||
# go-version-file: 'go.mod'
|
||||
|
||||
+2
-1
@@ -24,7 +24,8 @@ provider_installation {
|
||||
4. Build and test the provider
|
||||
|
||||
```sh
|
||||
go build -o bin/terraform-provider-binarylane
|
||||
# Build with line numbers
|
||||
go build -ldflags="-s -w" -o bin/terraform-provider-binarylane
|
||||
go install
|
||||
cd examples/basic
|
||||
terraform plan
|
||||
|
||||
@@ -20,9 +20,9 @@ Planned features:
|
||||
- [x] Cloud Init
|
||||
- [x] Updating server properties
|
||||
- [x] "Advanced Features"
|
||||
- [x] IPV6
|
||||
- [ ] Customised Backups
|
||||
- [ ] Software
|
||||
- [ ] IPV6
|
||||
- [ ] Alerts
|
||||
- [x] SSH Keys
|
||||
- [x] Virtual Private Cloud
|
||||
|
||||
@@ -33,8 +33,8 @@ data "binarylane_server" "example" {
|
||||
- must be a multiple of 5
|
||||
- \> 60 GB must be a multiple of 10
|
||||
- \> 200 GB must be a multiple of 100
|
||||
- `disks` (Attributes List) A list of the disks that are currently attached to the server. (see [below for nested schema](#nestedatt--disks))
|
||||
- `image` (String) The slug of the selected operating system, such as `debian-12`. You can fetch a full list of images from the BinaryLane API.
|
||||
- `ipv6` (Boolean) If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled.
|
||||
- `memory` (Number) The total memory in MB for this server. Leave null to accept the default size. Valid values:
|
||||
- must be a multiple of 128
|
||||
- \> 2048 MB must be a multiple of 1024
|
||||
@@ -44,9 +44,7 @@ data "binarylane_server" "example" {
|
||||
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
|
||||
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
|
||||
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
|
||||
- `private_ipv6_addresses` (List of String) The private IPv6 addresses assigned to the server.
|
||||
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
|
||||
- `public_ipv6_addresses` (List of String) The public IPv6 addresses assigned to the server.
|
||||
- `region` (String) The slug of the selected region.
|
||||
- `size` (String) The slug of the selected size.
|
||||
- `source_and_destination_check` (Boolean) This attribute can only be set if your server also has a `vpc_id` attribute set. When enabled (which is `true` by default), your server will only be able to send or receive packets that are directly addressed to one of the IP addresses associated with the Cloud Server. Generally, this is desirable behaviour because it prevents IP conflicts and other hard-to-diagnose networking faults due to incorrect network configuration. When `source_and_destination_check` is `false`, your Cloud Server will be able to send and receive packets addressed to any server. This is typically used when you want to use your Cloud Server as a VPN endpoint, a NAT server to provide internet access, or IP forwarding.
|
||||
@@ -69,3 +67,14 @@ Read-Only:
|
||||
- `qemu_guest_agent` (Boolean)
|
||||
- `uefi_boot` (Boolean)
|
||||
- `unset_uuid` (Boolean)
|
||||
|
||||
|
||||
<a id="nestedatt--disks"></a>
|
||||
### Nested Schema for `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.
|
||||
|
||||
@@ -44,7 +44,7 @@ resource "binarylane_server" "example" {
|
||||
- must be a multiple of 5
|
||||
- \> 60 GB must be a multiple of 10
|
||||
- \> 200 GB must be a multiple of 100
|
||||
- `ipv6` (Boolean) If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled.
|
||||
- `disks` (Attributes List) A list of the disks that are currently attached to the server. (see [below for nested schema](#nestedatt--disks))
|
||||
- `memory` (Number) The total memory in MB for this server. Leave null to accept the default size. Valid values:
|
||||
- must be a multiple of 128
|
||||
- \> 2048 MB must be a multiple of 1024
|
||||
@@ -65,9 +65,7 @@ resource "binarylane_server" "example" {
|
||||
- `password_change_supported` (Boolean) If this is true then the `password` attribute can be changed with Terraform. If this is false then the `password` attribute can only be replaced with a null/empty value, which will clear the root/administrator password allowing the password to be changed via the web console.
|
||||
- `permalink` (String) A randomly generated two-word identifier assigned to servers in regions that support this feature
|
||||
- `private_ipv4_addresses` (List of String) The private IPv4 addresses assigned to the server.
|
||||
- `private_ipv6_addresses` (List of String) The private IPv6 addresses assigned to the server.
|
||||
- `public_ipv4_addresses` (List of String) The public IPv4 addresses assigned to the server.
|
||||
- `public_ipv6_addresses` (List of String) The public IPv6 addresses assigned to the server.
|
||||
|
||||
<a id="nestedatt--advanced_features"></a>
|
||||
### Nested Schema for `advanced_features`
|
||||
@@ -89,6 +87,23 @@ Read-Only:
|
||||
- `uefi_boot` (Boolean) When this option is enabled the Cloud Server will use UEFI instead of legacy PC BIOS.
|
||||
|
||||
|
||||
<a id="nestedatt--disks"></a>
|
||||
### Nested Schema for `disks`
|
||||
|
||||
Required:
|
||||
|
||||
- `size_gigabytes` (Number) The size of the disk in GB.
|
||||
|
||||
Optional:
|
||||
|
||||
- `description` (String) A description of this disk.
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `id` (Number) The ID of this disk.
|
||||
- `primary` (Boolean) A primary disk is treated differently from other disks.
|
||||
|
||||
|
||||
<a id="nestedatt--timeouts"></a>
|
||||
### Nested Schema for `timeouts`
|
||||
|
||||
|
||||
@@ -17,4 +17,11 @@ resource "binarylane_server" "example" {
|
||||
size = "std-min" # 1 VPCU, 1 GB Memory, 20 GB NVME Storage, 1000 GB Data Transfer
|
||||
public_ipv4_count = 1
|
||||
# Password will be generated automatically and mailed to the account holder
|
||||
disks = [{
|
||||
description = "Primary Disk"
|
||||
size_gigabytes = 10
|
||||
}, {
|
||||
description = "Secondary Disk"
|
||||
size_gigabytes = 10
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ require (
|
||||
github.com/hashicorp/terraform-plugin-framework-validators v0.18.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.28.0
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.3
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.2
|
||||
github.com/oapi-codegen/oapi-codegen/v2 v2.5.0
|
||||
github.com/oapi-codegen/runtime v1.1.2
|
||||
)
|
||||
|
||||
@@ -163,8 +163,8 @@ github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9T
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 h1:NFPMacTrY/IdcIcnUB+7hsore1ZaRWU9cnB6jFoBnIM=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0/go.mod h1:QYmYnLfsosrxjCnGY1p9c7Zj6n9thnEE+7RObeYs3fA=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.3 h1:QLi/khB8Z0a5L54AfPrHukFpnwsGL8cwwswj4RZduCo=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.3/go.mod h1:WHQ9FDdiLoneey2/QHpGM/6SAYf4A7AZazVg7230pLE=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.2 h1:mSotG4Odl020vRjIenA3rggwo6Kg6XCKIwtRhYgp+/M=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.2/go.mod h1:WHQ9FDdiLoneey2/QHpGM/6SAYf4A7AZazVg7230pLE=
|
||||
github.com/hashicorp/terraform-registry-address v0.3.0 h1:HMpK3nqaGFPS9VmgRXrJL/dzHNdheGVKk5k7VlFxzCo=
|
||||
github.com/hashicorp/terraform-registry-address v0.3.0/go.mod h1:jRGCMiLaY9zii3GLC7hqpSnwhfnCN5yzvY0hh4iCGbM=
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
|
||||
|
||||
@@ -1356,16 +1356,16 @@ type DisableSelinuxType string
|
||||
// Disk defines model for Disk.
|
||||
type Disk struct {
|
||||
// Description A description of this disk.
|
||||
Description *string `json:"description"`
|
||||
Description *string `json:"description" tfsdk:"description"`
|
||||
|
||||
// Id The ID of this disk.
|
||||
Id *int64 `json:"id,omitempty"`
|
||||
Id *int64 `json:"id,omitempty" tfsdk:"id"`
|
||||
|
||||
// Primary A primary disk is treated differently from other disks.
|
||||
Primary *bool `json:"primary,omitempty"`
|
||||
Primary *bool `json:"primary,omitempty" tfsdk:"primary"`
|
||||
|
||||
// SizeGigabytes The size of the disk in GB.
|
||||
SizeGigabytes *float64 `json:"size_gigabytes,omitempty"`
|
||||
SizeGigabytes *float64 `json:"size_gigabytes,omitempty" tfsdk:"size_gigabytes"`
|
||||
}
|
||||
|
||||
// DistributionFeature
|
||||
@@ -1915,11 +1915,6 @@ type License struct {
|
||||
|
||||
// LicensedSoftware defines model for LicensedSoftware.
|
||||
type LicensedSoftware struct {
|
||||
// Incompatible Software that is incompatible with the server will be automatically removed at the next plan change.
|
||||
// Servers may have incompatible software due to changes made by support. Software is not incompatible merely because it is disabled;
|
||||
// disabled software may be retained by servers that already have it, incompatible software will be removed.
|
||||
Incompatible bool `json:"incompatible"`
|
||||
|
||||
// LicenceCount The current licence count for the software.
|
||||
LicenceCount int32 `json:"licence_count"`
|
||||
|
||||
@@ -2321,7 +2316,7 @@ type Resize struct {
|
||||
// ChangeImage This may be left null to keep the current base image for the server. If this is provided the server disks will be destroyed and the server will be rebuilt from the selected image.
|
||||
ChangeImage *ChangeImage `json:"change_image"`
|
||||
|
||||
// ChangeLicenses This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed. Any licences that are invalid will be removed regardless, see the documentation for `servers/{server_id}/software` for more information.
|
||||
// ChangeLicenses This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed.
|
||||
ChangeLicenses *ChangeLicenses `json:"change_licenses"`
|
||||
|
||||
// Options If this is null and the server has no selected size options the defaults for the size will be used. If this is null and the server has currently selected size options those will be preserved. If this is provided any option fields that are not included will be removed from the existing server.
|
||||
|
||||
@@ -31,8 +31,6 @@ type serverDataModel struct {
|
||||
resources.ServerModel
|
||||
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
|
||||
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
|
||||
PublicIpv6Addresses types.List `tfsdk:"public_ipv6_addresses"`
|
||||
PrivateIpv6Addresses types.List `tfsdk:"private_ipv6_addresses"`
|
||||
Permalink types.String `tfsdk:"permalink"`
|
||||
Memory types.Int32 `tfsdk:"memory"`
|
||||
Disk types.Int32 `tfsdk:"disk"`
|
||||
@@ -111,7 +109,6 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
|
||||
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
data.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
|
||||
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
|
||||
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
|
||||
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
|
||||
@@ -165,31 +162,12 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
data.PrivateIPv4Addresses = tfPrivateIpv4Addresses
|
||||
}
|
||||
|
||||
publicIpv6Addresses := []string{}
|
||||
privateIpv6Addresses := []string{}
|
||||
|
||||
for _, v6address := range serverResp.JSON200.Server.Networks.V6 {
|
||||
if v6address.Type == "public" {
|
||||
publicIpv6Addresses = append(publicIpv6Addresses, v6address.IpAddress)
|
||||
} else {
|
||||
privateIpv6Addresses = append(privateIpv6Addresses, v6address.IpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
tfPublicIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv6Addresses)
|
||||
disks, diag := types.ListValueFrom(ctx, data.Disks.ElementType(ctx), serverResp.JSON200.Server.Disks)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() {
|
||||
data.PublicIpv6Addresses = types.ListUnknown(data.PublicIpv6Addresses.ElementType(ctx))
|
||||
data.Disks = types.ListUnknown(data.Disks.ElementType(ctx))
|
||||
} else {
|
||||
data.PublicIpv6Addresses = tfPublicIpv6Addresses
|
||||
}
|
||||
|
||||
tfPrivateIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv6Addresses)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() {
|
||||
data.PrivateIpv6Addresses = types.ListUnknown(data.PrivateIpv6Addresses.ElementType(ctx))
|
||||
} else {
|
||||
data.PrivateIpv6Addresses = tfPrivateIpv6Addresses
|
||||
data.Disks = disks
|
||||
}
|
||||
|
||||
// Get user data script
|
||||
|
||||
@@ -256,40 +256,6 @@ func serverSchema(ctx context.Context) schema.Schema {
|
||||
},
|
||||
}
|
||||
|
||||
ipv6Description := "If `true` this will add a public and private IPv6 address to the server. By default, IPv6 is disabled."
|
||||
s.Attributes["ipv6"] = schema.BoolAttribute{
|
||||
Description: ipv6Description,
|
||||
MarkdownDescription: ipv6Description,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Default: booldefault.StaticBool(false), // Add default to ipv6
|
||||
}
|
||||
|
||||
publicIpv6AddressesDescription := "The public IPv6 addresses assigned to the server."
|
||||
s.Attributes["public_ipv6_addresses"] = schema.ListAttribute{
|
||||
Description: publicIpv6AddressesDescription,
|
||||
MarkdownDescription: publicIpv6AddressesDescription,
|
||||
ElementType: types.StringType,
|
||||
// read only
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
privateIpv6AddressesDescription := "The private IPv6 addresses assigned to the server."
|
||||
s.Attributes["private_ipv6_addresses"] = schema.ListAttribute{
|
||||
Description: privateIpv6AddressesDescription,
|
||||
MarkdownDescription: privateIpv6AddressesDescription,
|
||||
ElementType: types.StringType,
|
||||
// read only
|
||||
Optional: false,
|
||||
Required: false,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.List{
|
||||
listplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
}
|
||||
|
||||
s.Attributes["permalink"] = schema.StringAttribute{
|
||||
Description: "A randomly generated two-word identifier assigned to servers in regions that support this feature",
|
||||
MarkdownDescription: "A randomly generated two-word identifier assigned to servers in regions that support this feature",
|
||||
@@ -448,16 +414,6 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
|
||||
)
|
||||
}
|
||||
|
||||
if !plan.Ipv6.Equal(state.Ipv6) {
|
||||
if plan.Ipv6.ValueBool() {
|
||||
plan.PublicIpv6Addresses = types.ListUnknown(state.PublicIpv6Addresses.ElementType(ctx))
|
||||
plan.PrivateIpv6Addresses = types.ListUnknown(state.PrivateIpv6Addresses.ElementType(ctx))
|
||||
} else {
|
||||
plan.PublicIpv6Addresses = types.ListNull(state.PublicIpv6Addresses.ElementType(ctx))
|
||||
plan.PrivateIpv6Addresses = types.ListNull(state.PrivateIpv6Addresses.ElementType(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
// Use state for unknown disk/memory values, as long as server size is the same
|
||||
if (plan.Memory.IsNull() || plan.Memory.IsUnknown()) && plan.Size.Equal(state.Size) {
|
||||
plan.Memory = state.Memory
|
||||
@@ -465,8 +421,13 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
|
||||
if (plan.Disk.IsNull() || plan.Disk.IsUnknown()) && plan.Size.Equal(state.Size) {
|
||||
plan.Disk = state.Disk
|
||||
}
|
||||
if (plan.Disks.IsNull() || plan.Disks.IsUnknown()) && plan.Size.Equal(state.Size) {
|
||||
plan.Disks = state.Disks
|
||||
}
|
||||
|
||||
if isAdvFeatChanged(&config.AdvancedFeatures, &state.AdvancedFeatures) {
|
||||
if !isAdvFeatChanged(&config.AdvancedFeatures, &state.AdvancedFeatures) {
|
||||
plan.AdvancedFeatures = state.AdvancedFeatures
|
||||
} else {
|
||||
advFeatResp, err := r.bc.client.GetServersServerIdAvailableAdvancedFeaturesWithResponse(ctx, state.Id.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
@@ -571,7 +532,6 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
Ipv4Addresses: data.PublicIpv4Count.ValueInt32Pointer(),
|
||||
},
|
||||
Backups: data.Backups.ValueBoolPointer(),
|
||||
Ipv6: data.Ipv6.ValueBoolPointer(),
|
||||
}
|
||||
|
||||
if !data.Memory.IsNull() && !data.Memory.IsUnknown() {
|
||||
@@ -629,7 +589,6 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug)
|
||||
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
data.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
|
||||
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
|
||||
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
|
||||
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
|
||||
@@ -674,6 +633,14 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
data.PrivateIPv4Addresses, diags = types.ListValueFrom(ctx, types.StringType, privateIpv4Addresses)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
disks, diags := types.ListValueFrom(ctx, data.Disks.ElementType(ctx), serverResp.JSON200.Server.Disks)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if diags.HasError() {
|
||||
data.Disks = types.ListUnknown(data.Disks.ElementType(ctx))
|
||||
} else {
|
||||
data.Disks = disks
|
||||
}
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
@@ -704,8 +671,14 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
|
||||
// One extra read to check the final state of enabled_advanced_features, needed because
|
||||
// some flags (like "cloud-init") are not set until the server is fully created. See #13
|
||||
diag := r.fetchServerResourceState(ctx, &data)
|
||||
resp.Diagnostics.Append(diag...)
|
||||
diags = r.fetchServerResourceState(ctx, &data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
// Update disks
|
||||
if !config.Disks.IsNull() && !config.Disks.Equal(data.Disks) {
|
||||
diags = r.updateDisks(ctx, &data, config.Disks)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
}
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
@@ -936,43 +909,6 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Change ipv6
|
||||
if !plan.Ipv6.Equal(state.Ipv6) {
|
||||
ipv6Resp, err := r.bc.client.PostServersServerIdActionsChangeIpv6WithResponse(
|
||||
ctx,
|
||||
state.Id.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeIpv6JSONRequestBody{
|
||||
Type: "change_ipv6",
|
||||
Enabled: plan.Ipv6.ValueBool(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error changing IPv6", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *ipv6Resp.JSON200.Action.Id)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for changing IPv6", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
state.Ipv6 = plan.Ipv6
|
||||
state.PublicIpv6Addresses = plan.PublicIpv6Addresses
|
||||
state.PrivateIpv6Addresses = plan.PrivateIpv6Addresses
|
||||
|
||||
if state.PublicIpv6Addresses.IsUnknown() || state.PrivateIpv6Addresses.IsUnknown() || listContainsUnknown(ctx, state.PublicIpv6Addresses) || listContainsUnknown(ctx, state.PrivateIpv6Addresses) {
|
||||
refreshNeeded = true
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Rebuild operation
|
||||
if !plan.SshKeys.Equal(state.SshKeys) || !plan.UserData.IsNull() && !plan.UserData.Equal(state.UserData) {
|
||||
var rebuildReq *binarylane.PostServersServerIdActionsRebuildJSONRequestBody
|
||||
@@ -1082,6 +1018,18 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Update disks
|
||||
if !config.Disks.IsNull() && !config.Disks.Equal(plan.Disks) {
|
||||
diags := r.updateDisks(ctx, &state, config.Disks)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Enable or disable backups
|
||||
if !plan.Backups.Equal(state.Backups) {
|
||||
if plan.Backups.ValueBool() {
|
||||
@@ -1306,6 +1254,181 @@ func (r *serverResource) updateSourceDestCheck(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *serverResource) updateDisks(ctx context.Context, state *serverResourceModel, disks types.List) diag.Diagnostics {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
if state.Disk.IsUnknown() || state.Disk.IsNull() {
|
||||
diags.AddError("Error updating disks", "Unable to update disks because the `disk` attribute is unknown or null.")
|
||||
return diags
|
||||
}
|
||||
if state.Disks.IsUnknown() || state.Disks.IsNull() {
|
||||
diags.AddError("Error updating disks", "Unable to update disks because the `disks` attribute is unknown or null.")
|
||||
return diags
|
||||
}
|
||||
if state.Disks.Equal(disks) {
|
||||
tflog.Debug(ctx, "No changes to disks, skipping update")
|
||||
return diags
|
||||
}
|
||||
|
||||
var current []resources.DisksValue
|
||||
diags.Append(state.Disks.ElementsAs(ctx, ¤t, false)...)
|
||||
if diags.HasError() {
|
||||
return diags
|
||||
}
|
||||
|
||||
var desired []resources.DisksValue
|
||||
diags.Append(disks.ElementsAs(ctx, &desired, false)...)
|
||||
if diags.HasError() {
|
||||
return diags
|
||||
}
|
||||
|
||||
// 1. Check that desired disks fit within max VM disk size
|
||||
var totalDesired float64
|
||||
for _, d := range desired {
|
||||
totalDesired += d.SizeGigabytes.ValueFloat64()
|
||||
}
|
||||
if int32(totalDesired) > state.Disk.ValueInt32() {
|
||||
diags.AddError("Error updating disks",
|
||||
fmt.Sprintf("Total desired disk size %d GB exceeds maximum allowed %d GB",
|
||||
int32(totalDesired), state.Disk.ValueInt32()))
|
||||
return diags
|
||||
}
|
||||
|
||||
// Build maps for diffing
|
||||
currentMap := make(map[int64]resources.DisksValue)
|
||||
for _, d := range current {
|
||||
currentMap[d.Id.ValueInt64()] = d
|
||||
}
|
||||
|
||||
desiredMap := make(map[int64]resources.DisksValue)
|
||||
for _, d := range desired {
|
||||
// Only disks with IDs are "existing". New ones will have Id.IsNull()
|
||||
if !d.Id.IsNull() && !d.Id.IsUnknown() {
|
||||
desiredMap[d.Id.ValueInt64()] = d
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unused disks
|
||||
for id := range currentMap {
|
||||
if _, ok := desiredMap[id]; !ok {
|
||||
tflog.Debug(ctx, "Deleting disk", map[string]any{"disk_id": id})
|
||||
resp, err := r.bc.client.PostServersServerIdActionsDeleteDiskWithResponse(ctx, state.Id.ValueInt64(), binarylane.DeleteDisk{
|
||||
DiskId: id,
|
||||
Type: "delete_disk",
|
||||
})
|
||||
if err != nil {
|
||||
diags.AddError("Error deleting disk", err.Error())
|
||||
return diags
|
||||
}
|
||||
// WIP
|
||||
|
||||
// binarylane_server.example: Modifying... [name=tf-example-basic]
|
||||
// ╷
|
||||
// │ Error: Unexpected HTTP status code deleting disk
|
||||
// │
|
||||
// │ with binarylane_server.example,
|
||||
// │ on main.tf line 13, in resource "binarylane_server" "example":
|
||||
// │ 13: resource "binarylane_server" "example" {
|
||||
// │
|
||||
// │ Received 400 Bad Request deleting disk: server_id=523165, disk_id=1. Details:
|
||||
// │ {"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors
|
||||
// │ occurred","status":400,"errors":{"disk_id":["May not delete primary disk."]},"message":"May not delete
|
||||
// │ primary disk."}
|
||||
if resp.StatusCode() != http.StatusOK {
|
||||
diags.AddError("Unexpected HTTP status code deleting disk",
|
||||
fmt.Sprintf("Received %s deleting disk: server_id=%s, disk_id=%d. Details: %s",
|
||||
resp.Status(), state.Id.String(), id, resp.Body))
|
||||
return diags
|
||||
}
|
||||
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *resp.JSON200.Action.Id)
|
||||
if err != nil {
|
||||
diags.AddError("Error waiting for disks to be deleted", err.Error())
|
||||
return diags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Downsize disks
|
||||
for id, desiredDisk := range desiredMap {
|
||||
if currentDisk, ok := currentMap[id]; ok {
|
||||
curSize := int32(currentDisk.SizeGigabytes.ValueFloat64())
|
||||
newSize := int32(desiredDisk.SizeGigabytes.ValueFloat64())
|
||||
if newSize < curSize {
|
||||
tflog.Debug(ctx, "Downsizing disk", map[string]any{"disk_id": id, "from": curSize, "to": newSize})
|
||||
resp, err := r.bc.client.PostServersServerIdActionsResizeDiskWithResponse(ctx, state.Id.ValueInt64(), binarylane.ResizeDisk{
|
||||
DiskId: id,
|
||||
SizeGigabytes: newSize,
|
||||
Type: "resize_disk",
|
||||
})
|
||||
if err != nil {
|
||||
diags.AddError("Error downsizing disk", err.Error())
|
||||
return diags
|
||||
}
|
||||
if resp.StatusCode() != http.StatusOK {
|
||||
diags.AddError("Unexpected HTTP status code downsizing disks",
|
||||
fmt.Sprintf("Received %s downsizing disks: server_id=%s, disk_id=%d. Details: %s",
|
||||
resp.Status(), state.Id.String(), id, resp.Body))
|
||||
return diags
|
||||
}
|
||||
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *resp.JSON200.Action.Id)
|
||||
if err != nil {
|
||||
diags.AddError("Error waiting for disks to downsize", err.Error())
|
||||
return diags
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upsize disks
|
||||
for id, desiredDisk := range desiredMap {
|
||||
if currentDisk, ok := currentMap[id]; ok {
|
||||
curSize := int32(currentDisk.SizeGigabytes.ValueFloat64())
|
||||
newSize := int32(desiredDisk.SizeGigabytes.ValueFloat64())
|
||||
if newSize > curSize {
|
||||
tflog.Debug(ctx, "Upsizing disk", map[string]any{"disk_id": id, "from": curSize, "to": newSize})
|
||||
resp, err := r.bc.client.PostServersServerIdActionsResizeDiskWithResponse(ctx, state.Id.ValueInt64(), binarylane.ResizeDisk{
|
||||
DiskId: id,
|
||||
SizeGigabytes: newSize,
|
||||
Type: "resize_disk",
|
||||
})
|
||||
if err != nil {
|
||||
diags.AddError("Error upsizing disk", err.Error())
|
||||
return diags
|
||||
}
|
||||
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *resp.JSON200.Action.Id)
|
||||
if err != nil {
|
||||
diags.AddError("Error waiting for new disks to upsize", err.Error())
|
||||
return diags
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Add new disks (those with no ID in desired)
|
||||
for _, desiredDisk := range desired {
|
||||
if desiredDisk.Id.IsNull() || desiredDisk.Id.IsUnknown() {
|
||||
desc := desiredDisk.Description.ValueStringPointer()
|
||||
tflog.Debug(ctx, "Creating new disk", map[string]any{"size": desiredDisk.SizeGigabytes.ValueFloat64()})
|
||||
resp, err := r.bc.client.PostServersServerIdActionsAddDiskWithResponse(ctx, state.Id.ValueInt64(), binarylane.AddDisk{
|
||||
Description: desc,
|
||||
SizeGigabytes: int32(desiredDisk.SizeGigabytes.ValueFloat64()),
|
||||
Type: "add_disk",
|
||||
})
|
||||
if err != nil {
|
||||
diags.AddError("Error creating disk", err.Error())
|
||||
return diags
|
||||
}
|
||||
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *resp.JSON200.Action.Id)
|
||||
if err != nil {
|
||||
diags.AddError("Error waiting for new disk to be added", err.Error())
|
||||
return diags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func (r *serverResource) fetchServerResourceState(ctx context.Context, state *serverResourceModel) diag.Diagnostics {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
@@ -1341,7 +1464,6 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
|
||||
state.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
|
||||
state.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
|
||||
state.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
state.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
|
||||
|
||||
advFeat := *serverResp.JSON200.Server.AdvancedFeatures.EnabledAdvancedFeatures
|
||||
state.AdvancedFeatures, diags = resources.NewAdvancedFeaturesValue(
|
||||
@@ -1366,10 +1488,16 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
|
||||
privateIpv4Addresses := []string{}
|
||||
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
if v4address.Type == "public" {
|
||||
switch v4address.Type {
|
||||
case "public":
|
||||
publicIpv4Addresses = append(publicIpv4Addresses, v4address.IpAddress)
|
||||
} else if v4address.Type == "private" {
|
||||
case "private":
|
||||
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
|
||||
default:
|
||||
diags.AddError(
|
||||
"Unexpected IP address type",
|
||||
fmt.Sprintf("Received unexpected IP address type '%s' for server: id=%d, name=%s", v4address.Type, *serverResp.JSON200.Server.Id, *serverResp.JSON200.Server.Name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1391,31 +1519,12 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
|
||||
state.PrivateIPv4Addresses = tfPrivateIpv4Addresses
|
||||
}
|
||||
|
||||
publicIpv6Addresses := []string{}
|
||||
privateIpv6Addresses := []string{}
|
||||
|
||||
for _, v6address := range serverResp.JSON200.Server.Networks.V6 {
|
||||
if v6address.Type == "public" {
|
||||
publicIpv6Addresses = append(publicIpv6Addresses, v6address.IpAddress)
|
||||
} else {
|
||||
privateIpv6Addresses = append(privateIpv6Addresses, v6address.IpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
tfPublicIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, publicIpv6Addresses)
|
||||
disks, diag := types.ListValueFrom(ctx, state.Disks.ElementType(ctx), serverResp.JSON200.Server.Disks)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() || tfPublicIpv6Addresses.IsNull() {
|
||||
state.PublicIpv6Addresses = types.ListUnknown(state.PublicIpv6Addresses.ElementType(ctx))
|
||||
if diag.HasError() {
|
||||
state.Disks = types.ListUnknown(state.Disks.ElementType(ctx))
|
||||
} else {
|
||||
state.PublicIpv6Addresses = tfPublicIpv6Addresses
|
||||
}
|
||||
|
||||
tfPrivateIpv6Addresses, diag := types.ListValueFrom(ctx, types.StringType, privateIpv6Addresses)
|
||||
diags.Append(diag...)
|
||||
if diag.HasError() || tfPrivateIpv6Addresses.IsNull() {
|
||||
state.PrivateIpv6Addresses = types.ListUnknown(state.PrivateIpv6Addresses.ElementType(ctx))
|
||||
} else {
|
||||
state.PrivateIpv6Addresses = tfPrivateIpv6Addresses
|
||||
state.Disks = disks
|
||||
}
|
||||
|
||||
return diags
|
||||
|
||||
@@ -104,8 +104,11 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.cloud_init", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.qemu_guest_agent", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "advanced_features.uefi_boot", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ipv6", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv6_addresses.#", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.description"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "20"),
|
||||
|
||||
// Verify data source values
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
|
||||
@@ -132,8 +135,11 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.cloud_init", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.qemu_guest_agent", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "advanced_features.uefi_boot", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "ipv6", "false"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "public_ipv6_addresses.#", "0"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "disks.0.description"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "disks.0.size_gigabytes", "20"),
|
||||
),
|
||||
},
|
||||
// Test import by ID
|
||||
@@ -184,7 +190,6 @@ resource "binarylane_server" "test" {
|
||||
advanced_features = {
|
||||
emulated_hyperv = true
|
||||
}
|
||||
ipv6 = true
|
||||
|
||||
# source_and_destination_check = null # defaults to null
|
||||
# backups = false # defaults to false
|
||||
@@ -219,9 +224,6 @@ echo "Hello Whitespace" > /var/tmp/output.txt
|
||||
|
||||
|
||||
`),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "ipv6", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv6_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv6_addresses.#", "1"),
|
||||
),
|
||||
},
|
||||
// Change password testing (Cannot run at same time as Rebuild operation, so it has it's own test)
|
||||
@@ -321,6 +323,85 @@ resource "binarylane_server" "test" {
|
||||
})
|
||||
}
|
||||
|
||||
func TestServerResourceDisks(t *testing.T) {
|
||||
// Must assign a password to the server or Binary Lane will send emails
|
||||
password := GenerateTestPassword(t)
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Create and Read testing
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 0
|
||||
disks = [
|
||||
{
|
||||
description = "Primary Disk"
|
||||
size_gigabytes = 9
|
||||
},
|
||||
{
|
||||
description = "Secondary Disk"
|
||||
size_gigabytes = 2
|
||||
},
|
||||
{
|
||||
description = "Tertiary Disk"
|
||||
size_gigabytes = 4
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.#", "2"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.0.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.description", "Renamed Primary Disk"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.primary", "true"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.0.size_gigabytes", "10"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "disks.1.id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.description", "Secondary Disk"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.primary", "false"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disks.1.size_gigabytes", "5"),
|
||||
),
|
||||
},
|
||||
// Test import by ID
|
||||
{
|
||||
ResourceName: "binarylane_server.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"password", "ssh_keys", "timeouts"},
|
||||
},
|
||||
// Test import by ID
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-resource"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 0
|
||||
disks = [
|
||||
{
|
||||
description = "Renamed Primary Disk"
|
||||
size_gigabytes = 10
|
||||
},
|
||||
{
|
||||
description = "Secondary Disk"
|
||||
size_gigabytes = 10
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func GenerateTestPassword(t *testing.T) string {
|
||||
t.Helper()
|
||||
pwBytes := make([]byte, 12)
|
||||
|
||||
@@ -94,6 +94,42 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.\n",
|
||||
MarkdownDescription: "If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.\n",
|
||||
},
|
||||
"disks": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"description": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "A description of this disk.",
|
||||
MarkdownDescription: "A description of this disk.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The ID of this disk.",
|
||||
MarkdownDescription: "The ID of this disk.",
|
||||
},
|
||||
"primary": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "A primary disk is treated differently from other disks.",
|
||||
MarkdownDescription: "A primary disk is treated differently from other disks.",
|
||||
},
|
||||
"size_gigabytes": schema.Float64Attribute{
|
||||
Required: true,
|
||||
Description: "The size of the disk in GB.",
|
||||
MarkdownDescription: "The size of the disk in GB.",
|
||||
},
|
||||
},
|
||||
CustomType: DisksType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: DisksValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "A list of the disks that are currently attached to the server.",
|
||||
MarkdownDescription: "A list of the disks that are currently attached to the server.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
@@ -105,12 +141,6 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "The slug of the selected operating system.",
|
||||
MarkdownDescription: "The slug of the selected operating system.",
|
||||
},
|
||||
"ipv6": schema.BoolAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If true this will enable IPv6 for this server.",
|
||||
MarkdownDescription: "If true this will enable IPv6 for this server.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
@@ -168,9 +198,9 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
type ServerModel struct {
|
||||
AdvancedFeatures AdvancedFeaturesValue `tfsdk:"advanced_features"`
|
||||
Backups types.Bool `tfsdk:"backups"`
|
||||
Disks types.List `tfsdk:"disks"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
Ipv6 types.Bool `tfsdk:"ipv6"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
PortBlocking types.Bool `tfsdk:"port_blocking"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
@@ -998,3 +1028,492 @@ func (v AdvancedFeaturesValue) AttributeTypes(ctx context.Context) map[string]at
|
||||
"unset_uuid": basetypes.BoolType{},
|
||||
}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = DisksType{}
|
||||
|
||||
type DisksType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t DisksType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(DisksType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t DisksType) String() string {
|
||||
return "DisksType"
|
||||
}
|
||||
|
||||
func (t DisksType) 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))
|
||||
}
|
||||
|
||||
idAttribute, ok := attributes["id"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`id is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
idVal, ok := idAttribute.(basetypes.Int64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`id expected to be basetypes.Int64Value, was: %T`, idAttribute))
|
||||
}
|
||||
|
||||
primaryAttribute, ok := attributes["primary"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`primary is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
primaryVal, ok := primaryAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`primary expected to be basetypes.BoolValue, was: %T`, primaryAttribute))
|
||||
}
|
||||
|
||||
sizeGigabytesAttribute, ok := attributes["size_gigabytes"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`size_gigabytes is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
sizeGigabytesVal, ok := sizeGigabytesAttribute.(basetypes.Float64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`size_gigabytes expected to be basetypes.Float64Value, was: %T`, sizeGigabytesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return DisksValue{
|
||||
Description: descriptionVal,
|
||||
Id: idVal,
|
||||
Primary: primaryVal,
|
||||
SizeGigabytes: sizeGigabytesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewDisksValueNull() DisksValue {
|
||||
return DisksValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDisksValueUnknown() DisksValue {
|
||||
return DisksValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDisksValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (DisksValue, 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 DisksValue Attribute Value",
|
||||
"While creating a DisksValue value, a missing attribute value was detected. "+
|
||||
"A DisksValue 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("DisksValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid DisksValue Attribute Type",
|
||||
"While creating a DisksValue value, an invalid attribute value was detected. "+
|
||||
"A DisksValue 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("DisksValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("DisksValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra DisksValue Attribute Value",
|
||||
"While creating a DisksValue value, an extra attribute value was detected. "+
|
||||
"A DisksValue 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 DisksValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionAttribute, ok := attributes["description"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`description is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
||||
}
|
||||
|
||||
idAttribute, ok := attributes["id"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`id is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
idVal, ok := idAttribute.(basetypes.Int64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`id expected to be basetypes.Int64Value, was: %T`, idAttribute))
|
||||
}
|
||||
|
||||
primaryAttribute, ok := attributes["primary"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`primary is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
primaryVal, ok := primaryAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`primary expected to be basetypes.BoolValue, was: %T`, primaryAttribute))
|
||||
}
|
||||
|
||||
sizeGigabytesAttribute, ok := attributes["size_gigabytes"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`size_gigabytes is missing from object`)
|
||||
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
sizeGigabytesVal, ok := sizeGigabytesAttribute.(basetypes.Float64Value)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`size_gigabytes expected to be basetypes.Float64Value, was: %T`, sizeGigabytesAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewDisksValueUnknown(), diags
|
||||
}
|
||||
|
||||
return DisksValue{
|
||||
Description: descriptionVal,
|
||||
Id: idVal,
|
||||
Primary: primaryVal,
|
||||
SizeGigabytes: sizeGigabytesVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewDisksValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) DisksValue {
|
||||
object, diags := NewDisksValue(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("NewDisksValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t DisksType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewDisksValueNull(), 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 NewDisksValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewDisksValueNull(), 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 NewDisksValueMust(DisksValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t DisksType) ValueType(ctx context.Context) attr.Value {
|
||||
return DisksValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = DisksValue{}
|
||||
|
||||
type DisksValue struct {
|
||||
Description basetypes.StringValue `tfsdk:"description"`
|
||||
Id basetypes.Int64Value `tfsdk:"id"`
|
||||
Primary basetypes.BoolValue `tfsdk:"primary"`
|
||||
SizeGigabytes basetypes.Float64Value `tfsdk:"size_gigabytes"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v DisksValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 4)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["id"] = basetypes.Int64Type{}.TerraformType(ctx)
|
||||
attrTypes["primary"] = basetypes.BoolType{}.TerraformType(ctx)
|
||||
attrTypes["size_gigabytes"] = basetypes.Float64Type{}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 4)
|
||||
|
||||
val, err = v.Description.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["description"] = val
|
||||
|
||||
val, err = v.Id.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["id"] = val
|
||||
|
||||
val, err = v.Primary.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["primary"] = val
|
||||
|
||||
val, err = v.SizeGigabytes.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["size_gigabytes"] = 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 DisksValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v DisksValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v DisksValue) String() string {
|
||||
return "DisksValue"
|
||||
}
|
||||
|
||||
func (v DisksValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"description": basetypes.StringType{},
|
||||
"id": basetypes.Int64Type{},
|
||||
"primary": basetypes.BoolType{},
|
||||
"size_gigabytes": basetypes.Float64Type{},
|
||||
}
|
||||
|
||||
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,
|
||||
"id": v.Id,
|
||||
"primary": v.Primary,
|
||||
"size_gigabytes": v.SizeGigabytes,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v DisksValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(DisksValue)
|
||||
|
||||
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.Id.Equal(other.Id) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Primary.Equal(other.Primary) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.SizeGigabytes.Equal(other.SizeGigabytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v DisksValue) Type(ctx context.Context) attr.Type {
|
||||
return DisksType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v DisksValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"description": basetypes.StringType{},
|
||||
"id": basetypes.Int64Type{},
|
||||
"primary": basetypes.BoolType{},
|
||||
"size_gigabytes": basetypes.Float64Type{},
|
||||
}
|
||||
}
|
||||
|
||||
+18
-11
@@ -8,7 +8,7 @@
|
||||
"url": "https://support.binarylane.com.au",
|
||||
"email": "support@binarylane.com.au"
|
||||
},
|
||||
"version": "0.33.0",
|
||||
"version": "0.32.1",
|
||||
"x-logo": {
|
||||
"url": "https://www.binarylane.com.au/res/images/binarylane/logo.png",
|
||||
"backgroundColor": "#000000",
|
||||
@@ -7995,24 +7995,36 @@
|
||||
"type": "integer",
|
||||
"description": "The ID of this disk.",
|
||||
"format": "int64",
|
||||
"readOnly": true
|
||||
"readOnly": true,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "id"
|
||||
}
|
||||
},
|
||||
"size_gigabytes": {
|
||||
"type": "number",
|
||||
"description": "The size of the disk in GB.",
|
||||
"format": "double",
|
||||
"readOnly": true
|
||||
"readOnly": true,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "size_gigabytes"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "A description of this disk.",
|
||||
"nullable": true,
|
||||
"readOnly": true
|
||||
"readOnly": true,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "description"
|
||||
}
|
||||
},
|
||||
"primary": {
|
||||
"type": "boolean",
|
||||
"description": "A primary disk is treated differently from other disks.",
|
||||
"readOnly": true
|
||||
"readOnly": true,
|
||||
"x-oapi-codegen-extra-tags": {
|
||||
"tfsdk": "primary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -9336,7 +9348,6 @@
|
||||
},
|
||||
"LicensedSoftware": {
|
||||
"required": [
|
||||
"incompatible",
|
||||
"licence_count",
|
||||
"software"
|
||||
],
|
||||
@@ -9354,10 +9365,6 @@
|
||||
"type": "integer",
|
||||
"description": "The current licence count for the software.",
|
||||
"format": "int32"
|
||||
},
|
||||
"incompatible": {
|
||||
"type": "boolean",
|
||||
"description": "Software that is incompatible with the server will be automatically removed at the next plan change.\nServers may have incompatible software due to changes made by support. Software is not incompatible merely because it is disabled;\ndisabled software may be retained by servers that already have it, incompatible software will be removed."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10273,7 +10280,7 @@
|
||||
"$ref": "#/components/schemas/ChangeLicenses"
|
||||
}
|
||||
],
|
||||
"description": "This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed. Any licences that are invalid will be removed regardless, see the documentation for `servers/{server_id}/software` for more information.",
|
||||
"description": "This may be left null to keep the current licenses for the server. If this is provided any licenses that are not included will be removed.",
|
||||
"nullable": true
|
||||
},
|
||||
"pre_action_backup": {
|
||||
|
||||
+39
-7
@@ -834,13 +834,6 @@
|
||||
"description": "The slug of the selected operating system."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ipv6",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If true this will enable IPv6 for this server."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"string": {
|
||||
@@ -1013,6 +1006,45 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "disks",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "description",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "A description of this disk."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed",
|
||||
"description": "The ID of this disk."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "primary",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed",
|
||||
"description": "A primary disk is treated differently from other disks."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size_gigabytes",
|
||||
"float64": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The size of the disk in GB."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "A list of the disks that are currently attached to the server."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ resources:
|
||||
default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to
|
||||
disable deployment of default keys.
|
||||
ignores:
|
||||
- ipv6
|
||||
- licenses
|
||||
- links
|
||||
- server
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "disks",
|
||||
"list_nested": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"nested_object": {
|
||||
"attributes": [
|
||||
{
|
||||
"name": "description",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "A description of this disk."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
"computed_optional_required": "computed",
|
||||
"description": "The ID of this disk."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "primary",
|
||||
"bool": {
|
||||
"computed_optional_required": "computed",
|
||||
"description": "A primary disk is treated differently from other disks."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "size_gigabytes",
|
||||
"float64": {
|
||||
"computed_optional_required": "required",
|
||||
"description": "The size of the disk in GB."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "A list of the disks that are currently attached to the server."
|
||||
}
|
||||
}
|
||||
@@ -5,3 +5,6 @@ GENERATOR_CONFIG=$(dirname "$0")/../provider_code_spec.json
|
||||
# Server configuration
|
||||
ADVANCED_FEATURES_CONFIG=$(dirname "$0")/data/server_advanced_features.json
|
||||
cat <<<$(jq --tab --slurpfile adv_feat_cfg $ADVANCED_FEATURES_CONFIG '.resources[1].schema.attributes |= . + $adv_feat_cfg' $GENERATOR_CONFIG) >$GENERATOR_CONFIG
|
||||
|
||||
DISKS_CONFIG=$(dirname "$0")/data/server_disks.json
|
||||
cat <<<$(jq --tab --slurpfile disks_cfg $DISKS_CONFIG '.resources[1].schema.attributes |= . + $disks_cfg' $GENERATOR_CONFIG) >$GENERATOR_CONFIG
|
||||
|
||||
@@ -142,5 +142,11 @@ cat <<<$(jq '.components.schemas.SizeType.properties.slug += {"x-oapi-codegen-ex
|
||||
cat <<<$(jq '.components.schemas.SizeType.properties.name += {"x-oapi-codegen-extra-tags": {"tfsdk": "name"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.SizeType.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
## Disks
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.id += {"x-oapi-codegen-extra-tags": {"tfsdk": "id"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.description += {"x-oapi-codegen-extra-tags": {"tfsdk": "description"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.primary += {"x-oapi-codegen-extra-tags": {"tfsdk": "primary"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
cat <<<$(jq '.components.schemas.Disk.properties.size_gigabytes += {"x-oapi-codegen-extra-tags": {"tfsdk": "size_gigabytes"}}' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
# Edit description here because it's hard to override nested schema properties
|
||||
cat <<<$(jq '.components.schemas.ForwardingRule.properties.entry_protocol.description = "The protocol that traffic must match for the load balancer to forward it. Valid values are \"http\" and \"https\"."' $OPENAPI_FILE) >$OPENAPI_FILE
|
||||
|
||||
Reference in New Issue
Block a user