mirror of
https://github.com/oscarhermoso/terraform-provider-binarylane
synced 2026-10-20 05:56:05 +11:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 05a4a263ea | |||
| 7fa9d7f81f | |||
| 6ebc900a91 | |||
| 51b530bc89 | |||
| a109a41bba | |||
| cc03603386 | |||
| e15930ed7e |
@@ -53,7 +53,6 @@ data "binarylane_server" "example" {
|
||||
- `ssh_keys` (List of Number) This is a list of SSH key ids. If this is null or not provided, any SSH keys that have been marked as default will be deployed (assuming the operating system supports SSH Keys). Submit an empty list to disable deployment of default keys.
|
||||
- `user_data` (String) A script or cloud-config YAML file to configure the server. Can only be specified if the OS image supports UserData (i.e. not Windows). See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
- `vpc_ipv4_address` (String) If provided this will be the IPv4 address for the server's private VPC network adapter. If this is unspecified, then an unused IPv4 address will be assigned. This field is only valid when `vpc_id` is provided.
|
||||
|
||||
<a id="nestedatt--advanced_features"></a>
|
||||
### Nested Schema for `advanced_features`
|
||||
|
||||
@@ -58,7 +58,6 @@ resource "binarylane_server" "example" {
|
||||
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
|
||||
- `user_data` (String) A script or cloud-config YAML file to configure the server. Can only be specified if the OS image supports UserData (i.e. not Windows). See more: https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script
|
||||
- `vpc_id` (Number) Leave null to use default (public) network for the selected region.
|
||||
- `vpc_ipv4_address` (String) If provided this will be the IPv4 address for the server's private VPC network adapter. If this is unspecified, then an unused IPv4 address will be assigned. This field is only valid when `vpc_id` is provided.
|
||||
|
||||
### Read-Only
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
*.tfvars
|
||||
*.tfvars.json
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Ignore transient lock info files created by terraform apply
|
||||
.terraform.tfstate.lock.info
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# Ignore SSH keys
|
||||
.id_ed25519*
|
||||
.kube
|
||||
@@ -438,7 +438,7 @@ type Account struct {
|
||||
// TaxCode The tax code that currently applies to transactions for this account.
|
||||
TaxCode TaxCode `json:"tax_code"`
|
||||
|
||||
// TwoFactorAuthenticationEnabled Whether this account has enabled app-based two factor authentication.
|
||||
// TwoFactorAuthenticationEnabled Whether this account has enabled two factor authentication.
|
||||
TwoFactorAuthenticationEnabled bool `json:"two_factor_authentication_enabled"`
|
||||
}
|
||||
|
||||
@@ -1250,9 +1250,6 @@ type CreateServerRequest struct {
|
||||
|
||||
// VpcId Leave null to use default (public) network for the selected region.
|
||||
VpcId *int64 `json:"vpc_id"`
|
||||
|
||||
// VpcIpv4Address If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided.
|
||||
VpcIpv4Address *string `json:"vpc_ipv4_address"`
|
||||
}
|
||||
|
||||
// CreateServerResponse defines model for CreateServerResponse.
|
||||
|
||||
@@ -182,7 +182,7 @@ func init() {
|
||||
return fmt.Errorf("Unexpected status code getting load balancers for test sweep: %s", listResp.Body)
|
||||
}
|
||||
|
||||
loadBalancers := listResp.JSON200.LoadBalancers
|
||||
loadBalancers := listResp.JSON200.LoadBalancers
|
||||
for _, lb := range loadBalancers {
|
||||
if strings.HasPrefix(lb.Name, "tf-test-") {
|
||||
deleteResp, err := client.DeleteLoadBalancersLoadBalancerIdWithResponse(ctx, lb.Id)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/resources"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
@@ -108,30 +107,18 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
|
||||
data.Id = types.Int64Value(serverResp.JSON200.Server.Id)
|
||||
data.Name = types.StringValue(serverResp.JSON200.Server.Name)
|
||||
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
|
||||
data.Image = types.StringPointerValue(serverResp.JSON200.Server.Image.Slug)
|
||||
data.Region = types.StringValue(serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(serverResp.JSON200.Server.Size.Slug)
|
||||
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
data.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)
|
||||
data.Permalink = types.StringPointerValue(serverResp.JSON200.Server.Permalink)
|
||||
data.Memory = types.Int32Value(serverResp.JSON200.Server.Memory)
|
||||
data.Disk = types.Int32Value(serverResp.JSON200.Server.Disk)
|
||||
data.SourceAndDestinationCheck = types.BoolPointerValue(serverResp.JSON200.Server.Networks.SourceAndDestinationCheck)
|
||||
|
||||
if serverResp.JSON200.Server.VpcId == nil {
|
||||
data.VpcIpv4Address = types.StringNull()
|
||||
} else if len(serverResp.JSON200.Server.Networks.V4) > 0 {
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
// Skip addresses in 172.21.0.0/16, these are BL internal addresses that are not part of the user's VPC
|
||||
if v4address.Type == "private" && !strings.HasPrefix(v4address.IpAddress, "172.21.") {
|
||||
data.VpcIpv4Address = types.StringValue(v4address.IpAddress)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
advFeat := serverResp.JSON200.Server.AdvancedFeatures.EnabledAdvancedFeatures
|
||||
data.AdvancedFeatures, diags = resources.NewAdvancedFeaturesValue(
|
||||
resources.AdvancedFeaturesValue{}.AttributeTypes(ctx),
|
||||
|
||||
@@ -121,6 +121,29 @@ func (r *serverFirewallRulesResource) Create(ctx context.Context, req resource.C
|
||||
return
|
||||
}
|
||||
|
||||
// Read back the firewall rules to get the actual state from the API
|
||||
readResp, err := r.bc.client.GetServersServerIdAdvancedFirewallRulesWithResponse(ctx, data.ServerId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server firewall rules after create: server_id=%d", data.ServerId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if readResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading server firewall rules after create",
|
||||
fmt.Sprintf("Received %s reading server firewall rules: server_id=%d. Details: %s", readResp.Status(), data.ServerId.ValueInt64(), readResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
firewallRulesValue, diags := GetFirewallRulesState(ctx, &readResp.JSON200.FirewallRules)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.FirewallRules = firewallRulesValue
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
@@ -207,6 +230,29 @@ func (r *serverFirewallRulesResource) Update(ctx context.Context, req resource.U
|
||||
return
|
||||
}
|
||||
|
||||
// Read back the firewall rules to get the actual state from the API
|
||||
readResp, err := r.bc.client.GetServersServerIdAdvancedFirewallRulesWithResponse(ctx, data.ServerId.ValueInt64())
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error reading server firewall rules after update: server_id=%d", data.ServerId.ValueInt64()),
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if readResp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code reading server firewall rules after update",
|
||||
fmt.Sprintf("Received %s reading server firewall rules: server_id=%d. Details: %s", readResp.Status(), data.ServerId.ValueInt64(), readResp.Body))
|
||||
return
|
||||
}
|
||||
|
||||
firewallRulesValue, diags := GetFirewallRulesState(ctx, &readResp.JSON200.FirewallRules)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
data.FirewallRules = firewallRulesValue
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
@@ -262,6 +308,8 @@ func GetFirewallRulesState(ctx context.Context, firewallRules *[]binarylane.Adva
|
||||
if rule.DestinationPorts != nil {
|
||||
destinationPorts, diag = types.ListValueFrom(ctx, types.StringType, rule.DestinationPorts)
|
||||
diags.Append(diag...)
|
||||
} else {
|
||||
destinationPorts = types.ListNull(types.StringType)
|
||||
}
|
||||
|
||||
sourceAddresses, diag := types.ListValueFrom(ctx, types.StringType, rule.SourceAddresses)
|
||||
|
||||
@@ -126,6 +126,47 @@ resource "binarylane_server_firewall_rules" "test" {
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
),
|
||||
},
|
||||
// Test ICMP rule without destination_ports
|
||||
{
|
||||
Config: providerConfig + `
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-fw-rules"
|
||||
region = "per"
|
||||
image = "debian-12"
|
||||
size = "std-min"
|
||||
password = "` + password + `"
|
||||
public_ipv4_count = 0
|
||||
}
|
||||
|
||||
resource "binarylane_server_firewall_rules" "test" {
|
||||
server_id = binarylane_server.test.id
|
||||
firewall_rules = [
|
||||
{
|
||||
description = "Allow ICMP"
|
||||
protocol = "icmp"
|
||||
source_addresses = ["0.0.0.0/0"]
|
||||
destination_addresses = [binarylane_server.test.private_ipv4_addresses.0]
|
||||
action = "accept"
|
||||
# destination_ports intentionally not specified for ICMP
|
||||
},
|
||||
]
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Verify resource values with ICMP and no destination_ports
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "server_id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.description", "Allow ICMP"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.protocol", "icmp"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_addresses.0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.destination_ports.#", "0"),
|
||||
resource.TestCheckResourceAttr("binarylane_server_firewall_rules.test", "firewall_rules.0.action", "accept"),
|
||||
),
|
||||
},
|
||||
// Delete testing automatically occurs in TestCase
|
||||
},
|
||||
})
|
||||
|
||||
@@ -186,9 +186,10 @@ func serverSchema(ctx context.Context) schema.Schema {
|
||||
}
|
||||
|
||||
// Additional attributes
|
||||
pwDescription := "If this is provided the specified or default remote user's account password will be set to this value. " +
|
||||
"Only valid if the server supports password change actions. If omitted and the server supports password " +
|
||||
"change actions a random password will be generated and emailed to the account email address."
|
||||
pwDescription :=
|
||||
"If this is provided the specified or default remote user's account password will be set to this value. " +
|
||||
"Only valid if the server supports password change actions. If omitted and the server supports password " +
|
||||
"change actions a random password will be generated and emailed to the account email address."
|
||||
s.Attributes["password"] = schema.StringAttribute{
|
||||
Description: pwDescription,
|
||||
MarkdownDescription: pwDescription,
|
||||
@@ -363,29 +364,11 @@ func serverSchema(ctx context.Context) schema.Schema {
|
||||
},
|
||||
}
|
||||
|
||||
s.Attributes["timeouts"] = timeouts.Attributes(ctx, timeouts.Opts{
|
||||
Create: true,
|
||||
Update: true,
|
||||
})
|
||||
|
||||
vpcIpv4Addr := s.Attributes["vpc_ipv4_address"]
|
||||
vpcIpv4AddrDescription := "If provided this will be the IPv4 address for the server's private VPC network adapter." +
|
||||
" If this is unspecified, then an unused IPv4 address will be assigned. This field is only valid when `vpc_id`" +
|
||||
" is provided."
|
||||
s.Attributes["vpc_ipv4_address"] = schema.StringAttribute{
|
||||
Description: vpcIpv4AddrDescription,
|
||||
MarkdownDescription: vpcIpv4AddrDescription,
|
||||
Optional: vpcIpv4Addr.IsOptional(),
|
||||
Computed: vpcIpv4Addr.IsComputed(),
|
||||
Validators: []validator.String{
|
||||
stringvalidator.AlsoRequires(path.Expressions{
|
||||
path.MatchRoot("vpc_id"),
|
||||
}...),
|
||||
},
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
}
|
||||
s.Attributes["timeouts"] =
|
||||
timeouts.Attributes(ctx, timeouts.Opts{
|
||||
Create: true,
|
||||
Update: true,
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
@@ -418,15 +401,6 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
if plan.VpcIpv4Address.IsUnknown() {
|
||||
if plan.VpcId.IsNull() {
|
||||
plan.VpcIpv4Address = types.StringNull()
|
||||
} else {
|
||||
plan.VpcIpv4Address = types.StringUnknown()
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
if req.State.Raw.IsNull() {
|
||||
// Creation plan, no further modification needed
|
||||
return
|
||||
@@ -438,10 +412,7 @@ func (r *serverResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan
|
||||
return
|
||||
}
|
||||
|
||||
if !plan.VpcId.Equal(state.VpcId) || !plan.VpcIpv4Address.Equal(state.VpcIpv4Address) {
|
||||
if config.VpcIpv4Address.IsNull() {
|
||||
plan.VpcIpv4Address = types.StringUnknown()
|
||||
}
|
||||
if !plan.VpcId.Equal(state.VpcId) {
|
||||
plan.PrivateIPv4Addresses = types.ListUnknown(plan.PrivateIPv4Addresses.ElementType(ctx))
|
||||
}
|
||||
|
||||
@@ -609,9 +580,7 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
if !data.Disk.IsNull() && !data.Disk.IsUnknown() {
|
||||
body.Options.Disk = data.Disk.ValueInt32Pointer()
|
||||
}
|
||||
if !data.VpcIpv4Address.IsNull() && !data.VpcIpv4Address.IsUnknown() {
|
||||
body.VpcIpv4Address = data.VpcIpv4Address.ValueStringPointer()
|
||||
}
|
||||
|
||||
if data.Password.IsNull() {
|
||||
data.Password = types.StringNull()
|
||||
} else {
|
||||
@@ -656,14 +625,14 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
|
||||
data.Id = types.Int64Value(serverResp.JSON200.Server.Id)
|
||||
data.Name = types.StringValue(serverResp.JSON200.Server.Name)
|
||||
data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
|
||||
data.Image = types.StringPointerValue(serverResp.JSON200.Server.Image.Slug)
|
||||
data.Region = types.StringValue(serverResp.JSON200.Server.Region.Slug)
|
||||
data.Size = types.StringValue(serverResp.JSON200.Server.Size.Slug)
|
||||
data.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
data.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)
|
||||
data.Permalink = types.StringPointerValue(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)
|
||||
@@ -671,18 +640,6 @@ func (r *serverResource) Create(ctx context.Context, req resource.CreateRequest,
|
||||
serverRespSourceDestCheck := types.BoolPointerValue(serverResp.JSON200.Server.Networks.SourceAndDestinationCheck)
|
||||
data.SourceAndDestinationCheck = serverRespSourceDestCheck
|
||||
|
||||
if serverResp.JSON200.Server.VpcId == nil {
|
||||
data.VpcIpv4Address = types.StringNull()
|
||||
} else if len(serverResp.JSON200.Server.Networks.V4) > 0 {
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
// Skip addresses in 172.21.0.0/16, these are BL internal addresses that are not part of the user's VPC
|
||||
if v4address.Type == "private" && !strings.HasPrefix(v4address.IpAddress, "172.21.") {
|
||||
data.VpcIpv4Address = types.StringValue(v4address.IpAddress)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
advFeat := serverResp.JSON200.Server.AdvancedFeatures.EnabledAdvancedFeatures
|
||||
data.AdvancedFeatures, diags = resources.NewAdvancedFeaturesValue(
|
||||
resources.AdvancedFeaturesValue{}.AttributeTypes(ctx),
|
||||
@@ -886,55 +843,6 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Change VPC IPv4 address
|
||||
if !plan.VpcIpv4Address.Equal(state.VpcIpv4Address) && !plan.VpcIpv4Address.IsNull() && !plan.VpcIpv4Address.IsUnknown() {
|
||||
// If VPC was just changed, IP address needs to be fetched so it can be sent in the request
|
||||
if refreshNeeded {
|
||||
diag := r.fetchServerResourceState(ctx, &state)
|
||||
resp.Diagnostics.Append(diag...)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// We might have got lucky - the new randomly assigned IP address might be the one we want, so check before making API call
|
||||
if state.VpcIpv4Address.ValueString() != plan.VpcIpv4Address.ValueString() {
|
||||
vpcIpv4Resp, err := r.bc.client.PostServersServerIdActionsChangeVpcIpv4WithResponse(
|
||||
ctx,
|
||||
state.Id.ValueInt64(),
|
||||
binarylane.PostServersServerIdActionsChangeVpcIpv4JSONRequestBody{
|
||||
Type: "change_vpc_ipv4",
|
||||
CurrentIpv4Address: state.VpcIpv4Address.ValueString(),
|
||||
NewIpv4Address: plan.VpcIpv4Address.ValueString(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
fmt.Sprintf("Error changing VPC IPv4 address for server: server_id=%s", state.Id.String()),
|
||||
err.Error())
|
||||
return
|
||||
}
|
||||
if vpcIpv4Resp.StatusCode() != http.StatusOK {
|
||||
resp.Diagnostics.AddError(
|
||||
"Unexpected HTTP status code changing VPC IPv4 address for server",
|
||||
fmt.Sprintf("Received %s changing VPC IPv4 address for server: server_id=%s. Details: %s", vpcIpv4Resp.Status(), state.Id.String(), vpcIpv4Resp.Body))
|
||||
return
|
||||
}
|
||||
err = r.waitForServerAction(ctx, state.Id.ValueInt64(), vpcIpv4Resp.JSON200.Action.Id)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error waiting for VPC IPv4 address to change", err.Error())
|
||||
return
|
||||
}
|
||||
state.VpcIpv4Address = plan.VpcIpv4Address
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
refreshNeeded = true // Refresh to populate private_ipv4_addresses
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resize operation
|
||||
if !plan.Size.Equal(state.Size) ||
|
||||
!plan.Memory.IsNull() && !plan.Memory.IsUnknown() && !plan.Memory.Equal(state.Memory) ||
|
||||
@@ -1028,7 +936,7 @@ func (r *serverResource) Update(ctx context.Context, req resource.UpdateRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Change IPv6
|
||||
// Change ipv6
|
||||
if !plan.Ipv6.Equal(state.Ipv6) {
|
||||
ipv6Resp, err := r.bc.client.PostServersServerIdActionsChangeIpv6WithResponse(
|
||||
ctx,
|
||||
@@ -1421,13 +1329,13 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
|
||||
|
||||
state.Id = types.Int64Value(serverResp.JSON200.Server.Id)
|
||||
state.Name = types.StringValue(serverResp.JSON200.Server.Name)
|
||||
state.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug)
|
||||
state.Image = types.StringPointerValue(serverResp.JSON200.Server.Image.Slug)
|
||||
state.Region = types.StringValue(serverResp.JSON200.Server.Region.Slug)
|
||||
state.Size = types.StringValue(serverResp.JSON200.Server.Size.Slug)
|
||||
state.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
state.PortBlocking = types.BoolValue(serverResp.JSON200.Server.Networks.PortBlocking)
|
||||
state.VpcId = types.Int64PointerValue(serverResp.JSON200.Server.VpcId)
|
||||
state.Permalink = types.StringValue(*serverResp.JSON200.Server.Permalink)
|
||||
state.Permalink = types.StringPointerValue(serverResp.JSON200.Server.Permalink)
|
||||
state.PasswordChangeSupported = types.BoolValue(serverResp.JSON200.Server.PasswordChangeSupported)
|
||||
state.SourceAndDestinationCheck = types.BoolPointerValue(serverResp.JSON200.Server.Networks.SourceAndDestinationCheck)
|
||||
state.Memory = types.Int32Value(serverResp.JSON200.Server.Memory)
|
||||
@@ -1435,18 +1343,6 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
|
||||
state.Backups = types.BoolValue(serverResp.JSON200.Server.NextBackupWindow != nil)
|
||||
state.Ipv6 = types.BoolValue(len(serverResp.JSON200.Server.Networks.V6) > 0)
|
||||
|
||||
if serverResp.JSON200.Server.VpcId == nil {
|
||||
state.VpcIpv4Address = types.StringNull()
|
||||
} else if len(serverResp.JSON200.Server.Networks.V4) > 0 {
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
// Skip addresses in 172.21.0.0/16, these are BL internal addresses that are not part of the user's VPC
|
||||
if v4address.Type == "private" && !strings.HasPrefix(v4address.IpAddress, "172.21.") {
|
||||
state.VpcIpv4Address = types.StringValue(v4address.IpAddress)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
advFeat := serverResp.JSON200.Server.AdvancedFeatures.EnabledAdvancedFeatures
|
||||
state.AdvancedFeatures, diags = resources.NewAdvancedFeaturesValue(
|
||||
resources.AdvancedFeaturesValue{}.AttributeTypes(ctx),
|
||||
@@ -1470,10 +1366,9 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
|
||||
privateIpv4Addresses := []string{}
|
||||
|
||||
for _, v4address := range serverResp.JSON200.Server.Networks.V4 {
|
||||
switch v4address.Type {
|
||||
case "public":
|
||||
if v4address.Type == "public" {
|
||||
publicIpv4Addresses = append(publicIpv4Addresses, v4address.IpAddress)
|
||||
case "private":
|
||||
} else if v4address.Type == "private" {
|
||||
privateIpv4Addresses = append(privateIpv4Addresses, v4address.IpAddress)
|
||||
}
|
||||
}
|
||||
@@ -1500,10 +1395,9 @@ func (r *serverResource) fetchServerResourceState(ctx context.Context, state *se
|
||||
privateIpv6Addresses := []string{}
|
||||
|
||||
for _, v6address := range serverResp.JSON200.Server.Networks.V6 {
|
||||
switch v6address.Type {
|
||||
case "public":
|
||||
if v6address.Type == "public" {
|
||||
publicIpv6Addresses = append(publicIpv6Addresses, v6address.IpAddress)
|
||||
case "private":
|
||||
} else {
|
||||
privateIpv6Addresses = append(privateIpv6Addresses, v6address.IpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"terraform-provider-binarylane/internal/binarylane"
|
||||
"testing"
|
||||
@@ -82,7 +81,6 @@ data "binarylane_server" "test" {
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "memory", "1152"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "disk", "20"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttrSet("binarylane_server.test", "vpc_ipv4_address"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "public_ipv4_count", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "password", password1),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "user_data", `#cloud-config
|
||||
@@ -116,7 +114,6 @@ echo "Hello World" > /var/tmp/output.txt
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "debian-11"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "size", "std-min"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "vpc_id"),
|
||||
resource.TestCheckResourceAttrSet("data.binarylane_server.test", "vpc_ipv4_address"),
|
||||
resource.TestCheckResourceAttrPair("data.binarylane_server.test", "permalink", "binarylane_server.test", "permalink"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "user_data", `#cloud-config
|
||||
echo "Hello World" > /var/tmp/output.txt
|
||||
@@ -324,153 +321,6 @@ resource "binarylane_server" "test" {
|
||||
})
|
||||
}
|
||||
|
||||
func TestServerVpcIpv4Change(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{
|
||||
// Plan should fail if vpc_ipv4_address is set without vpc_id
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "per"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_ipv4_address = "10.241.69.69"
|
||||
}
|
||||
`,
|
||||
PlanOnly: true,
|
||||
ExpectError: regexp.MustCompile("Attribute \"vpc_id\" must be specified when \"vpc_ipv4_address\" is specified"),
|
||||
},
|
||||
// Setup
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test1" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc" "test2" {
|
||||
name = "tf-test-server-vpcipv4-2"
|
||||
ip_range = "10.241.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "per"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test1.id
|
||||
vpc_ipv4_address = "10.240.0.69"
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "vpc_id", "binarylane_vpc.test1", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "vpc_ipv4_address", "10.240.0.69"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.0", "10.240.0.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "vpc_ipv4_address", "10.240.0.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.0", "10.240.0.69"),
|
||||
),
|
||||
},
|
||||
// Change IP without changing VPC
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test1" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc" "test2" {
|
||||
name = "tf-test-server-vpcipv4-2"
|
||||
ip_range = "10.241.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "per"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test1.id
|
||||
vpc_ipv4_address = "10.240.4.20"
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "vpc_id", "binarylane_vpc.test1", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "vpc_ipv4_address", "10.240.4.20"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.0", "10.240.4.20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "vpc_ipv4_address", "10.240.4.20"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.0", "10.240.4.20"),
|
||||
),
|
||||
},
|
||||
// Change VPC (should also change IP)
|
||||
{
|
||||
Config: providerConfig + `
|
||||
resource "binarylane_vpc" "test1" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
ip_range = "10.240.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_vpc" "test2" {
|
||||
name = "tf-test-server-vpcipv4-2"
|
||||
ip_range = "10.241.0.0/16"
|
||||
}
|
||||
|
||||
resource "binarylane_server" "test" {
|
||||
name = "tf-test-server-vpcipv4-1"
|
||||
region = "per"
|
||||
image = "debian-11"
|
||||
size = "std-min"
|
||||
public_ipv4_count = 0
|
||||
password = "` + password + `"
|
||||
vpc_id = binarylane_vpc.test2.id
|
||||
vpc_ipv4_address = "10.241.69.69"
|
||||
}
|
||||
|
||||
data "binarylane_server" "test" {
|
||||
depends_on = [binarylane_server.test]
|
||||
|
||||
id = binarylane_server.test.id
|
||||
}
|
||||
`,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrPair("binarylane_server.test", "vpc_id", "binarylane_vpc.test2", "id"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "vpc_ipv4_address", "10.241.69.69"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("binarylane_server.test", "private_ipv4_addresses.0", "10.241.69.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "vpc_ipv4_address", "10.241.69.69"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.binarylane_server.test", "private_ipv4_addresses.0", "10.241.69.69"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func GenerateTestPassword(t *testing.T) string {
|
||||
t.Helper()
|
||||
pwBytes := make([]byte, 12)
|
||||
@@ -486,6 +336,7 @@ func init() {
|
||||
Name: "server",
|
||||
F: func(_ string) error {
|
||||
client, err := binarylane.NewClientWithDefaultConfig()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Binary Lane API client: %w", err)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (d *sizesDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
||||
}
|
||||
regResults = append(regResults, listResp.JSON200.Sizes...)
|
||||
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ func (r *sshKeyResource) ImportState(
|
||||
break
|
||||
}
|
||||
}
|
||||
if sshResp.JSON200.Links == nil || sshResp.JSON200.Links.Pages.Next == nil {
|
||||
if sshResp.JSON200.Links == nil || sshResp.JSON200.Links.Pages.Next == nil {
|
||||
nextPage = false
|
||||
break
|
||||
}
|
||||
@@ -326,7 +326,7 @@ func (r *sshKeyResource) ImportState(
|
||||
page++
|
||||
}
|
||||
|
||||
if sshKey.Id == 0 {
|
||||
if false {
|
||||
resp.Diagnostics.AddError(
|
||||
"Could not find SSH key by fingerprint",
|
||||
fmt.Sprintf("Error finding SSH key: fingerprint=%s", req.ID),
|
||||
|
||||
@@ -151,14 +151,14 @@ func init() {
|
||||
for _, k := range keys {
|
||||
if strings.HasPrefix(*k.Name, "tf-test-") {
|
||||
|
||||
deleteResp, err := client.DeleteAccountKeysKeyIdWithResponse(ctx, int(k.Id))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting SSH key %d for test sweep: %w", k.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting SSH key %d for test sweep: %s", deleteResp.StatusCode(), k.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted SSH key for test sweep:", k.Id)
|
||||
deleteResp, err := client.DeleteAccountKeysKeyIdWithResponse(ctx, int(k.Id))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting SSH key %d for test sweep: %w", k.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting SSH key %d for test sweep: %s", deleteResp.StatusCode(), k.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted SSH key for test sweep:", k.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
|
||||
@@ -156,17 +156,17 @@ func init() {
|
||||
}
|
||||
|
||||
vpcs := listResp.JSON200.Vpcs
|
||||
for _, vpc := range vpcs {
|
||||
if strings.HasPrefix(vpc.Name, "tf-test-") {
|
||||
for _, vpc := range vpcs {
|
||||
if strings.HasPrefix(vpc.Name, "tf-test-") {
|
||||
|
||||
deleteResp, err := client.DeleteVpcsVpcIdWithResponse(ctx, vpc.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting VPC %d in test sweep: %w", vpc.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting VPC %d for test sweep: %s", deleteResp.StatusCode(), vpc.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted VPC for test sweep:", vpc.Id)
|
||||
deleteResp, err := client.DeleteVpcsVpcIdWithResponse(ctx, vpc.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting VPC %d in test sweep: %w", vpc.Id, err)
|
||||
}
|
||||
if deleteResp.StatusCode() != http.StatusNoContent {
|
||||
return fmt.Errorf("Unexpected status %d deleting VPC %d for test sweep: %s", deleteResp.StatusCode(), vpc.Id, deleteResp.Body)
|
||||
}
|
||||
log.Println("Deleted VPC for test sweep:", vpc.Id)
|
||||
}
|
||||
}
|
||||
if listResp.JSON200.Links == nil || listResp.JSON200.Links.Pages.Next == nil {
|
||||
|
||||
@@ -161,12 +161,6 @@ func ServerResourceSchema(ctx context.Context) schema.Schema {
|
||||
Description: "Leave null to use default (public) network for the selected region.",
|
||||
MarkdownDescription: "Leave null to use default (public) network for the selected region.",
|
||||
},
|
||||
"vpc_ipv4_address": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided.",
|
||||
MarkdownDescription: "If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -184,7 +178,6 @@ type ServerModel struct {
|
||||
SshKeys types.List `tfsdk:"ssh_keys"`
|
||||
UserData types.String `tfsdk:"user_data"`
|
||||
VpcId types.Int64 `tfsdk:"vpc_id"`
|
||||
VpcIpv4Address types.String `tfsdk:"vpc_ipv4_address"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = AdvancedFeaturesType{}
|
||||
|
||||
+4
-9
@@ -2,13 +2,13 @@
|
||||
"openapi": "3.0.4",
|
||||
"info": {
|
||||
"title": "BinaryLane API",
|
||||
"description": "# Introduction\n\nThis API is used by BinaryLane's 'mPanel' website, so you'll find everything you need here to automate your use of BinaryLane or develop tools for others. You could even build your own version of 'mPanel' if you wanted!\n\n\n## Getting Started Guide\n\nYou can find a detailed guide to using the API to manage your servers <a href='https://support.binarylane.com.au/support/solutions/articles/11000125203-getting-started-with-binarylane-api' target='_blank' rel='noopener noreferrer'>here</a>.\n\n\n# API Tools\n\nWe have created a CLI tool to interact with the API. You can find the repository <a href='https://github.com/binarylane/binarylane-cli' target='_blank' rel='noopener noreferrer'>here</a>.\n\n\n# Developer Preview Notes\n\n**Warning: this specification is currently in active development.** Until this warning is removed breaking changes are possible without the version changing.\nHowever, this API is mature and we have a fully functioning new version of mPanel that uses it, so breaking changes will be very limited.\n\nWe welcome all feedback from API users at the support email address above.\n\n\n# Version Information\n\nThis section will contain a link to our changelog once the developer preview period is complete.",
|
||||
"description": "\n\n# Introduction\n\nThis API is used by BinaryLane's 'mPanel' website, so you'll find everything you need here to automate your use of BinaryLane or develop tools for others. You could even build your own version of 'mPanel' if you wanted!\n\n\n## Getting Started Guide\n\nWe are working on a complete 'Getting Started' guide as well as this introductory section.\n\n\n# Developer Preview Notes\n\n**Warning: this specification is currently in active development.** Until this warning is removed breaking changes are possible without the version changing.\nHowever, this API is relatively mature and we are actively developing a new website and other tools against this API so breaking changes should be rare at this point.\n\nWe currently have a CLI (command line interface) tool in development and are in the early stage of planning other tools.\n\nWe welcome all feedback from API users at the support email address above.\n\n\n## Active Development Status\n\nThis is the list of BinaryLane features that are not yet available through the API but are currently under active development.\nIf you need an ETA on when these will be available please contact support.\n\n- Domains\n\n\n## Future Development Status\n\nThis is the list of BinaryLane features that are not yet available through the API and are not currently in active development. This list is in a rough order of priority.\nIf you need an ETA or want to ask for increased priority on any of these please contact support, we are happy to hear any feedback on this.\n\n- Account Updates\n- Payments\n\n\n\n# API Tools\n\nThis section will include links to tools to assist with consuming this API. We have a CLI tool and a GO library in active development, and are planning libraries for other languages.\n\n\n# Version Information\n\nThis section will contain a link to our changelog once the developer preview period is complete.\n\n",
|
||||
"contact": {
|
||||
"name": "BinaryLane Support",
|
||||
"url": "https://support.binarylane.com.au/support/home",
|
||||
"url": "https://support.binarylane.com.au",
|
||||
"email": "support@binarylane.com.au"
|
||||
},
|
||||
"version": "0.34.0",
|
||||
"version": "0.33.1",
|
||||
"x-logo": {
|
||||
"url": "https://www.binarylane.com.au/res/images/binarylane/logo.png",
|
||||
"backgroundColor": "#000000",
|
||||
@@ -5929,7 +5929,7 @@
|
||||
},
|
||||
"two_factor_authentication_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this account has enabled app-based two factor authentication."
|
||||
"description": "Whether this account has enabled two factor authentication."
|
||||
},
|
||||
"status": {
|
||||
"allOf": [
|
||||
@@ -7610,11 +7610,6 @@
|
||||
"format": "int64",
|
||||
"nullable": true
|
||||
},
|
||||
"vpc_ipv4_address": {
|
||||
"type": "string",
|
||||
"description": "If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided.",
|
||||
"nullable": true
|
||||
},
|
||||
"ssh_keys": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -929,13 +929,6 @@
|
||||
"description": "Leave null to use default (public) network for the selected region."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vpc_ipv4_address",
|
||||
"string": {
|
||||
"computed_optional_required": "computed_optional",
|
||||
"description": "If provided this will be the Ipv4 address for the server's private VPC network adapter. If this is null an unused Ipv4 address will be assigned. This field is only valid when VpcId is provided."
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"int64": {
|
||||
|
||||
Reference in New Issue
Block a user