Skip to content

Commit

Permalink
Hotfix 271124-1
Browse files Browse the repository at this point in the history
- Fixed some issues with the ensure processes where we could get nil pointers
- Added a new process to delete vms that were created but the creation process failed
- Added the enforce of the flag force_changes on the create where it will delete vms if the pre-exist
- Fixed some issues with the getVms method where the id was not being escaped
- Added a fix for the intel macs where we now add the user to the sudoers list if not present
- Added a fix where if PD failed to install we would get an error with dependencies
  • Loading branch information
cjlapao committed Nov 27, 2024
1 parent 2e2ff7a commit 9ae3ad0
Show file tree
Hide file tree
Showing 13 changed files with 340 additions and 137 deletions.
2 changes: 2 additions & 0 deletions internal/apiclient/get_vms.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strconv"
"strings"

"terraform-provider-parallels-desktop/internal/apiclient/apimodels"
"terraform-provider-parallels-desktop/internal/constants"
Expand All @@ -18,6 +19,7 @@ func GetVms(ctx context.Context, config HostConfig, filterField, filterValue str
diagnostic := diag.Diagnostics{}
response := make([]apimodels.VirtualMachine, 0)
urlHost := helpers.GetHostUrl(config.Host)
filterValue = strings.ReplaceAll(filterValue, "\"", "")
var url string

if config.IsOrchestrator {
Expand Down
29 changes: 21 additions & 8 deletions internal/common/ensure_machine_as_internal_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"terraform-provider-parallels-desktop/internal/apiclient"
"terraform-provider-parallels-desktop/internal/apiclient/apimodels"

"terraform-provider-parallels-desktop/internal/constants"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
Expand All @@ -19,16 +21,33 @@ func EnsureMachineHasInternalIp(ctx context.Context, hostConfig apiclient.HostCo
diagnostics.Append(ensureRunningDiag...)
return nil, diagnostics
}
if refreshVm == nil {
diagnostics.AddError("There was an error getting the vm", "vm is nil")
return nil, diagnostics
}

maxRetries := 10
maxRetries := constants.DEFAULT_OPERATION_MAX_RETRY_COUNT
retryCount := 0
for {
diagnostics = diag.Diagnostics{}
retryCount += 1
// We have run out of retries, add an error and break out of the loop
if retryCount >= maxRetries {
diagnostics.AddError("Error in Internal IP", "We could not get the internal IP of the machine")
break
}

if refreshVm.InternalIpAddress == "" || refreshVm.InternalIpAddress == "-" {
updatedVm, checkVmDiag := apiclient.GetVm(ctx, hostConfig, refreshVm.ID)
if checkVmDiag.HasError() {
diagnostics.Append(checkVmDiag...)
tflog.Error(ctx, "Error getting vm Internal IP")
continue
}
if updatedVm == nil {
diagnostics.AddError("Error in Internal IP", "VM not found")
tflog.Error(ctx, "Error getting vm Internal IP, VM not found")
continue
}

// If we have the internal IP, break out of the loop
Expand All @@ -39,13 +58,7 @@ func EnsureMachineHasInternalIp(ctx context.Context, hostConfig apiclient.HostCo
break
}

// We have run out of retries, add an error and break out of the loop
if retryCount >= maxRetries {
diagnostics.AddError("error getting vm Internal IP", "We could not get the internal IP of the machine")
break
}

time.Sleep(10 * time.Second)
time.Sleep(constants.DEFAULT_OPERATION_RETRY_INTERVAL_IN_SECONDS * time.Second)
} else {
break
}
Expand Down
94 changes: 94 additions & 0 deletions internal/common/ensure_machine_is_removed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package common

import (
"context"
"strings"
"time"

"terraform-provider-parallels-desktop/internal/apiclient"
"terraform-provider-parallels-desktop/internal/constants"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

func EnsureMachineIsRemoved(ctx context.Context, hostConfig apiclient.HostConfig, vmIdOrName string) diag.Diagnostics {
diagnostics := diag.Diagnostics{}
maxRetries := constants.DEFAULT_OPERATION_MAX_RETRY_COUNT
vmIdOrName = strings.ReplaceAll(vmIdOrName, "\"", "")
retryCount := 0
for {
diagnostics = diag.Diagnostics{}
retryCount += 1

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

currentVm, refreshDiags := apiclient.GetVm(ctx, hostConfig, vmIdOrName)
if refreshDiags.HasError() {
diagnostics.Append(refreshDiags...)
continue
}

// If the machine is not found, return ok as we cannot remove something that does not exist
if currentVm == nil {
// We are waiting for half of the time to wait for the orchestrator to update
time.Sleep(constants.DEFAULT_OPERATION_RETRY_INTERVAL_IN_SECONDS * time.Second)
currentVm, refreshDiags := apiclient.GetVm(ctx, hostConfig, vmIdOrName)
if refreshDiags.HasError() {
diagnostics.Append(refreshDiags...)
continue
}
if currentVm == nil {
diagnostics = diag.Diagnostics{}
break
}
}

tflog.Info(ctx, "Checking if "+currentVm.Name+" is running")
// making sure the VM is stopped before removing it
if _, ensureStopped := EnsureMachineStopped(ctx, hostConfig, currentVm); ensureStopped.HasError() {
diagnostics.Append(ensureStopped...)
continue
}

// Refresh the state of the machine
refreshedVm, refreshedVmDiag := apiclient.GetVm(ctx, hostConfig, currentVm.ID)
if refreshedVmDiag.HasError() {
diagnostics.Append(refreshedVmDiag...)
continue
}

if refreshedVm == nil {
// We are waiting for half of the time to wait for the orchestrator to update
time.Sleep(constants.DEFAULT_OPERATION_RETRY_INTERVAL_IN_SECONDS * time.Second)
refreshedVm, refreshDiags := apiclient.GetVm(ctx, hostConfig, vmIdOrName)
if refreshDiags.HasError() {
diagnostics.Append(refreshDiags...)
continue
}
if refreshedVm == nil {
diagnostics = diag.Diagnostics{}
break
}
}

// The machine is stopped, we can remove it
if refreshedVm.State == "stopped" {
tflog.Info(ctx, "Machine "+currentVm.Name+" is stopped, removing it")
if removeDiag := apiclient.DeleteVm(ctx, hostConfig, refreshedVm.ID); removeDiag.HasError() {
diagnostics.Append(removeDiag...)
continue
}
// The machine has been removed, break out of the loop
break
}

time.Sleep(constants.DEFAULT_OPERATION_RETRY_INTERVAL_IN_SECONDS * time.Second)
}

return diagnostics
}
60 changes: 37 additions & 23 deletions internal/common/ensure_machine_running.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"terraform-provider-parallels-desktop/internal/apiclient"
"terraform-provider-parallels-desktop/internal/apiclient/apimodels"
"terraform-provider-parallels-desktop/internal/constants"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-log/tflog"
Expand All @@ -15,66 +16,79 @@ import (
func EnsureMachineRunning(ctx context.Context, hostConfig apiclient.HostConfig, vm *apimodels.VirtualMachine) (*apimodels.VirtualMachine, diag.Diagnostics) {
diagnostics := diag.Diagnostics{}

returnVm, refreshDiags := apiclient.GetVm(ctx, hostConfig, vm.ID)
currentVm, refreshDiags := apiclient.GetVm(ctx, hostConfig, vm.ID)
if refreshDiags.HasError() {
diagnostics.Append(refreshDiags...)
return vm, diagnostics
}

maxRetries := 20
if currentVm == nil {
diagnostics.AddError("There was an error getting the vm", "vm is nil")
return vm, diagnostics
}

maxRetries := constants.DEFAULT_OPERATION_MAX_RETRY_COUNT
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))

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

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

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

tflog.Info(ctx, "Checking if "+returnVm.Name+" is running")
tflog.Info(ctx, "Checking if "+currentVm.Name+" is running")

updatedVm, checkVmDiag := apiclient.GetVm(ctx, hostConfig, returnVm.ID)
if checkVmDiag.HasError() {
diagnostics.Append(checkVmDiag...)
refreshedVm, refreshedVmDiag := apiclient.GetVm(ctx, hostConfig, currentVm.ID)
if refreshedVmDiag.HasError() {
diagnostics.Append(refreshedVmDiag...)
continue
}
if refreshedVm == nil {
diagnostics.AddError("Error starting vm", "Could not verify the state of the machine after starting, retry count exceeded")
continue
}

// The machine is running, lets check if we have the tools initialized
if updatedVm.State == "running" {
if refreshedVm.State == "running" {
echoHelloCommand := apimodels.PostScriptItem{
Command: "echo 'I am running'",
VirtualMachineId: updatedVm.ID,
VirtualMachineId: refreshedVm.ID,
}

// Only breaking out of the loop if the script executes successfully
if _, execDiag := apiclient.ExecuteScript(ctx, hostConfig, echoHelloCommand); !execDiag.HasError() {
tflog.Info(ctx, "Machine "+returnVm.Name+" is running")
tflog.Info(ctx, "Machine "+currentVm.Name+" is running")
diagnostics = diag.Diagnostics{}
returnVm = updatedVm
currentVm = refreshedVm
break
}
}

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

time.Sleep(10 * time.Second)
time.Sleep(constants.DEFAULT_OPERATION_RETRY_INTERVAL_IN_SECONDS * time.Second)
} else {
break
}
}

return returnVm, diagnostics
return currentVm, diagnostics
}
57 changes: 33 additions & 24 deletions internal/common/ensure_machine_stopped.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"terraform-provider-parallels-desktop/internal/apiclient"
"terraform-provider-parallels-desktop/internal/apiclient/apimodels"
"terraform-provider-parallels-desktop/internal/constants"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-log/tflog"
Expand All @@ -15,58 +16,66 @@ import (
func EnsureMachineStopped(ctx context.Context, hostConfig apiclient.HostConfig, vm *apimodels.VirtualMachine) (*apimodels.VirtualMachine, diag.Diagnostics) {
diagnostics := diag.Diagnostics{}

returnVm, refreshDiags := apiclient.GetVm(ctx, hostConfig, vm.ID)
currentVm, refreshDiags := apiclient.GetVm(ctx, hostConfig, vm.ID)
if refreshDiags.HasError() {
diagnostics.Append(refreshDiags...)
return vm, diagnostics
}
if currentVm == nil {
diagnostics.AddError("There was an error getting the vm", "vm is nil")
return vm, diagnostics
}

maxRetries := 30
maxRetries := constants.DEFAULT_OPERATION_MAX_RETRY_COUNT
retryCount := 0
for {
diagnostics = diag.Diagnostics{}
retryCount += 1
if returnVm.State != "stopped" {
tflog.Info(ctx, "Machine "+returnVm.Name+" is not stopped, stopping it"+fmt.Sprintf("[%v/%v]", retryCount, maxRetries))
result, stateDiag := apiclient.SetMachineState(ctx, hostConfig, returnVm.ID, apiclient.MachineStateOpStop)

// We have run out of retries, add an error and break out of the loop
if retryCount >= maxRetries {
diagnostics.AddError("error stopping vm", "error stopping vm")
break
}

if currentVm.State != "stopped" {
tflog.Info(ctx, "Machine "+currentVm.Name+" is not stopped, stopping it"+fmt.Sprintf("[%v/%v]", retryCount, maxRetries))
result, stateDiag := apiclient.SetMachineState(ctx, hostConfig, currentVm.ID, apiclient.MachineStateOpStop)
if stateDiag.HasError() {
diagnostics.Append(stateDiag...)
continue
}

if !result {
diagnostics.AddError("error stopping vm", "error stopping vm")
diagnostics.AddError("error stopping vm", "Could not set the state of the machine to stopped")
continue
}

tflog.Info(ctx, "Checking if "+returnVm.Name+" is stopped")
tflog.Info(ctx, "Checking if "+currentVm.Name+" is stopped")

updatedVm, checkVmDiag := apiclient.GetVm(ctx, hostConfig, returnVm.ID)
if checkVmDiag.HasError() {
diagnostics.Append(checkVmDiag...)
refreshedVm, refreshedVmDiag := apiclient.GetVm(ctx, hostConfig, currentVm.ID)
if refreshedVmDiag.HasError() {
diagnostics.Append(refreshedVmDiag...)
continue
}
if updatedVm == nil {
diagnostics.AddError("error stopping vm", "VM not found")
return returnVm, diagnostics
if refreshedVm == nil {
diagnostics.AddError("error stopping vm", "Could not verify the state of the machine after stopping, vm is nil")
continue
}

// All if good, break out of the loop
if updatedVm.State == "stopped" {
tflog.Info(ctx, "Machine "+returnVm.Name+" is stopped")
if refreshedVm.State == "stopped" {
tflog.Info(ctx, "Machine "+currentVm.Name+" is stopped")
diagnostics = diag.Diagnostics{}
returnVm = updatedVm
break
}

// We have run out of retries, add an error and break out of the loop
if retryCount >= maxRetries {
diagnostics.AddError("error stopping vm", "error stopping vm")
currentVm = refreshedVm
break
}

time.Sleep(10 * time.Second)
time.Sleep(constants.DEFAULT_OPERATION_RETRY_INTERVAL_IN_SECONDS * time.Second)
} else {
break
}
}

return returnVm, diagnostics
return currentVm, diagnostics
}
Loading

0 comments on commit 9ae3ad0

Please sign in to comment.