diff --git a/docs/resources/clone_vm.md b/docs/resources/clone_vm.md index f26c7b1..962f573 100644 --- a/docs/resources/clone_vm.md +++ b/docs/resources/clone_vm.md @@ -149,6 +149,7 @@ Optional: Optional: +- `environment_variables` (Map of String) Environment variables that can be used in the DevOps service, please see documentation to see which variables are available - `inline` (List of String) Inline script - `retry` (Block, Optional) Retry settings (see [below for nested schema](#nestedblock--on_destroy_script--retry)) @@ -182,6 +183,7 @@ Optional: Optional: +- `environment_variables` (Map of String) Environment variables that can be used in the DevOps service, please see documentation to see which variables are available - `inline` (List of String) Inline script - `retry` (Block, Optional) Retry settings (see [below for nested schema](#nestedblock--post_processor_script--retry)) diff --git a/docs/resources/remote_vm.md b/docs/resources/remote_vm.md index 279a83a..eaa124e 100644 --- a/docs/resources/remote_vm.md +++ b/docs/resources/remote_vm.md @@ -162,6 +162,7 @@ Optional: Optional: +- `environment_variables` (Map of String) Environment variables that can be used in the DevOps service, please see documentation to see which variables are available - `inline` (List of String) Inline script - `retry` (Block, Optional) Retry settings (see [below for nested schema](#nestedblock--on_destroy_script--retry)) @@ -195,6 +196,7 @@ Optional: Optional: +- `environment_variables` (Map of String) Environment variables that can be used in the DevOps service, please see documentation to see which variables are available - `inline` (List of String) Inline script - `retry` (Block, Optional) Retry settings (see [below for nested schema](#nestedblock--post_processor_script--retry)) diff --git a/docs/resources/vagrant_box.md b/docs/resources/vagrant_box.md index 9e8f447..65c39be 100644 --- a/docs/resources/vagrant_box.md +++ b/docs/resources/vagrant_box.md @@ -168,6 +168,7 @@ Optional: Optional: +- `environment_variables` (Map of String) Environment variables that can be used in the DevOps service, please see documentation to see which variables are available - `inline` (List of String) Inline script - `retry` (Block, Optional) Retry settings (see [below for nested schema](#nestedblock--on_destroy_script--retry)) @@ -201,6 +202,7 @@ Optional: Optional: +- `environment_variables` (Map of String) Environment variables that can be used in the DevOps service, please see documentation to see which variables are available - `inline` (List of String) Inline script - `retry` (Block, Optional) Retry settings (see [below for nested schema](#nestedblock--post_processor_script--retry)) diff --git a/internal/apiclient/apimodels/vm_execute.go b/internal/apiclient/apimodels/vm_execute.go index 1e4d133..bf31a1c 100644 --- a/internal/apiclient/apimodels/vm_execute.go +++ b/internal/apiclient/apimodels/vm_execute.go @@ -1,7 +1,15 @@ package apimodels +type PostScriptItem struct { + Command string `json:"command"` + VirtualMachineId string `json:"virtual_machine_id"` + OS string `json:"os"` + EnvironmentVariables map[string]string `json:"environment_variables,omitempty"` +} + type VmExecuteCommandRequest struct { - Command string `json:"command"` + Command string `json:"command"` + EnvironmentVariables map[string]string `json:"environment_variables,omitempty"` } type VmExecuteCommandResponse struct { diff --git a/internal/apiclient/execute.go b/internal/apiclient/execute.go index 6e9e97c..885929a 100644 --- a/internal/apiclient/execute.go +++ b/internal/apiclient/execute.go @@ -12,14 +12,14 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" ) -func ExecuteScript(ctx context.Context, config HostConfig, machineId string, script string) (*apimodels.VmExecuteCommandResponse, diag.Diagnostics) { +func ExecuteScript(ctx context.Context, config HostConfig, r apimodels.PostScriptItem) (*apimodels.VmExecuteCommandResponse, diag.Diagnostics) { diagnostics := diag.Diagnostics{} urlHost := helpers.GetHostUrl(config.Host) var url string if config.IsOrchestrator { - url = fmt.Sprintf("%s/orchestrator/machines/%s/execute", helpers.GetHostApiVersionedBaseUrl(urlHost), machineId) + url = fmt.Sprintf("%s/orchestrator/machines/%s/execute", helpers.GetHostApiVersionedBaseUrl(urlHost), r.VirtualMachineId) } else { - url = fmt.Sprintf("%s/machines/%s/execute", helpers.GetHostApiVersionedBaseUrl(urlHost), machineId) + url = fmt.Sprintf("%s/machines/%s/execute", helpers.GetHostApiVersionedBaseUrl(urlHost), r.VirtualMachineId) } auth, err := authenticator.GetAuthenticator(ctx, urlHost, config.License, config.Authorization, config.DisableTlsValidation) @@ -27,8 +27,10 @@ func ExecuteScript(ctx context.Context, config HostConfig, machineId string, scr diagnostics.AddError("There was an error getting the authenticator", err.Error()) return nil, diagnostics } + request := apimodels.VmExecuteCommandRequest{ - Command: script, + Command: r.Command, + EnvironmentVariables: r.EnvironmentVariables, } client := helpers.NewHttpCaller(ctx, config.DisableTlsValidation) diff --git a/internal/common/check_required_specs.go b/internal/common/check_required_specs.go index 128c284..e8c981f 100644 --- a/internal/common/check_required_specs.go +++ b/internal/common/check_required_specs.go @@ -42,7 +42,7 @@ func CheckIfEnoughSpecs(ctx context.Context, hostConfig apiclient.HostConfig, sp } } if !foundArchitectureResources { - diagnostics.AddError("error getting hardware info", fmt.Sprintf("error getting hardware info for %s architecture", arch)) + diagnostics.AddError("Hardware", fmt.Sprintf("Did not find any hosts for %s architecture in the orchestrator, please check if you have any online", arch)) return diagnostics } } else { diff --git a/internal/common/post_processor_script.go b/internal/common/post_processor_script.go index ea9e81e..9335b17 100644 --- a/internal/common/post_processor_script.go +++ b/internal/common/post_processor_script.go @@ -51,10 +51,14 @@ 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)) - resultDiag := script.Apply(ctx, hostConfig, refreshVm.ID) + resultDiag := script.Apply(ctx, hostConfig, vm) tflog.Info(ctx, fmt.Sprintf("Script %s executed, result %v", script.Inline, resultDiag)) if resultDiag.HasError() { - return errors.New("script failed") + errorMessages := "Script failed to run:" + for _, diag := range resultDiag.Errors() { + errorMessages += "\n" + diag.Summary() + " " + diag.Detail() + } + return errors.New(errorMessages) } return nil diff --git a/internal/remoteimage/resource.go b/internal/remoteimage/resource.go index 6b4f457..f1d472f 100644 --- a/internal/remoteimage/resource.go +++ b/internal/remoteimage/resource.go @@ -123,6 +123,11 @@ func (r *RemoteVmResource) Create(ctx context.Context, req resource.CreateReques resp.Diagnostics.AddError("Catalog Not Found", fmt.Sprintf("Catalog %s was not found on %s", data.CatalogId.ValueString(), catalogHostConfig.Host)) return } + // the catalog manifest is nil, we will add an error to the diagnostics + if catalogManifest == nil { + resp.Diagnostics.AddError("Catalog Not Found", fmt.Sprintf("Catalog %s was not found on %s", data.CatalogId.ValueString(), catalogHostConfig.Host)) + return + } // Checking if the VM already exists in the host vm, diag := apiclient.GetVms(ctx, hostConfig, "Name", data.Name.String()) diff --git a/internal/schemas/postprocessorscript/post_processor_script.go b/internal/schemas/postprocessorscript/post_processor_script.go index 2ef1e6a..b1600aa 100644 --- a/internal/schemas/postprocessorscript/post_processor_script.go +++ b/internal/schemas/postprocessorscript/post_processor_script.go @@ -3,8 +3,10 @@ package postprocessorscript import ( "context" "fmt" + "strings" "terraform-provider-parallels-desktop/internal/apiclient" + "terraform-provider-parallels-desktop/internal/apiclient/apimodels" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -14,12 +16,13 @@ import ( ) type PostProcessorScript struct { - Inline types.List `tfsdk:"inline"` - Retry *PostProcessorScriptRetry `tfsdk:"retry"` - Result basetypes.ListValue `tfsdk:"result"` + Inline types.List `tfsdk:"inline"` + Retry *PostProcessorScriptRetry `tfsdk:"retry"` + EnvironmentVariables map[string]types.String `tfsdk:"environment_variables"` + Result basetypes.ListValue `tfsdk:"result"` } -func (s *PostProcessorScript) Apply(ctx context.Context, config apiclient.HostConfig, vmId string) diag.Diagnostics { +func (s *PostProcessorScript) Apply(ctx context.Context, config apiclient.HostConfig, vm *apimodels.VirtualMachine) diag.Diagnostics { diagnostic := diag.Diagnostics{} elements := make([]attr.Value, 0) t := PostProcessorScriptRunResult{} @@ -38,7 +41,22 @@ func (s *PostProcessorScript) Apply(ctx context.Context, config apiclient.HostCo return diagnostic } - resp, resultDiagnostic := apiclient.ExecuteScript(ctx, config, vmId, stringScript) + environmentVariables := map[string]string{} + + if len(s.EnvironmentVariables) > 0 { + for key, value := range s.EnvironmentVariables { + environmentVariables[key] = strings.TrimPrefix(strings.TrimSuffix(value.String(), "\""), "\"") + } + } + + request := apimodels.PostScriptItem{ + Command: stringScript, + EnvironmentVariables: environmentVariables, + VirtualMachineId: vm.ID, + OS: vm.OS, + } + + resp, resultDiagnostic := apiclient.ExecuteScript(ctx, config, request) if resultDiagnostic.HasError() { tflog.Error(ctx, fmt.Sprintf("Error executing script: %v", resultDiagnostic)) diagnostic = append(diagnostic, resultDiagnostic...) diff --git a/internal/schemas/postprocessorscript/schemas.go b/internal/schemas/postprocessorscript/schemas.go index 8ec3670..81222e5 100644 --- a/internal/schemas/postprocessorscript/schemas.go +++ b/internal/schemas/postprocessorscript/schemas.go @@ -5,64 +5,70 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -var SchemaName = "post_processor_script" -var SchemaBlock = schema.ListNestedBlock{ - MarkdownDescription: "Run any script after the virtual machine is created", - Description: "Run any script after the virtual machine is created", - NestedObject: schema.NestedBlockObject{ - Blocks: map[string]schema.Block{ - "retry": schema.SingleNestedBlock{ - MarkdownDescription: "Retry settings", - Attributes: map[string]schema.Attribute{ - "attempts": schema.Int64Attribute{ - MarkdownDescription: "Number of attempts", - Optional: true, - }, - "wait_between_attempts": schema.StringAttribute{ - MarkdownDescription: "Wait between attempts, you can use the suffixes 's' for seconds, 'm' for minutes", - Optional: true, - }, - }, - }, - }, - Attributes: map[string]schema.Attribute{ - "inline": schema.ListAttribute{ - MarkdownDescription: "Inline script", - Optional: true, - Description: "Inline script", - ElementType: types.StringType, - }, - - "result": schema.ListNestedAttribute{ - MarkdownDescription: "Result of the script", - Description: "Result of the script", - Computed: true, - Sensitive: true, - NestedObject: schema.NestedAttributeObject{ +var ( + SchemaName = "post_processor_script" + SchemaBlock = schema.ListNestedBlock{ + MarkdownDescription: "Run any script after the virtual machine is created", + Description: "Run any script after the virtual machine is created", + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "retry": schema.SingleNestedBlock{ + MarkdownDescription: "Retry settings", Attributes: map[string]schema.Attribute{ - "exit_code": schema.StringAttribute{ - MarkdownDescription: "Exit code", - Optional: true, - Description: "Exit code", - }, - "stdout": schema.StringAttribute{ - MarkdownDescription: "Stdout", + "attempts": schema.Int64Attribute{ + MarkdownDescription: "Number of attempts", Optional: true, - Description: "Stdout", }, - "stderr": schema.StringAttribute{ - MarkdownDescription: "Stderr", + "wait_between_attempts": schema.StringAttribute{ + MarkdownDescription: "Wait between attempts, you can use the suffixes 's' for seconds, 'm' for minutes", Optional: true, - Description: "Stderr", }, - "script": schema.StringAttribute{ - MarkdownDescription: "Script", - Optional: true, - Description: "Script", + }, + }, + }, + Attributes: map[string]schema.Attribute{ + "inline": schema.ListAttribute{ + MarkdownDescription: "Inline script", + Optional: true, + Description: "Inline script", + ElementType: types.StringType, + }, + "environment_variables": schema.MapAttribute{ + MarkdownDescription: "Environment variables that can be used in the DevOps service, please see documentation to see which variables are available", + Optional: true, + ElementType: types.StringType, + }, + "result": schema.ListNestedAttribute{ + MarkdownDescription: "Result of the script", + Description: "Result of the script", + Computed: true, + Sensitive: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "exit_code": schema.StringAttribute{ + MarkdownDescription: "Exit code", + Optional: true, + Description: "Exit code", + }, + "stdout": schema.StringAttribute{ + MarkdownDescription: "Stdout", + Optional: true, + Description: "Stdout", + }, + "stderr": schema.StringAttribute{ + MarkdownDescription: "Stderr", + Optional: true, + Description: "Stderr", + }, + "script": schema.StringAttribute{ + MarkdownDescription: "Script", + Optional: true, + Description: "Script", + }, }, }, }, }, }, - }, -} + } +)