diff --git a/internal/apiclient/get_vms.go b/internal/apiclient/get_vms.go index b184421..628f183 100644 --- a/internal/apiclient/get_vms.go +++ b/internal/apiclient/get_vms.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "strings" "terraform-provider-parallels-desktop/internal/apiclient/apimodels" "terraform-provider-parallels-desktop/internal/constants" @@ -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 { diff --git a/internal/common/ensure_machine_as_internal_ip.go b/internal/common/ensure_machine_as_internal_ip.go index 9961205..4b2aaf7 100644 --- a/internal/common/ensure_machine_as_internal_ip.go +++ b/internal/common/ensure_machine_as_internal_ip.go @@ -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" ) @@ -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 @@ -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 } diff --git a/internal/common/ensure_machine_is_removed.go b/internal/common/ensure_machine_is_removed.go new file mode 100644 index 0000000..1ea446a --- /dev/null +++ b/internal/common/ensure_machine_is_removed.go @@ -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 +} diff --git a/internal/common/ensure_machine_running.go b/internal/common/ensure_machine_running.go index 2b38236..5b61aa5 100644 --- a/internal/common/ensure_machine_running.go +++ b/internal/common/ensure_machine_running.go @@ -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" @@ -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 } diff --git a/internal/common/ensure_machine_stopped.go b/internal/common/ensure_machine_stopped.go index 7d1d693..ae507c5 100644 --- a/internal/common/ensure_machine_stopped.go +++ b/internal/common/ensure_machine_stopped.go @@ -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" @@ -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 } diff --git a/internal/common/post_processor_script.go b/internal/common/post_processor_script.go index 183ae30..c8a26f0 100644 --- a/internal/common/post_processor_script.go +++ b/internal/common/post_processor_script.go @@ -24,17 +24,22 @@ func RunPostProcessorScript(ctx context.Context, hostConfig apiclient.HostConfig return diagnostics } - refreshVm, diag := EnsureMachineRunning(ctx, hostConfig, vm) + currentVm, diag := EnsureMachineRunning(ctx, hostConfig, vm) if diag.HasError() { diagnostics.Append(diag...) return diagnostics } + if currentVm == nil { + diagnostics.AddError("There was an error getting the vm", "vm is nil") + return diagnostics + } - tflog.Info(ctx, "Running post processor script on vm "+refreshVm.Name+" with state "+refreshVm.State+"...") + tflog.Info(ctx, "Running post processor script on vm "+currentVm.Name+" with state "+currentVm.State+"...") for _, script := range scripts { - maxRetries := constants.DEFAULT_MAX_RETRY_COUNT - waitBeforeRetry := time.Second * time.Duration(constants.DEFAULT_RETRY_INTERVAL_IN_SECONDS) + maxRetries := constants.DEFAULT_SCRIPT_MAX_RETRY_COUNT + waitBeforeRetry := time.Second * time.Duration(constants.DEFAULT_SCRIPT_RETRY_INTERVAL_IN_SECONDS) + if script.Retry != nil { if !diag.HasError() { retriesMaxRetries := int(script.Retry.Attempts.ValueInt64()) @@ -50,7 +55,7 @@ func RunPostProcessorScript(ctx context.Context, hostConfig apiclient.HostConfig } if err := retry.For(maxRetries, waitBeforeRetry, func() error { - tflog.Info(ctx, fmt.Sprintf("Running post processor script %s on vm %s with state %s [%v]", script.Inline, refreshVm.Name, refreshVm.State, maxRetries)) + tflog.Info(ctx, fmt.Sprintf("Running post processor script %s on vm %s with state %s [%v]", script.Inline, currentVm.Name, currentVm.State, maxRetries)) resultDiag := script.Apply(ctx, hostConfig, vm) tflog.Info(ctx, fmt.Sprintf("Script %s executed, result %v", script.Inline, resultDiag)) if resultDiag.HasError() { @@ -63,7 +68,7 @@ func RunPostProcessorScript(ctx context.Context, hostConfig apiclient.HostConfig return nil }); err != nil { - tflog.Info(ctx, fmt.Sprintf("Error running post processor script %s on vm %s with state %s [%v]", script.Inline, refreshVm.Name, refreshVm.State, maxRetries)) + tflog.Info(ctx, fmt.Sprintf("Error running post processor script %s on vm %s with state %s [%v]", script.Inline, currentVm.Name, currentVm.State, maxRetries)) tflog.Info(ctx, "Error running post processor script") diagnostics.AddError("Error running post processor script", err.Error()) return diagnostics diff --git a/internal/constants/main.go b/internal/constants/main.go index ad0eb98..588eef4 100644 --- a/internal/constants/main.go +++ b/internal/constants/main.go @@ -9,6 +9,8 @@ const ( ) const ( - DEFAULT_MAX_RETRY_COUNT = 4 - DEFAULT_RETRY_INTERVAL_IN_SECONDS = 30 + DEFAULT_SCRIPT_MAX_RETRY_COUNT = 4 + DEFAULT_SCRIPT_RETRY_INTERVAL_IN_SECONDS = 30 + DEFAULT_OPERATION_MAX_RETRY_COUNT = 20 + DEFAULT_OPERATION_RETRY_INTERVAL_IN_SECONDS = 10 ) diff --git a/internal/deploy/devops_service.go b/internal/deploy/devops_service.go index e4d4113..ea0c5c6 100644 --- a/internal/deploy/devops_service.go +++ b/internal/deploy/devops_service.go @@ -107,6 +107,39 @@ func (c *DevOpsServiceClient) InstallDependencies(listToInstall []string) ([]str installed_dependencies = append(installed_dependencies, "brew") } } + // setting up sudo access for brew without password + + cmd := "echo" + // " %v | sudo -S echo hello | sudo grep -q '^%v ALL=(ALL) NOPASSWD:ALL$' /etc/sudoers || echo '%v ALL=(ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers", c.client.Password(), c.client.Username(), c.client.Username() + sudoArgs := []string{ + c.client.Password(), + "|", + "sudo", + "-S", + "echo", + "hello", + "|", + "sudo", + "grep", + "-q", + fmt.Sprintf("'^%v ALL=(ALL) NOPASSWD:ALL$'", c.client.Username()), + "/etc/sudoers", + "||", + "echo", + fmt.Sprintf("'%v ALL=(ALL) NOPASSWD:ALL'", c.client.Username()), + "|", + "sudo", + "tee", + "-a", + "/etc/sudoers", + } + + _, err := c.client.RunCommand(cmd, sudoArgs) + fullCommand := fmt.Sprintf("%v %v", cmd, strings.Join(sudoArgs, " ")) + tflog.Info(c.ctx, "Full command: "+fullCommand) + if err != nil { + return installed_dependencies, errors.New("Error setting up sudo access for brew without password, error: " + err.Error()) + } case "git": gitPresent := c.findPath("git") brewPresent := c.findPath("brew") @@ -151,6 +184,8 @@ func (c *DevOpsServiceClient) UninstallDependencies(installedDependencies []stri if !ok { for _, dep := range installedDependencies { switch dep { + case "brew": + continue case "git": if err := c.UninstallGit(); err != nil { uninstalllErrors = append(uninstalllErrors, err) @@ -164,7 +199,7 @@ func (c *DevOpsServiceClient) UninstallDependencies(installedDependencies []stri uninstalllErrors = append(uninstalllErrors, err) } default: - uninstalllErrors = append(uninstalllErrors, errors.New("Unsupported dependency")) + uninstalllErrors = append(uninstalllErrors, errors.New("Unsupported dependency"+dep+" to uninstall")) } } } else { diff --git a/internal/deploy/resource.go b/internal/deploy/resource.go index 88177e6..8aad8e7 100644 --- a/internal/deploy/resource.go +++ b/internal/deploy/resource.go @@ -879,7 +879,9 @@ func (r *DeployResource) installParallelsDesktop(parallelsClient *DevOpsServiceC } if err := parallelsClient.UninstallParallelsDesktop(); err != nil { diag.AddError("Error uninstalling dependencies", err.Error()) + return installed_dependencies, diag } + diag.AddError("Error installing parallels desktop", err.Error()) return installed_dependencies, diag } diff --git a/internal/interfaces/cmdclient.go b/internal/interfaces/cmdclient.go index f591074..df4619f 100644 --- a/internal/interfaces/cmdclient.go +++ b/internal/interfaces/cmdclient.go @@ -2,4 +2,6 @@ package interfaces type CommandClient interface { RunCommand(command string, arguments []string) (string, error) + Username() string + Password() string } diff --git a/internal/localclient/main.go b/internal/localclient/main.go index 874d644..0341c4e 100644 --- a/internal/localclient/main.go +++ b/internal/localclient/main.go @@ -57,3 +57,11 @@ func executeWithOutput(command Command) (stdout string, stderr string, exitCode stdout = strings.TrimSuffix(stdOut.String(), "\n") return stdout, stderr, cmd.ProcessState.ExitCode(), nil } + +func (l *LocalClient) Username() string { + return "" +} + +func (l *LocalClient) Password() string { + return "" +} diff --git a/internal/remoteimage/resource.go b/internal/remoteimage/resource.go index f5fe38a..65a8bf9 100644 --- a/internal/remoteimage/resource.go +++ b/internal/remoteimage/resource.go @@ -134,9 +134,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques } // Checking if the VM already exists in the host - vm, vmDiag := apiclient.GetVms(ctx, hostConfig, "Name", data.Name.String()) - if vmDiag.HasError() { - resp.Diagnostics.Append(vmDiag...) + vms, createVmResponseDiag := apiclient.GetVms(ctx, hostConfig, "Name", data.Name.String()) + if createVmResponseDiag.HasError() { + resp.Diagnostics.Append(createVmResponseDiag...) return } @@ -155,9 +155,17 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques } } - if len(vm) > 0 { - resp.Diagnostics.AddError("Vm already exists", "The vm "+data.Name.ValueString()+" already exists") - return + if len(vms) > 0 { + if !data.ForceChanges.ValueBool() { + resp.Diagnostics.AddError("Vm already exists", "The vm "+data.Name.ValueString()+" already exists") + return + } else { + // if we have force changes, we will remove the vm + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.Name.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + return + } + } } version := catalogManifest.Version @@ -187,18 +195,24 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques createMachineRequest.Owner = data.Owner.ValueString() } - response, vmDiag := apiclient.CreateVm(ctx, hostConfig, createMachineRequest) - if vmDiag.HasError() { - resp.Diagnostics.Append(vmDiag...) + createVmResponse, createVmResponseDiag := apiclient.CreateVm(ctx, hostConfig, createMachineRequest) + if createVmResponseDiag.HasError() { + common.EnsureMachineIsRemoved(ctx, hostConfig, data.Name.ValueString()) + resp.Diagnostics.Append(createVmResponseDiag...) return } - data.ID = types.StringValue(response.ID) + data.ID = types.StringValue(createVmResponse.ID) tflog.Info(ctx, "Created vm with id "+data.ID.ValueString()) - createdVM, vmDiag := apiclient.GetVm(ctx, hostConfig, response.ID) - if vmDiag.HasError() { - resp.Diagnostics.Append(vmDiag...) + createdVM, createVmResponseDiag := apiclient.GetVm(ctx, hostConfig, createVmResponse.ID) + if createVmResponseDiag.HasError() { + if data.ID.ValueString() != "" { + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + } + } + resp.Diagnostics.Append(createVmResponseDiag...) return } @@ -212,9 +226,14 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques // 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, vmDiag := common.EnsureMachineStopped(ctx, hostConfig, createdVM) - if vmDiag.HasError() { - resp.Diagnostics.Append(vmDiag...) + stoppedVm, createVmResponseDiag := common.EnsureMachineStopped(ctx, hostConfig, createdVM) + if createVmResponseDiag.HasError() { + if data.ID.ValueString() != "" { + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + } + } + resp.Diagnostics.Append(createVmResponseDiag...) } // Applying the Specs block @@ -222,13 +241,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if diags := common.SpecsBlockOnCreate(ctx, hostConfig, stoppedVm, data.Specs); diags.HasError() { resp.Diagnostics.Append(diags...) if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) } return } @@ -238,13 +253,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if vmBlockDiag := common.VmConfigBlockOnCreate(ctx, hostConfig, stoppedVm, data.Config); vmBlockDiag.HasError() { resp.Diagnostics.Append(vmBlockDiag...) if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) } return } @@ -253,13 +264,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if prlctlDiag := common.PrlCtlBlockOnCreate(ctx, hostConfig, stoppedVm, data.PrlCtl); prlctlDiag.HasError() { resp.Diagnostics.Append(prlctlDiag...) if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) } return } @@ -268,13 +275,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if sharedFolderDiag := common.SharedFoldersBlockOnCreate(ctx, hostConfig, stoppedVm, data.SharedFolder); sharedFolderDiag.HasError() { resp.Diagnostics.Append(sharedFolderDiag...) if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) } return } @@ -283,13 +286,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if postProcessDiag := common.RunPostProcessorScript(ctx, hostConfig, stoppedVm, data.PostProcessorScripts); postProcessDiag.HasError() { resp.Diagnostics.Append(postProcessDiag...) if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) } return } @@ -299,6 +298,11 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques rpHostConfig.HostId = stoppedVm.HostId rpHosts, updateDiag := updateReverseProxyHostsTarget(ctx, &data, rpHostConfig, stoppedVm) if updateDiag.HasError() { + if data.ID.ValueString() != "" { + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + } + } resp.Diagnostics.Append(updateDiag...) return } @@ -312,14 +316,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques } if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, rpHostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, rpHostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - - apiclient.DeleteVm(ctx, rpHostConfig, data.ID.ValueString()) } return } @@ -334,20 +333,21 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if _, diag := common.EnsureMachineRunning(ctx, hostConfig, stoppedVm); diag.HasError() { resp.Diagnostics.Append(diag...) if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) } return } - _, diag := apiclient.GetVm(ctx, hostConfig, response.ID) + _, diag := apiclient.GetVm(ctx, hostConfig, createVmResponse.ID) if diag.HasError() { resp.Diagnostics.Append(diag...) + if data.ID.ValueString() != "" { + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + } + } return } } else { @@ -355,13 +355,9 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { resp.Diagnostics.Append(diag...) if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) } return } @@ -413,6 +409,11 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques mappedObject, diag := result.MapObject(ctx) if diag.HasError() { resp.Diagnostics.Append(diag...) + if data.ID.ValueString() != "" { + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + } + } return } @@ -420,6 +421,11 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques listValue, diag := types.ListValue(result.ElementType(ctx), elements) if diag.HasError() { resp.Diagnostics.Append(diag...) + if data.ID.ValueString() != "" { + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + } + } return } @@ -431,6 +437,11 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques if (data.RunAfterCreate.ValueBool() || data.RunAfterCreate.IsUnknown() || data.RunAfterCreate.IsNull()) && (refreshVm.State == "stopped") { if _, diag := common.EnsureMachineRunning(ctx, hostConfig, refreshVm); diag.HasError() { resp.Diagnostics.Append(diag...) + if data.ID.ValueString() != "" { + if ensureRemoveDiag := common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()); ensureRemoveDiag.HasError() { + resp.Diagnostics.Append(ensureRemoveDiag...) + } + } return } } @@ -438,13 +449,7 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) if resp.Diagnostics.HasError() { if data.ID.ValueString() != "" { - // If we have an ID, we need to delete the machine - apiclient.SetMachineState(ctx, hostConfig, data.ID.ValueString(), apiclient.MachineStateOpStop) - if _, diag := common.EnsureMachineStopped(ctx, hostConfig, stoppedVm); diag.HasError() { - resp.Diagnostics.Append(diag...) - return - } - apiclient.DeleteVm(ctx, hostConfig, data.ID.ValueString()) + common.EnsureMachineIsRemoved(ctx, hostConfig, data.ID.ValueString()) } return } diff --git a/internal/ssh/client.go b/internal/ssh/client.go index ee90d24..45f14f9 100644 --- a/internal/ssh/client.go +++ b/internal/ssh/client.go @@ -173,3 +173,15 @@ func (c *SshClient) TransferFile(localFile, remoteFile string) error { return nil } + +func (c *SshClient) Close() error { + return nil +} + +func (c *SshClient) Username() string { + return c.Auth.User +} + +func (c *SshClient) Password() string { + return c.Auth.Password +}