Compare commits

...

4 Commits

Author SHA1 Message Date
Oscar Hermoso 239e48e940 🎒 allow updating of the server backups property (#55) 2024-12-15 09:30:27 +08:00
dependabot[bot] 2221f8c853 🐹 Bump golang.org/x/crypto from 0.29.0 to 0.31.0 (#54)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.29.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.31.0)

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

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


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

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-framework-validators
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-14 08:58:38 +08:00
Oscar Hermoso 7f5bae6b15 💾 allow customising server memory and disk (#52) 2024-12-08 17:09:54 +08:00
8 changed files with 262 additions and 36 deletions
+2
View File
@@ -22,7 +22,9 @@ Provides a Binary Lane Server data source. This can be used to read existing ser
### Read-Only
- `backups` (Boolean) If true this will enable two daily backups for the server. `options.daily_backups` will override this value if provided. Setting this to false has no effect.
- `disk` (Number) The amount of storage in GB assigned to the server.
- `image` (String) The slug of the selected operating system.
- `memory` (Number) The amount of memory in MB assigned to the server.
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
- `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.
+14
View File
@@ -25,6 +25,20 @@ 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.
- `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:
- must be a multiple of 5
- \> 60GB must be a multiple of 10
- \> 200GB must be a multiple of 100
- `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
- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name.
- `password` (String, Sensitive) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions. If omitted and the server supports password change actions a random password will be generated and emailed to the account email address.
- `port_blocking` (Boolean) Port blocking of outgoing connections for email, SSH and Remote Desktop (TCP ports 22, 25, and 3389) is enabled by default for all new servers. If this is false port blocking will be disabled. Disabling port blocking is only available to reviewed accounts.
+5 -5
View File
@@ -9,7 +9,7 @@ require (
github.com/hashicorp/terraform-plugin-docs v0.20.1
github.com/hashicorp/terraform-plugin-framework v1.13.0
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0
github.com/hashicorp/terraform-plugin-go v0.25.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.11.0
@@ -99,13 +99,13 @@ require (
github.com/yuin/goldmark-meta v1.1.0 // indirect
github.com/zclconf/go-cty v1.15.0 // indirect
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.22.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
+10 -10
View File
@@ -151,8 +151,8 @@ github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ
github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaKFhm4h2TgvMUlNzFAtUqlcOWnWPm+9E=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1/go.mod h1:MsjL1sQ9L7wGwzJ5RjcI6FzEMdyoBnw+XK8ZnOvQOLY=
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0 h1:RXMmu7JgpFjnI1a5QjMCBb11usrW2OtAG+iOTIj5c9Y=
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY=
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0 h1:O9QqGoYDzQT7lwTXUsZEtgabeWW96zUBh47Smn2lkFA=
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0/go.mod h1:Bh89/hNmqsEWug4/XWKYBwtnw3tbz5BAy1L1OgvbIaY=
github.com/hashicorp/terraform-plugin-go v0.25.0 h1:oi13cx7xXA6QciMcpcFi/rwA974rdTxjqEhXJjbAyks=
github.com/hashicorp/terraform-plugin-go v0.25.0/go.mod h1:+SYagMYadJP86Kvn+TGeV+ofr/R3g4/If0O5sO96MVw=
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
@@ -319,8 +319,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -343,8 +343,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -370,8 +370,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -382,8 +382,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+18
View File
@@ -30,6 +30,8 @@ type serverDataModel struct {
PublicIpv4Addresses types.List `tfsdk:"public_ipv4_addresses"`
PrivateIPv4Addresses types.List `tfsdk:"private_ipv4_addresses"`
Permalink types.String `tfsdk:"permalink"`
Memory types.Int32 `tfsdk:"memory"`
Disk types.Int32 `tfsdk:"disk"`
}
func (d *serverDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
@@ -98,6 +100,20 @@ func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequ
ElementType: types.StringType,
Computed: true,
}
memoryDescription := "The amount of memory in MB assigned to the server."
resp.Schema.Attributes["memory"] = &schema.Int32Attribute{
Description: memoryDescription,
MarkdownDescription: memoryDescription,
Computed: true,
}
diskDescription := "The amount of storage in GB assigned to the server."
resp.Schema.Attributes["disk"] = &schema.Int32Attribute{
Description: diskDescription,
MarkdownDescription: diskDescription,
Computed: true,
}
}
func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
@@ -137,6 +153,8 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
data.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
data.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
data.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
publicIpv4Addresses := []string{}
privateIpv4Addresses := []string{}
+144 -20
View File
@@ -116,10 +116,9 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
resp.Schema.Attributes["backups"] = &schema.BoolAttribute{
Description: backups.GetDescription(),
MarkdownDescription: backups.GetMarkdownDescription(),
Optional: backups.IsOptional(),
Computed: backups.IsComputed(),
Default: booldefault.StaticBool(false), // Add default to backups
PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()}, // TODO (#37)
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false), // Add default to backups
}
user_data := resp.Schema.Attributes["user_data"]
@@ -185,6 +184,7 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
// Default: int32default.StaticInt32(0), // TODO: Uncomment with 1.0 release (see issue #30)
Validators: []validator.Int32{
int32validator.AtLeast(0),
int32validator.AtMost(8),
},
}
@@ -260,6 +260,58 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r
},
}
memoryDescription := `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.`
memoryValidValues := "Valid values must be a multiple of 128. If the value is greater than 2048MB, it must be a " +
"multiple of 1024. If the value is greater than 16384MB, it must be a multiple of 2048. If the value is greater " +
"than 24576MB, it must be a multiple of 4096."
memoryValidValuesMarkdown := ` 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`
resp.Schema.Attributes["memory"] = &schema.Int32Attribute{
Description: memoryDescription + memoryValidValues,
MarkdownDescription: memoryDescription + memoryValidValuesMarkdown,
Optional: true,
Required: false,
Computed: true,
Validators: []validator.Int32{
int32validator.AtLeast(128),
MultipleOfValidator{Multiple: 128},
MultipleOfValidator{Multiple: 1024, RangeFrom: 2048, RangeTo: 16384},
MultipleOfValidator{Multiple: 2048, RangeFrom: 16384, RangeTo: 24576},
MultipleOfValidator{Multiple: 4096, RangeFrom: 24576},
},
}
diskDescription := `The total storage in GB for this server. 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.`
diskValidValues := "Valid values must be a multiple of 5. If the value is greater than 60GB, it must be a multiple of 10. " +
"if the value is greater than 200GB, it must be a multiple of 100. "
diskValidValuesMarkdown := ` Valid values:
- must be a multiple of 5
- \> 60GB must be a multiple of 10
- \> 200GB must be a multiple of 100`
resp.Schema.Attributes["disk"] = &schema.Int32Attribute{
Description: diskDescription + diskValidValues,
MarkdownDescription: diskDescription + diskValidValuesMarkdown,
Optional: true,
Required: false,
Computed: true,
Validators: []validator.Int32{
int32validator.AtLeast(20),
MultipleOfValidator{Multiple: 5},
MultipleOfValidator{Multiple: 10, RangeFrom: 60, RangeTo: 200},
MultipleOfValidator{Multiple: 100, RangeFrom: 200},
},
}
resp.Schema.Attributes["timeouts"] =
timeouts.Attributes(ctx, timeouts.Opts{
Create: true,
@@ -274,6 +326,7 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
// Destruction plan, no modification needed
return
}
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
@@ -293,6 +346,7 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
// Creation plan, no further modification needed
return
}
// Read Terraform state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
@@ -335,6 +389,14 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
)
}
// Use state for unknown disk/memory values, as long as server size is the same
if (plan.Memory.IsNull() || plan.Memory.IsUnknown()) && plan.Size.Equal(state.Size) {
plan.Memory = state.Memory
}
if (plan.Disk.IsNull() || plan.Disk.IsUnknown()) && plan.Size.Equal(state.Size) {
plan.Disk = state.Disk
}
// Save data into Terraform state
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
}
@@ -376,6 +438,14 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
Options: &binarylane.SizeOptionsRequest{
Ipv4Addresses: data.PublicIpv4Count.ValueInt32Pointer(),
},
Backups: data.Backups.ValueBoolPointer(),
}
if !data.Memory.IsNull() && !data.Memory.IsUnknown() {
body.Options.Memory = data.Memory.ValueInt32Pointer()
}
if !data.Disk.IsNull() && !data.Disk.IsUnknown() {
body.Options.Disk = data.Disk.ValueInt32Pointer()
}
if data.Password.IsNull() {
@@ -430,6 +500,8 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
data.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
data.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
data.PasswordChangeSupported = types.BoolValue(*serverResp.JSON200.Server.PasswordChangeSupported)
data.Memory = types.Int32Value(*serverResp.JSON200.Server.Memory)
data.Disk = types.Int32Value(*serverResp.JSON200.Server.Disk)
plannedSourceDestCheck := data.SourceAndDestinationCheck
serverRespSourceDestCheck := types.BoolPointerValue(serverResp.JSON200.Server.Networks.SourceAndDestinationCheck)
data.SourceAndDestinationCheck = serverRespSourceDestCheck
@@ -636,18 +708,42 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
}
// Resize operation
if !plan.Size.Equal(state.Size) || !plan.PublicIpv4Count.Equal(state.PublicIpv4Count) || !plan.Image.Equal(state.Image) {
if !plan.Size.Equal(state.Size) ||
!plan.Memory.IsNull() && !plan.Memory.IsUnknown() && !plan.Memory.Equal(state.Memory) ||
!plan.Disk.IsNull() && !plan.Disk.IsUnknown() && !plan.Disk.Equal(state.Disk) ||
!plan.Image.Equal(state.Image) ||
!plan.PublicIpv4Count.Equal(state.PublicIpv4Count) {
resizeReq := &binarylane.PostServersServerIdActionsResizeJSONRequestBody{
Type: "resize",
Type: "resize",
Options: &binarylane.ChangeSizeOptionsRequest{},
}
if !plan.Size.Equal(state.Size) {
if !plan.Size.Equal(state.Size) ||
!plan.Memory.IsNull() && !plan.Memory.IsUnknown() && !plan.Memory.Equal(state.Memory) ||
!plan.Disk.IsNull() && !plan.Disk.IsUnknown() && !plan.Disk.Equal(state.Disk) {
resizeReq.Size = plan.Size.ValueStringPointer()
state.Size = plan.Size
}
if !plan.PublicIpv4Count.Equal(state.PublicIpv4Count) {
resizeReq.Options = &binarylane.ChangeSizeOptionsRequest{
Ipv4Addresses: plan.PublicIpv4Count.ValueInt32Pointer(),
if !plan.Memory.IsUnknown() && !plan.Memory.IsNull() {
resizeReq.Options.Memory = plan.Memory.ValueInt32Pointer()
}
if !plan.Disk.IsNull() && !plan.Disk.IsUnknown() {
resizeReq.Options.Disk = plan.Disk.ValueInt32Pointer()
}
state.Size = plan.Size
state.Memory = plan.Memory
state.Disk = plan.Disk
}
if !plan.Image.Equal(state.Image) {
resizeReq.ChangeImage = &binarylane.ChangeImage{
Image: plan.Image.ValueStringPointer(),
}
state.Image = plan.Image
}
if !plan.PublicIpv4Count.Equal(state.PublicIpv4Count) {
resizeReq.Options.Ipv4Addresses = plan.PublicIpv4Count.ValueInt32Pointer()
if plan.PublicIpv4Count.ValueInt32() < state.PublicIpv4Count.ValueInt32() {
currentIps := []string{}
diags := state.PublicIpv4Addresses.ElementsAs(ctx, &currentIps, false)
@@ -665,12 +761,6 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
state.PublicIpv4Count = plan.PublicIpv4Count
state.PublicIpv4Addresses = plan.PublicIpv4Addresses
}
if !plan.Image.Equal(state.Image) {
resizeReq.ChangeImage = &binarylane.ChangeImage{
Image: plan.Image.ValueStringPointer(),
}
state.Image = plan.Image
}
tflog.Info(ctx, fmt.Sprintf("Resizing server: server_id=%s", state.Id.String()))
@@ -699,8 +789,7 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
return
}
if state.PublicIpv4Addresses.IsUnknown() || listContainsUnknown(ctx, state.PublicIpv4Addresses) {
// New IPs may have been allocated, so we need to check the server again
if state.PublicIpv4Addresses.IsUnknown() || listContainsUnknown(ctx, state.PublicIpv4Addresses) || state.Memory.IsNull() || state.Memory.IsUnknown() {
refreshNeeded = true
}
}
@@ -771,6 +860,38 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
// Enable or disable backups
if !plan.Backups.Equal(state.Backups) {
if plan.Backups.ValueBool() {
backupResp, err := r.bc.client.PostServersServerIdActionsEnableBackupsWithResponse(ctx, state.Id.ValueInt64(), binarylane.EnableBackups{
Type: "enable_backups",
})
if err != nil {
resp.Diagnostics.AddError("Error enabling backups", err.Error())
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *backupResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for backups to be enabled", err.Error())
return
}
} else {
backupResp, err := r.bc.client.PostServersServerIdActionsDisableBackupsWithResponse(ctx, state.Id.ValueInt64(), binarylane.DisableBackups{
Type: "disable_backups",
})
if err != nil {
resp.Diagnostics.AddError("Error disabling backups", err.Error())
return
}
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), *backupResp.JSON200.Action.Id)
if err != nil {
resp.Diagnostics.AddError("Error waiting for backups to be disabled", err.Error())
return
}
}
state.Backups = plan.Backups
}
}
func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
@@ -949,6 +1070,9 @@ func setServerResourceState(ctx context.Context, data *serverResourceModel, serv
data.Permalink = types.StringValue(*serverResp.Server.Permalink)
data.PasswordChangeSupported = types.BoolValue(*serverResp.Server.PasswordChangeSupported)
data.SourceAndDestinationCheck = types.BoolPointerValue(serverResp.Server.Networks.SourceAndDestinationCheck)
data.Memory = types.Int32Value(*serverResp.Server.Memory)
data.Disk = types.Int32Value(*serverResp.Server.Disk)
data.Backups = types.BoolValue(serverResp.Server.NextBackupWindow != nil)
publicIpv4Addresses := []string{}
privateIpv4Addresses := []string{}
+14 -1
View File
@@ -56,11 +56,13 @@ resource "binarylane_server" "test" {
region = "per"
image = "debian-11"
size = "std-min"
memory = 1152
password = "` + password + `"
vpc_id = binarylane_vpc.test.id
public_ipv4_count = 1
ssh_keys = [binarylane_ssh_key.initial.id]
source_and_destination_check = false
backups = true
user_data = <<EOT
#cloud-config
echo "Hello World" > /var/tmp/output.txt
@@ -80,6 +82,8 @@ data "binarylane_server" "test" {
resource.TestCheckResourceAttr("binarylane_server.test", "region", "per"),
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-11"),
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"),
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "1152"),
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "20"),
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_id"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "1"),
resource.TestCheckResourceAttr("binarylane_server.test", "password", password),
@@ -93,6 +97,7 @@ echo "Hello World" > /var/tmp/output.txt
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.initial", "id"),
resource.TestCheckResourceAttrSet("binarylane_server.test", "permalink"),
resource.TestCheckResourceAttr("binarylane_server.test", "source_and_destination_check", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "true"),
// Verify data source values
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "id", "binarylane_server.test", "id"),
@@ -105,6 +110,9 @@ echo "Hello World" > /var/tmp/output.txt
resource.TestCheckResourceAttr("data.binarylane_server.test", "user_data", `#cloud-config
echo "Hello World" > /var/tmp/output.txt
`),
resource.TestCheckResourceAttr("data.binarylane_server.test", "memory", "1152"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "disk", "20"),
resource.TestCheckResourceAttr("data.binarylane_server.test", "backups", "true"),
),
},
// Test import by ID
@@ -147,11 +155,13 @@ resource "binarylane_server" "test" {
region = "per"
image = "debian-12"
size = "std-1vcpu"
disk = "45"
password = "` + password + `"
vpc_id = null
public_ipv4_count = 0
ssh_keys = [binarylane_ssh_key.updated.id]
# source_and_destination_check = null # defaults to null when vpc_id is null
# source_and_destination_check = null # defaults to null when vpc_id is not set
# backups = false # defaults to false when backups is not set
user_data = <<EOT
#cloud-config
echo "Hello Whitespace" > /var/tmp/output.txt
@@ -163,6 +173,8 @@ EOT
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-test-server-resource-2"),
resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-1vcpu"),
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "2048"),
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "45"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "0"),
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_addresses.#", "0"),
resource.TestCheckResourceAttr("binarylane_server.test", "image", "debian-12"),
@@ -170,6 +182,7 @@ EOT
resource.TestCheckResourceAttr("binarylane_server.test", "ssh_keys.#", "1"),
resource.TestCheckResourceAttrPair("binarylane_server.test", "ssh_keys.0", "binarylane_ssh_key.updated", "id"),
resource.TestCheckNoResourceAttr("binarylane_server.test", "source_and_destination_check"),
resource.TestCheckResourceAttr("binarylane_server.test", "backups", "false"),
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", // test extra whitespace
`#cloud-config
echo "Hello Whitespace" > /var/tmp/output.txt
+55
View File
@@ -0,0 +1,55 @@
package provider
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
)
var (
_ validator.Int32 = &MultipleOfValidator{}
)
type MultipleOfValidator struct {
Multiple, RangeFrom, RangeTo int32
}
func (v MultipleOfValidator) Description(ctx context.Context) string {
if v.RangeFrom == 0 {
return fmt.Sprintf(`must be a multiple of %d`, v.Multiple)
}
return fmt.Sprintf("when greater than %d, must be a multiple of %d", v.RangeFrom, v.Multiple)
}
func (v MultipleOfValidator) MarkdownDescription(ctx context.Context) string {
if v.RangeFrom == 0 {
return fmt.Sprintf(`must be a multiple of %d`, v.Multiple)
}
return fmt.Sprintf("when greater than %d, must be a multiple of %d", v.RangeFrom, v.Multiple)
}
func (v MultipleOfValidator) ValidateInt32(ctx context.Context, req validator.Int32Request, resp *validator.Int32Response) {
if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() {
return
}
memory := req.ConfigValue.ValueInt32()
if memory%v.Multiple == 0 || v.RangeFrom != 0 && memory < v.RangeFrom || v.RangeTo != 0 && memory >= v.RangeTo {
return
}
if v.RangeFrom != 0 {
resp.Diagnostics.AddAttributeError(
req.Path,
"Not a Multiple",
fmt.Sprintf("if greater than %d, value must be a multiple of %d", v.RangeFrom, v.Multiple),
)
} else {
resp.Diagnostics.AddAttributeError(
req.Path,
"Not a Multiple",
fmt.Sprintf("value must be a multiple of %d", v.Multiple),
)
}
}