diff --git a/examples/ssh/aem.tf b/examples/ssh/aem.tf index d3e4244..75ee3d2 100644 --- a/examples/ssh/aem.tf +++ b/examples/ssh/aem.tf @@ -11,18 +11,13 @@ resource "aem_instance" "single" { } } compose { - version = "1.5.7" + version = "1.5.8" data_dir = "/home/ec2-user/aemc" lib_dir = "aem/home/lib" config_file = "aem/default/etc/aem.yml" } } - output "aem_instances" { value = aem_instance.single.instances } - -output "aem_config_file_checksum" { - value = aem_instance.single.compose.config_file_checksum -} diff --git a/examples/ssh/aem/default/etc/aem.yml b/examples/ssh/aem/default/etc/aem.yml index 2254d82..faded7c 100644 --- a/examples/ssh/aem/default/etc/aem.yml +++ b/examples/ssh/aem/default/etc/aem.yml @@ -18,11 +18,13 @@ instance: - -Duser.country=US - -Duser.timezone=UTC start_opts: [] + secret_vars: - ACME_SECRET=value env_vars: - ACME_VAR=value sling_props: [] + local_publish: active: true http_url: http://127.0.0.1:4503 diff --git a/internal/provider/instance/plan_modifiers.go b/internal/provider/instance/plan_modifiers.go new file mode 100644 index 0000000..e3a07e2 --- /dev/null +++ b/internal/provider/instance/plan_modifiers.go @@ -0,0 +1,42 @@ +package instance + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/wttech/terraform-provider-aem/internal/utils" +) + +func ConfigFileChecksumPlanModifier() planmodifier.String { + return &configFileChecksumPlanModifier{} +} + +type configFileChecksumPlanModifier struct{} + +func (m *configFileChecksumPlanModifier) Description(ctx context.Context) string { + return "Updates AEM configuration file checksum when contents change." +} + +func (m *configFileChecksumPlanModifier) MarkdownDescription(ctx context.Context) string { + return m.Description(ctx) +} + +func (m *configFileChecksumPlanModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { + var configFile types.String + diags := req.Plan.GetAttribute(ctx, path.Root("compose").AtName("config_file"), &configFile) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !configFile.IsNull() { + configFilePath := configFile.ValueString() + checksum, err := utils.HashFileMD5(configFilePath) + if err != nil { + resp.Diagnostics.AddError("Unable to calculate checksum of AEM configuration file", fmt.Sprintf("path '%s', error: %s", configFilePath, err)) + return + } + resp.PlanValue = types.StringValue(checksum) + } +} diff --git a/internal/provider/instance_resource.go b/internal/provider/instance_resource.go index 627936e..839cbda 100644 --- a/internal/provider/instance_resource.go +++ b/internal/provider/instance_resource.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/wttech/terraform-provider-aem/internal/client" + "github.com/wttech/terraform-provider-aem/internal/provider/instance" "time" ) @@ -106,7 +107,8 @@ func (r *InstanceResource) Schema(ctx context.Context, req resource.SchemaReques Default: stringdefault.StaticString("aem/default/etc/aem.yml"), }, "config_file_checksum": schema.StringAttribute{ - Computed: true, + Computed: true, + PlanModifiers: []planmodifier.String{instance.ConfigFileChecksumPlanModifier()}, }, "lib_dir": schema.StringAttribute{ MarkdownDescription: "Local path to directory from which AEM library files will be copied to the remote AEM machine", @@ -150,9 +152,10 @@ func (r *InstanceResource) Schema(ctx context.Context, req resource.SchemaReques }, }, PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), listplanmodifier.RequiresReplaceIf(func(ctx context.Context, request planmodifier.ListRequest, response *listplanmodifier.RequiresReplaceIfFuncResponse) { // TODO check if: [1] list is not empty; [2] the same instances are still created; [3] dirs have not changed - response.RequiresReplace = true + // response.RequiresReplace = true }, "If the value of this attribute changes, Terraform will destroy and recreate the resource.", "If the value of this attribute changes, Terraform will destroy and recreate the resource."), }, }, @@ -200,16 +203,9 @@ func (r *InstanceResource) createOrUpdate(ctx context.Context, plan *tfsdk.Plan, return } - md5, err := hashFileMD5(model.Compose.ConfigFile.ValueString()) - if err != nil { - diags.AddError("Unable to calculate MD5 checksum for AEM configuration file", fmt.Sprintf("%s", err)) - return - } - model.Compose.ConfigFileChecksum = types.StringValue(md5) - tflog.Info(ctx, "Started setting up AEM instance resource") - ic, err := r.Client(ctx, model, time.Minute*5) + ic, err := r.client(ctx, model, time.Minute*5) if err != nil { diags.AddError("Unable to connect to AEM instance", fmt.Sprintf("%s", err)) return @@ -301,14 +297,7 @@ func (r *InstanceResource) Read(ctx context.Context, req resource.ReadRequest, r return } - md5, err := hashFileMD5(model.Compose.ConfigFile.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Unable to calculate MD5 checksum for AEM configuration file", fmt.Sprintf("%s", err)) - return - } - model.Compose.ConfigFileChecksum = types.StringValue(md5) - - ic, err := r.Client(ctx, model, time.Second*15) + ic, err := r.client(ctx, model, time.Second*15) if err != nil { resp.Diagnostics.AddWarning("Unable to connect to AEM instance", fmt.Sprintf("%s", err)) } else { @@ -345,10 +334,11 @@ func (r *InstanceResource) Delete(ctx context.Context, req resource.DeleteReques } func (r *InstanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // TODO implement it properly resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } -func (r *InstanceResource) Client(ctx context.Context, model InstanceResourceModel, timeout time.Duration) (*InstanceClient, error) { +func (r *InstanceResource) client(ctx context.Context, model InstanceResourceModel, timeout time.Duration) (*InstanceClient, error) { tflog.Info(ctx, "Connecting to AEM instance machine") typeName := model.Client.Type.ValueString() diff --git a/internal/provider/provider_utils.go b/internal/utils/file.go similarity index 89% rename from internal/provider/provider_utils.go rename to internal/utils/file.go index df06f66..83dc1e7 100644 --- a/internal/provider/provider_utils.go +++ b/internal/utils/file.go @@ -1,4 +1,4 @@ -package provider +package utils import ( "crypto/md5" @@ -8,7 +8,7 @@ import ( ) // TODO hash with ignoring line endings / OS-independent -func hashFileMD5(file string) (string, error) { +func HashFileMD5(file string) (string, error) { // Open the file f, err := os.Open(file) if err != nil {