Skip to content

Commit

Permalink
Add always run scripts
Browse files Browse the repository at this point in the history
- Added the ability to set a post_processor_script to always run on update
- Fixed some issues where in some cases the update would but the vm in the wrong state
- Fixed an issue where some errors would bring a nil pointer
- Added an option to wait for network before querying to the datasource vm
- Added a retry mechanism to attempt to get the internal ip on create/update
  • Loading branch information
cjlapao committed Nov 21, 2024
1 parent 5929a13 commit ab844f9
Show file tree
Hide file tree
Showing 14 changed files with 449 additions and 116 deletions.
112 changes: 83 additions & 29 deletions internal/clone_vm/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ func (r *CloneVmResource) Create(ctx context.Context, req resource.CreateRequest
}

// Checking if the name is already in use
existingVms, diag := apiclient.GetVms(ctx, hostConfig, "name", data.Name.ValueString())
if diag.HasError() {
resp.Diagnostics.Append(diag...)
existingVms, existingVmDiag := apiclient.GetVms(ctx, hostConfig, "name", data.Name.ValueString())
if existingVmDiag.HasError() {
resp.Diagnostics.Append(existingVmDiag...)
return
}

Expand All @@ -146,9 +146,9 @@ func (r *CloneVmResource) Create(ctx context.Context, req resource.CreateRequest
}

// Checking if we can find the base vm to clone
vm, diag := apiclient.GetVm(ctx, hostConfig, data.BaseVmId.ValueString())
if diag.HasError() {
diag.Append(diag...)
vm, getVmDiag := apiclient.GetVm(ctx, hostConfig, data.BaseVmId.ValueString())
if getVmDiag.HasError() {
resp.Diagnostics.Append(getVmDiag...)
return
}

Expand Down Expand Up @@ -177,9 +177,9 @@ func (r *CloneVmResource) Create(ctx context.Context, req resource.CreateRequest
}

// Checking if we can find the base vm to clone
createdVms, diag := apiclient.GetVms(ctx, hostConfig, "name", data.Name.ValueString())
if diag.HasError() {
diag.Append(diag...)
createdVms, createVmDiag := apiclient.GetVms(ctx, hostConfig, "name", data.Name.ValueString())
if createVmDiag.HasError() {
resp.Diagnostics.Append(createVmDiag...)
return
}
if len(createdVms) != 1 {
Expand All @@ -194,9 +194,9 @@ func (r *CloneVmResource) Create(ctx context.Context, req resource.CreateRequest
// stopping the machine as it might need some operations where the machine needs to be stopped
// add anything here in sequence that needs to be done before the machine is started
// so we do not loose time waiting for the machine to stop
stoppedVm, diag := common.EnsureMachineStopped(ctx, hostConfig, &clonedVm)
if diag.HasError() {
resp.Diagnostics.Append(diag...)
stoppedVm, stoppedVmDiag := common.EnsureMachineStopped(ctx, hostConfig, &clonedVm)
if stoppedVmDiag.HasError() {
resp.Diagnostics.Append(stoppedVmDiag...)
}

// Applying the Specs block
Expand Down Expand Up @@ -331,13 +331,24 @@ func (r *CloneVmResource) Create(ctx context.Context, req resource.CreateRequest

externalIp := ""
internalIp := ""
refreshVm, refreshDiag := apiclient.GetVm(ctx, hostConfig, vm.ID)
if refreshDiag.HasError() {
resp.Diagnostics.Append(refreshDiag...)
return
} else {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
retryAttempts := 10
var refreshVm *apimodels.VirtualMachine
var refreshDiag diag.Diagnostics
for {
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, vm.ID)
if !refreshDiag.HasError() {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
}
if internalIp != "" {
time.Sleep(5 * time.Second)
break
}
if retryAttempts == 0 {
internalIp = "-"
break
}
retryAttempts--
}

data.ExternalIp = types.StringValue(externalIp)
Expand Down Expand Up @@ -507,15 +518,16 @@ func (r *CloneVmResource) Update(ctx context.Context, req resource.UpdateRequest
DisableTlsValidation: r.provider.DisableTlsValidation.ValueBool(),
}

vm, diag := apiclient.GetVm(ctx, hostConfig, currentData.ID.ValueString())
if diag.HasError() {
resp.Diagnostics.Append(diag...)
vm, getVmDiag := apiclient.GetVm(ctx, hostConfig, currentData.ID.ValueString())
if getVmDiag.HasError() {
resp.Diagnostics.Append(getVmDiag...)
return
}
if vm == nil {
resp.State.RemoveResource(ctx)
return
}
currentVmState := vm.State

nameChanges := apimodels.NewVmConfigRequest(vm.User)
currentState := vm.State
Expand Down Expand Up @@ -666,13 +678,24 @@ func (r *CloneVmResource) Update(ctx context.Context, req resource.UpdateRequest

externalIp := ""
internalIp := ""
refreshVm, refreshDiag := apiclient.GetVm(ctx, hostConfig, vm.ID)
if refreshDiag.HasError() {
resp.Diagnostics.Append(refreshDiag...)
return
} else {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
retryAttempts := 10
var refreshVm *apimodels.VirtualMachine
var refreshDiag diag.Diagnostics
for {
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, vm.ID)
if !refreshDiag.HasError() {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
}
if internalIp != "" {
time.Sleep(5 * time.Second)
break
}
if retryAttempts == 0 {
internalIp = "-"
break
}
retryAttempts--
}

data.ID = types.StringValue(refreshVm.ID)
Expand Down Expand Up @@ -705,6 +728,37 @@ func (r *CloneVmResource) Update(ctx context.Context, req resource.UpdateRequest
}
}

if currentVmState != refreshVm.State {
// If the vm state is desync we nee to set it right
switch currentVmState {
case "running":
if refreshVm.State == "stopped" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStart)
}
if refreshVm.State == "paused" || refreshVm.State == "suspended" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpResume)
}
case "stopped":
if refreshVm.State == "running" || refreshVm.State == "paused" || refreshVm.State == "suspended" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop)
}
case "paused":
if refreshVm.State == "running" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpResume)
}
if refreshVm.State == "stopped" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStart)
}
case "suspended":
if refreshVm.State == "running" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpResume)
}
if refreshVm.State == "stopped" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStart)
}
}
}

tflog.Info(ctx, "Updated vm with id "+data.ID.ValueString()+" and name "+data.Name.ValueString())

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
Expand Down
12 changes: 8 additions & 4 deletions internal/common/ensure_machine_running.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,24 @@ func EnsureMachineRunning(ctx context.Context, hostConfig apiclient.HostConfig,
return vm, diagnostics
}

maxRetries := 10
maxRetries := 20
retryCount := 0
for {
diagnostics = diag.Diagnostics{}
retryCount += 1
if returnVm.State != "running" {
tflog.Info(ctx, "Machine "+returnVm.Name+" is not running, starting it"+fmt.Sprintf("[%v/%v]", retryCount, maxRetries))
result, stateDiag := apiclient.SetMachineState(ctx, hostConfig, returnVm.ID, apiclient.MachineStateOpStart)
op := apiclient.MachineStateOpStart
if returnVm.State == "suspended" || returnVm.State == "paused" {
op = apiclient.MachineStateOpResume
}
result, stateDiag := apiclient.SetMachineState(ctx, hostConfig, returnVm.ID, op)
if stateDiag.HasError() {
diagnostics.Append(stateDiag...)
}

if !result {
diagnostics.AddError("error starting vm", "error starting vm")
diagnostics.AddError("Error starting vm", "Could not set the state of the machine to running")
}

tflog.Info(ctx, "Checking if "+returnVm.Name+" is running")
Expand Down Expand Up @@ -62,7 +66,7 @@ func EnsureMachineRunning(ctx context.Context, hostConfig apiclient.HostConfig,

// We have run out of retries, add an error and break out of the loop
if retryCount >= maxRetries {
diagnostics.AddError("error starting vm", "error starting vm")
diagnostics.AddError("Error starting vm", "Could not verify the state of the machine after starting, retry count exceeded")
break
}

Expand Down
6 changes: 6 additions & 0 deletions internal/common/post_processor_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ func RunPostProcessorScript(ctx context.Context, hostConfig apiclient.HostConfig

func PostProcessorHasChanges(ctx context.Context, planPostProcessorScript, statePostProcessorScript []*postprocessorscript.PostProcessorScript) bool {
for i, script := range planPostProcessorScript {
if script.AlwaysRunOnUpdate.ValueBool() {
return true
}
innerElements := script.Inline.Elements()
if len(innerElements) > 0 && len(statePostProcessorScript) == 0 {
return true
}
if len(innerElements) != len(statePostProcessorScript[i].Inline.Elements()) {
return true
}
Expand Down
4 changes: 2 additions & 2 deletions internal/common/specs.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func SpecsBlockOnUpdate(ctx context.Context, hostConfig apiclient.HostConfig, vm
}

if hardwareInfo.TotalAvailable.LogicalCpuCount-int64(updateValueInt) <= 0 {
diagnostics.AddError("not enough cpus", "not enough cpus")
diagnostics.AddError("Not enough cpus", "You requested more cpus than available, the machine will need "+updateValue+" cpus and we have "+fmt.Sprintf("%v", hardwareInfo.TotalAvailable.LogicalCpuCount))
return diagnostics
}

Expand All @@ -88,7 +88,7 @@ func SpecsBlockOnUpdate(ctx context.Context, hostConfig apiclient.HostConfig, vm
}

if hardwareInfo.TotalAvailable.MemorySize-float64(updateValueInt) <= 0 {
diagnostics.AddError("not enough memory", "not enough memory")
diagnostics.AddError("Not enough memory", "You requested more memory than available, the machine will need "+updateValue+" memory and we have "+fmt.Sprintf("%v", hardwareInfo.TotalAvailable.MemorySize))
return diagnostics
}

Expand Down
101 changes: 78 additions & 23 deletions internal/remoteimage/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,13 +369,24 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques

externalIp := ""
internalIp := ""
refreshVm, refreshDiag := apiclient.GetVm(ctx, hostConfig, response.ID)
if refreshDiag.HasError() {
resp.Diagnostics.Append(refreshDiag...)
return
} else {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
retryAttempts := 10
var refreshVm *apimodels.VirtualMachine
var refreshDiag diag.Diagnostics
for {
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, refreshVm.ID)
if !refreshDiag.HasError() {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
}
if internalIp != "" {
time.Sleep(5 * time.Second)
break
}
if retryAttempts == 0 {
internalIp = "-"
break
}
retryAttempts--
}

data.OsType = types.StringValue(createdVM.OS)
Expand Down Expand Up @@ -557,15 +568,16 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques
DisableTlsValidation: r.provider.DisableTlsValidation.ValueBool(),
}

vm, diag := apiclient.GetVm(ctx, hostConfig, currentData.ID.ValueString())
if diag.HasError() {
resp.Diagnostics.Append(diag...)
vm, getVmDiag := apiclient.GetVm(ctx, hostConfig, currentData.ID.ValueString())
if getVmDiag.HasError() {
resp.Diagnostics.Append(getVmDiag...)
return
}
if vm == nil {
resp.State.RemoveResource(ctx)
return
}
currentVmState := vm.State

hostConfig.HostId = vm.HostId

Expand Down Expand Up @@ -609,9 +621,9 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques

// Changing the name of the machine
if nameChanges.HasChanges() {
_, diag := apiclient.ConfigureMachine(ctx, hostConfig, vm.ID, nameChanges)
if diag.HasError() {
resp.Diagnostics.Append(diag...)
_, nameChangeDiag := apiclient.ConfigureMachine(ctx, hostConfig, vm.ID, nameChanges)
if nameChangeDiag.HasError() {
resp.Diagnostics.Append(nameChangeDiag...)
return
}
}
Expand All @@ -624,8 +636,8 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques
return
}

if diags := common.SpecsBlockOnUpdate(ctx, hostConfig, vm, data.Specs, currentData.Specs); diags.HasError() {
resp.Diagnostics.Append(diags...)
if specBlockDiag := common.SpecsBlockOnUpdate(ctx, hostConfig, vm, data.Specs, currentData.Specs); specBlockDiag.HasError() {
resp.Diagnostics.Append(specBlockDiag...)
return
}
}
Expand Down Expand Up @@ -734,13 +746,24 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques

externalIp := ""
internalIp := ""
refreshVm, refreshDiag := apiclient.GetVm(ctx, hostConfig, vm.ID)
if refreshDiag.HasError() {
resp.Diagnostics.Append(refreshDiag...)
return
} else {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
retryAttempts := 10
var refreshVm *apimodels.VirtualMachine
var refreshDiag diag.Diagnostics
for {
refreshVm, refreshDiag = apiclient.GetVm(ctx, hostConfig, vm.ID)
if !refreshDiag.HasError() {
externalIp = refreshVm.HostExternalIpAddress
internalIp = refreshVm.InternalIpAddress
}
if internalIp != "" {
time.Sleep(5 * time.Second)
break
}
if retryAttempts == 0 {
internalIp = "-"
break
}
retryAttempts--
}

data.ID = types.StringValue(vm.ID)
Expand Down Expand Up @@ -775,7 +798,39 @@ func (r *RemoteVmResource) Update(ctx context.Context, req resource.UpdateReques
}
}

if (data.RunAfterCreate.ValueBool() || data.RunAfterCreate.IsUnknown() || data.RunAfterCreate.IsNull()) && (vm.State == "stopped") {
if currentVmState != refreshVm.State {
// If the vm state is desync we nee to set it right
switch currentVmState {
case "running":
if refreshVm.State == "stopped" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStart)
}
if refreshVm.State == "paused" || refreshVm.State == "suspended" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpResume)
}
case "stopped":
if refreshVm.State == "running" || refreshVm.State == "paused" || refreshVm.State == "suspended" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop)
}
case "paused":
if refreshVm.State == "running" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpResume)
}
if refreshVm.State == "stopped" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStart)
}
case "suspended":
if refreshVm.State == "running" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpResume)
}
if refreshVm.State == "stopped" {
apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStart)
}
}
}

if (data.RunAfterCreate.ValueBool() || data.KeepRunning.ValueBool() || (data.RunAfterCreate.IsUnknown() && data.KeepRunning.IsUnknown())) &&
(vm.State == "stopped") {
if _, diag := common.EnsureMachineRunning(ctx, hostConfig, vm); diag.HasError() {
resp.Diagnostics.Append(diag...)
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
type PostProcessorScript struct {
Inline types.List `tfsdk:"inline"`
Retry *PostProcessorScriptRetry `tfsdk:"retry"`
AlwaysRunOnUpdate types.Bool `tfsdk:"always_run_on_update"`
EnvironmentVariables map[string]types.String `tfsdk:"environment_variables"`
Result basetypes.ListValue `tfsdk:"result"`
}
Expand Down
Loading

0 comments on commit ab844f9

Please sign in to comment.