Some checks failed
CI / build (push) Failing after 34s
- gofmt: fix struct field alignment in types.go, resource_server.go, data_source_ssh_keys_by_user.go - nilerr: refactor GetAllPages pagination detection to avoid returning nil error when json.Unmarshal fails (intentional passthrough for non-paginated responses) - .golangci.yml: replace deprecated linter names (vet -> govet, tenv -> usetesting) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
638 lines
21 KiB
Go
638 lines
21 KiB
Go
// Copyright (c) EZSCALE.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package provider
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"terraform-provider-virtfusion/internal/client"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
)
|
|
|
|
// Ensure provider defined types fully satisfy framework interfaces.
|
|
var (
|
|
_ resource.Resource = &ServerResource{}
|
|
_ resource.ResourceWithConfigure = &ServerResource{}
|
|
_ resource.ResourceWithImportState = &ServerResource{}
|
|
)
|
|
|
|
// NewServerResource returns a new server resource.
|
|
func NewServerResource() resource.Resource {
|
|
return &ServerResource{}
|
|
}
|
|
|
|
// ServerResource defines the resource implementation.
|
|
type ServerResource struct {
|
|
client *client.Client
|
|
}
|
|
|
|
// ServerResourceModel describes the resource data model.
|
|
type ServerResourceModel struct {
|
|
// Computed
|
|
ID types.Int64 `tfsdk:"id"`
|
|
UUID types.String `tfsdk:"uuid"`
|
|
Hostname types.String `tfsdk:"hostname"`
|
|
|
|
// Required (create)
|
|
PackageID types.Int64 `tfsdk:"package_id"`
|
|
UserID types.Int64 `tfsdk:"user_id"`
|
|
HypervisorID types.Int64 `tfsdk:"hypervisor_id"`
|
|
|
|
// Optional (create)
|
|
Ipv4 types.Int64 `tfsdk:"ipv4"`
|
|
Storage types.Int64 `tfsdk:"storage"`
|
|
Memory types.Int64 `tfsdk:"memory"`
|
|
Cores types.Int64 `tfsdk:"cores"`
|
|
Traffic types.Int64 `tfsdk:"traffic"`
|
|
InboundNetworkSpeed types.Int64 `tfsdk:"inbound_network_speed"`
|
|
OutboundNetworkSpeed types.Int64 `tfsdk:"outbound_network_speed"`
|
|
StorageProfile types.Int64 `tfsdk:"storage_profile"`
|
|
NetworkProfile types.Int64 `tfsdk:"network_profile"`
|
|
DryRun types.Bool `tfsdk:"dry_run"`
|
|
AdditionalStorage1 types.Int64 `tfsdk:"additional_storage_1"`
|
|
AdditionalStorage1Profile types.Int64 `tfsdk:"additional_storage_1_profile"`
|
|
AdditionalStorage2 types.Int64 `tfsdk:"additional_storage_2"`
|
|
AdditionalStorage2Profile types.Int64 `tfsdk:"additional_storage_2_profile"`
|
|
|
|
// Optional (update-only)
|
|
Name types.String `tfsdk:"name"`
|
|
CPUThrottle types.Int64 `tfsdk:"cpu_throttle"`
|
|
VNCEnabled types.Bool `tfsdk:"vnc_enabled"`
|
|
Suspended types.Bool `tfsdk:"suspended"`
|
|
BackupPlanID types.Int64 `tfsdk:"backup_plan_id"`
|
|
CustomXML types.String `tfsdk:"custom_xml"`
|
|
OwnerUserID types.Int64 `tfsdk:"owner_user_id"`
|
|
}
|
|
|
|
func (r *ServerResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
|
resp.TypeName = req.ProviderTypeName + "_server"
|
|
}
|
|
|
|
func (r *ServerResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
|
resp.Schema = schema.Schema{
|
|
MarkdownDescription: "Manages a VirtFusion server.",
|
|
Attributes: map[string]schema.Attribute{
|
|
// Computed
|
|
"id": schema.Int64Attribute{
|
|
MarkdownDescription: "The server ID.",
|
|
Computed: true,
|
|
PlanModifiers: []planmodifier.Int64{
|
|
int64planmodifier.UseStateForUnknown(),
|
|
},
|
|
},
|
|
"uuid": schema.StringAttribute{
|
|
MarkdownDescription: "The server UUID.",
|
|
Computed: true,
|
|
PlanModifiers: []planmodifier.String{
|
|
stringplanmodifier.UseStateForUnknown(),
|
|
},
|
|
},
|
|
"hostname": schema.StringAttribute{
|
|
MarkdownDescription: "The server hostname.",
|
|
Computed: true,
|
|
PlanModifiers: []planmodifier.String{
|
|
stringplanmodifier.UseStateForUnknown(),
|
|
},
|
|
},
|
|
|
|
// Required
|
|
"package_id": schema.Int64Attribute{
|
|
MarkdownDescription: "The package ID for the server.",
|
|
Required: true,
|
|
},
|
|
"user_id": schema.Int64Attribute{
|
|
MarkdownDescription: "The user ID who owns the server.",
|
|
Required: true,
|
|
},
|
|
"hypervisor_id": schema.Int64Attribute{
|
|
MarkdownDescription: "The hypervisor ID where the server will be created.",
|
|
Required: true,
|
|
},
|
|
|
|
// Optional (create)
|
|
"ipv4": schema.Int64Attribute{
|
|
MarkdownDescription: "Number of IPv4 addresses to assign. Defaults to 1.",
|
|
Optional: true,
|
|
Computed: true,
|
|
Default: int64default.StaticInt64(1),
|
|
},
|
|
"storage": schema.Int64Attribute{
|
|
MarkdownDescription: "Storage size override in GB.",
|
|
Optional: true,
|
|
},
|
|
"memory": schema.Int64Attribute{
|
|
MarkdownDescription: "Memory size override in MB.",
|
|
Optional: true,
|
|
},
|
|
"cores": schema.Int64Attribute{
|
|
MarkdownDescription: "Number of CPU cores override.",
|
|
Optional: true,
|
|
},
|
|
"traffic": schema.Int64Attribute{
|
|
MarkdownDescription: "Traffic limit override in GB.",
|
|
Optional: true,
|
|
},
|
|
"inbound_network_speed": schema.Int64Attribute{
|
|
MarkdownDescription: "Inbound network speed override in Mbps.",
|
|
Optional: true,
|
|
},
|
|
"outbound_network_speed": schema.Int64Attribute{
|
|
MarkdownDescription: "Outbound network speed override in Mbps.",
|
|
Optional: true,
|
|
},
|
|
"storage_profile": schema.Int64Attribute{
|
|
MarkdownDescription: "Storage profile ID.",
|
|
Optional: true,
|
|
},
|
|
"network_profile": schema.Int64Attribute{
|
|
MarkdownDescription: "Network profile ID.",
|
|
Optional: true,
|
|
},
|
|
"dry_run": schema.BoolAttribute{
|
|
MarkdownDescription: "If true, validates the request without creating the server.",
|
|
Optional: true,
|
|
Computed: true,
|
|
Default: booldefault.StaticBool(false),
|
|
},
|
|
"additional_storage_1": schema.Int64Attribute{
|
|
MarkdownDescription: "Additional storage 1 size in GB.",
|
|
Optional: true,
|
|
},
|
|
"additional_storage_1_profile": schema.Int64Attribute{
|
|
MarkdownDescription: "Additional storage 1 profile ID.",
|
|
Optional: true,
|
|
},
|
|
"additional_storage_2": schema.Int64Attribute{
|
|
MarkdownDescription: "Additional storage 2 size in GB.",
|
|
Optional: true,
|
|
},
|
|
"additional_storage_2_profile": schema.Int64Attribute{
|
|
MarkdownDescription: "Additional storage 2 profile ID.",
|
|
Optional: true,
|
|
},
|
|
|
|
// Optional (update-only)
|
|
"name": schema.StringAttribute{
|
|
MarkdownDescription: "The server display name.",
|
|
Optional: true,
|
|
Computed: true,
|
|
PlanModifiers: []planmodifier.String{
|
|
stringplanmodifier.UseStateForUnknown(),
|
|
},
|
|
},
|
|
"cpu_throttle": schema.Int64Attribute{
|
|
MarkdownDescription: "CPU throttle percentage (0-100).",
|
|
Optional: true,
|
|
},
|
|
"vnc_enabled": schema.BoolAttribute{
|
|
MarkdownDescription: "Whether VNC is enabled on the server.",
|
|
Optional: true,
|
|
},
|
|
"suspended": schema.BoolAttribute{
|
|
MarkdownDescription: "Whether the server is suspended.",
|
|
Optional: true,
|
|
},
|
|
"backup_plan_id": schema.Int64Attribute{
|
|
MarkdownDescription: "Backup plan ID. Set to 0 to remove the backup plan.",
|
|
Optional: true,
|
|
},
|
|
"custom_xml": schema.StringAttribute{
|
|
MarkdownDescription: "Custom XML configuration for the server.",
|
|
Optional: true,
|
|
},
|
|
"owner_user_id": schema.Int64Attribute{
|
|
MarkdownDescription: "The user ID to transfer ownership to.",
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *ServerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
|
if req.ProviderData == nil {
|
|
return
|
|
}
|
|
|
|
c, ok := req.ProviderData.(*client.Client)
|
|
if !ok {
|
|
resp.Diagnostics.AddError(
|
|
"Unexpected Resource Configure Type",
|
|
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
|
|
)
|
|
return
|
|
}
|
|
|
|
r.client = c
|
|
}
|
|
|
|
func (r *ServerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
|
var plan ServerResourceModel
|
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
// Build the create request from plan values.
|
|
createReq := client.ServerCreateRequest{
|
|
PackageID: plan.PackageID.ValueInt64(),
|
|
UserID: plan.UserID.ValueInt64(),
|
|
HypervisorID: plan.HypervisorID.ValueInt64(),
|
|
}
|
|
|
|
if !plan.Ipv4.IsNull() && !plan.Ipv4.IsUnknown() {
|
|
v := plan.Ipv4.ValueInt64()
|
|
createReq.Ipv4 = &v
|
|
}
|
|
if !plan.Storage.IsNull() && !plan.Storage.IsUnknown() {
|
|
v := plan.Storage.ValueInt64()
|
|
createReq.Storage = &v
|
|
}
|
|
if !plan.Memory.IsNull() && !plan.Memory.IsUnknown() {
|
|
v := plan.Memory.ValueInt64()
|
|
createReq.Memory = &v
|
|
}
|
|
if !plan.Cores.IsNull() && !plan.Cores.IsUnknown() {
|
|
v := plan.Cores.ValueInt64()
|
|
createReq.CPUCores = &v
|
|
}
|
|
if !plan.Traffic.IsNull() && !plan.Traffic.IsUnknown() {
|
|
v := plan.Traffic.ValueInt64()
|
|
createReq.Traffic = &v
|
|
}
|
|
if !plan.InboundNetworkSpeed.IsNull() && !plan.InboundNetworkSpeed.IsUnknown() {
|
|
v := plan.InboundNetworkSpeed.ValueInt64()
|
|
createReq.NetworkSpeedInbound = &v
|
|
}
|
|
if !plan.OutboundNetworkSpeed.IsNull() && !plan.OutboundNetworkSpeed.IsUnknown() {
|
|
v := plan.OutboundNetworkSpeed.ValueInt64()
|
|
createReq.NetworkSpeedOutbound = &v
|
|
}
|
|
if !plan.StorageProfile.IsNull() && !plan.StorageProfile.IsUnknown() {
|
|
v := plan.StorageProfile.ValueInt64()
|
|
createReq.StorageProfile = &v
|
|
}
|
|
if !plan.NetworkProfile.IsNull() && !plan.NetworkProfile.IsUnknown() {
|
|
v := plan.NetworkProfile.ValueInt64()
|
|
createReq.NetworkProfile = &v
|
|
}
|
|
if !plan.DryRun.IsNull() && !plan.DryRun.IsUnknown() {
|
|
v := plan.DryRun.ValueBool()
|
|
createReq.DryRun = &v
|
|
}
|
|
if !plan.AdditionalStorage1.IsNull() && !plan.AdditionalStorage1.IsUnknown() {
|
|
v := plan.AdditionalStorage1.ValueInt64()
|
|
createReq.AdditionalStorage1 = &v
|
|
}
|
|
if !plan.AdditionalStorage1Profile.IsNull() && !plan.AdditionalStorage1Profile.IsUnknown() {
|
|
v := plan.AdditionalStorage1Profile.ValueInt64()
|
|
createReq.AdditionalStorage1Profile = &v
|
|
}
|
|
if !plan.AdditionalStorage2.IsNull() && !plan.AdditionalStorage2.IsUnknown() {
|
|
v := plan.AdditionalStorage2.ValueInt64()
|
|
createReq.AdditionalStorage2 = &v
|
|
}
|
|
if !plan.AdditionalStorage2Profile.IsNull() && !plan.AdditionalStorage2Profile.IsUnknown() {
|
|
v := plan.AdditionalStorage2Profile.ValueInt64()
|
|
createReq.AdditionalStorage2Profile = &v
|
|
}
|
|
|
|
// Create the server.
|
|
rawResp, err := r.client.Post(ctx, "/servers", createReq)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Creating Server", err.Error())
|
|
return
|
|
}
|
|
|
|
// Parse the create response.
|
|
var serverResp client.ServerResponse
|
|
if err := json.Unmarshal(rawResp, &serverResp); err != nil {
|
|
resp.Diagnostics.AddError("Error Parsing Server Response", err.Error())
|
|
return
|
|
}
|
|
|
|
// Set computed values from the response.
|
|
plan.ID = types.Int64Value(serverResp.Data.ID)
|
|
plan.UUID = types.StringValue(serverResp.Data.UUID)
|
|
plan.Hostname = types.StringValue(serverResp.Data.Hostname)
|
|
|
|
// If name was not set in the plan, use the name from the API response.
|
|
if plan.Name.IsNull() || plan.Name.IsUnknown() {
|
|
plan.Name = types.StringValue(serverResp.Data.Name)
|
|
}
|
|
|
|
// After creation, apply update-only attributes if they are set.
|
|
serverID := serverResp.Data.ID
|
|
|
|
// Apply name if explicitly set in the plan.
|
|
if !plan.Name.IsNull() && !plan.Name.IsUnknown() && plan.Name.ValueString() != serverResp.Data.Name {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/modify/name", serverID), client.ServerModifyNameRequest{
|
|
Name: plan.Name.ValueString(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Setting Server Name", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Apply CPU throttle if set.
|
|
if !plan.CPUThrottle.IsNull() && !plan.CPUThrottle.IsUnknown() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/modify/cpuThrottle", serverID), client.ServerModifyCPUThrottleRequest{
|
|
Percentage: plan.CPUThrottle.ValueInt64(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Setting CPU Throttle", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Apply VNC if set.
|
|
if !plan.VNCEnabled.IsNull() && !plan.VNCEnabled.IsUnknown() && plan.VNCEnabled.ValueBool() {
|
|
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/vnc", serverID), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Enabling VNC", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Apply suspended if set to true.
|
|
if !plan.Suspended.IsNull() && !plan.Suspended.IsUnknown() && plan.Suspended.ValueBool() {
|
|
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/suspend", serverID), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Suspending Server", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Apply backup plan if set.
|
|
if !plan.BackupPlanID.IsNull() && !plan.BackupPlanID.IsUnknown() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/backups/plan/%d", serverID, plan.BackupPlanID.ValueInt64()), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Setting Backup Plan", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Apply custom XML if set.
|
|
if !plan.CustomXML.IsNull() && !plan.CustomXML.IsUnknown() {
|
|
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/customXML", serverID), client.ServerCustomXMLRequest{
|
|
XML: plan.CustomXML.ValueString(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Setting Custom XML", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Apply owner change if set and different from the creating user.
|
|
if !plan.OwnerUserID.IsNull() && !plan.OwnerUserID.IsUnknown() && plan.OwnerUserID.ValueInt64() != plan.UserID.ValueInt64() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/owner/%d", serverID, plan.OwnerUserID.ValueInt64()), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Changing Server Owner", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
|
}
|
|
|
|
func (r *ServerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
|
var state ServerResourceModel
|
|
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
rawResp, err := r.client.Get(ctx, fmt.Sprintf("/servers/%d", state.ID.ValueInt64()))
|
|
if err != nil {
|
|
var apiErr *client.APIError
|
|
if errors.As(err, &apiErr) && apiErr.IsNotFound() {
|
|
resp.State.RemoveResource(ctx)
|
|
return
|
|
}
|
|
resp.Diagnostics.AddError("Error Reading Server", err.Error())
|
|
return
|
|
}
|
|
|
|
var serverResp client.ServerResponse
|
|
if err := json.Unmarshal(rawResp, &serverResp); err != nil {
|
|
resp.Diagnostics.AddError("Error Parsing Server Response", err.Error())
|
|
return
|
|
}
|
|
|
|
// Map API response to state model.
|
|
s := serverResp.Data
|
|
state.ID = types.Int64Value(s.ID)
|
|
state.UUID = types.StringValue(s.UUID)
|
|
state.Hostname = types.StringValue(s.Hostname)
|
|
state.Name = types.StringValue(s.Name)
|
|
state.HypervisorID = types.Int64Value(s.HypervisorID)
|
|
|
|
// Map optional create attributes from the nested API response if they were set in state.
|
|
if !state.Storage.IsNull() && s.Settings != nil && s.Settings.Resources != nil {
|
|
state.Storage = types.Int64Value(s.Settings.Resources.Storage)
|
|
}
|
|
if !state.Memory.IsNull() && s.Settings != nil && s.Settings.Resources != nil {
|
|
state.Memory = types.Int64Value(s.Settings.Resources.Memory)
|
|
}
|
|
if !state.Cores.IsNull() && s.CPU != nil {
|
|
state.Cores = types.Int64Value(s.CPU.Cores)
|
|
}
|
|
if !state.Traffic.IsNull() && s.Settings != nil && s.Settings.Resources != nil {
|
|
state.Traffic = types.Int64Value(s.Settings.Resources.Traffic)
|
|
}
|
|
// NetworkSpeed and profiles are not returned by the detail API; preserve from state.
|
|
|
|
if !state.Suspended.IsNull() {
|
|
state.Suspended = types.BoolValue(s.Suspended)
|
|
}
|
|
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
|
}
|
|
|
|
func (r *ServerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
|
var plan, state ServerResourceModel
|
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
|
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
serverID := state.ID.ValueInt64()
|
|
|
|
// Preserve computed values from state.
|
|
plan.ID = state.ID
|
|
plan.UUID = state.UUID
|
|
plan.Hostname = state.Hostname
|
|
|
|
// Name change.
|
|
if !plan.Name.Equal(state.Name) {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/modify/name", serverID), client.ServerModifyNameRequest{
|
|
Name: plan.Name.ValueString(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Modifying Server Name", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// CPU cores change.
|
|
if !plan.Cores.Equal(state.Cores) && !plan.Cores.IsNull() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/modify/cpuCores", serverID), client.ServerModifyCPURequest{
|
|
CPUCores: plan.Cores.ValueInt64(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Modifying Server CPU Cores", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Memory change.
|
|
if !plan.Memory.Equal(state.Memory) && !plan.Memory.IsNull() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/modify/memory", serverID), client.ServerModifyMemoryRequest{
|
|
Memory: plan.Memory.ValueInt64(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Modifying Server Memory", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Traffic change.
|
|
if !plan.Traffic.Equal(state.Traffic) && !plan.Traffic.IsNull() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/modify/traffic", serverID), client.ServerModifyTrafficRequest{
|
|
Traffic: plan.Traffic.ValueInt64(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Modifying Server Traffic", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// CPU throttle change.
|
|
if !plan.CPUThrottle.Equal(state.CPUThrottle) && !plan.CPUThrottle.IsNull() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/modify/cpuThrottle", serverID), client.ServerModifyCPUThrottleRequest{
|
|
Percentage: plan.CPUThrottle.ValueInt64(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Modifying CPU Throttle", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// VNC toggle.
|
|
if !plan.VNCEnabled.Equal(state.VNCEnabled) && !plan.VNCEnabled.IsNull() {
|
|
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/vnc", serverID), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Toggling VNC", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Suspended change.
|
|
if !plan.Suspended.Equal(state.Suspended) && !plan.Suspended.IsNull() {
|
|
if plan.Suspended.ValueBool() {
|
|
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/suspend", serverID), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Suspending Server", err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/unsuspend", serverID), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Unsuspending Server", err.Error())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Backup plan change.
|
|
if !plan.BackupPlanID.Equal(state.BackupPlanID) {
|
|
planID := int64(0)
|
|
if !plan.BackupPlanID.IsNull() {
|
|
planID = plan.BackupPlanID.ValueInt64()
|
|
}
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/backups/plan/%d", serverID, planID), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Modifying Backup Plan", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Custom XML change.
|
|
if !plan.CustomXML.Equal(state.CustomXML) && !plan.CustomXML.IsNull() {
|
|
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/customXML", serverID), client.ServerCustomXMLRequest{
|
|
XML: plan.CustomXML.ValueString(),
|
|
})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Setting Custom XML", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Owner change.
|
|
if !plan.OwnerUserID.Equal(state.OwnerUserID) && !plan.OwnerUserID.IsNull() {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/owner/%d", serverID, plan.OwnerUserID.ValueInt64()), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Changing Server Owner", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
// Package change.
|
|
if !plan.PackageID.Equal(state.PackageID) {
|
|
_, err := r.client.Put(ctx, fmt.Sprintf("/servers/%d/package/%d", serverID, plan.PackageID.ValueInt64()), nil)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Error Changing Server Package", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
|
}
|
|
|
|
func (r *ServerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
|
var state ServerResourceModel
|
|
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
_, err := r.client.Delete(ctx, fmt.Sprintf("/servers/%d?delay=0", state.ID.ValueInt64()))
|
|
if err != nil {
|
|
var apiErr *client.APIError
|
|
if errors.As(err, &apiErr) && apiErr.IsNotFound() {
|
|
// Already deleted, nothing to do.
|
|
return
|
|
}
|
|
resp.Diagnostics.AddError("Error Deleting Server", err.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
func (r *ServerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
|
id, err := strconv.ParseInt(req.ID, 10, 64)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError(
|
|
"Invalid Import ID",
|
|
fmt.Sprintf("Could not parse server ID %q as an integer: %s", req.ID, err),
|
|
)
|
|
return
|
|
}
|
|
|
|
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), types.Int64Value(id))...)
|
|
}
|