diff --git a/docs/resources/instance.md b/docs/resources/instance.md index a4eb6fc..612beaa 100644 --- a/docs/resources/instance.md +++ b/docs/resources/instance.md @@ -101,6 +101,7 @@ Optional: - `bootstrap` (Attributes) Script executed once upon instance connection, often for mounting on VM data volumes from attached disks (e.g., AWS EBS, Azure Disk Storage). This script runs only once, even during instance recreation, as changes are typically persistent and system-wide. If re-execution is needed, it is recommended to set up a new machine. (see [below for nested schema](#nestedatt--system--bootstrap)) - `data_dir` (String) Remote root path in which AEM Compose files and unpacked AEM instances will be stored. - `env` (Map of String) Environment variables for AEM instances. +- `service_enabled` (Boolean) Enabled the AEM system service (systemd). - `service_config` (String) Contents of the AEM system service definition file (systemd). - `user` (String) System user under which AEM instance will be running. By default, the same as the user used to connect to the machine. - `work_dir` (String) Remote root path where provider-related files will be stored. diff --git a/examples/aws_ssm/aws.tf b/examples/aws_ssm/aws.tf index 76472af..1ee3150 100644 --- a/examples/aws_ssm/aws.tf +++ b/examples/aws_ssm/aws.tf @@ -3,10 +3,10 @@ resource "aws_instance" "aem_single" { instance_type = "m5.xlarge" iam_instance_profile = aws_iam_instance_profile.aem_ec2.name tags = local.tags - user_data = trimspace(<= 11, < 12" + + # Pre-installed local JDK dir + # a) keep it empty to download open source Java automatically for current OS and architecture + # b) set it to absolute path or to env var '[[.Env.JAVA_HOME]]' to indicate where closed source Java like Oracle is installed + home_dir: "" + + # Auto-installed JDK options + download: + # Source URL with template vars support + url: "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.18%2B10/OpenJDK11U-jdk_[[.Arch]]_[[.Os]]_hotspot_11.0.18_10.[[.ArchiveExt]]" + # Map source URL template vars to be compatible with Adoptium Java + replacements: + # Var 'Os' (GOOS) + "darwin": "mac" + # Var 'Arch' (GOARCH) + "x86_64": "x64" + "amd64": "x64" + "386": "x86-32" + # enforce non-ARM Java as some AEM features are not working on ARM (e.g Scene7) + "arm64": "x64" + "aarch64": "x64" + +base: + # Location of temporary files (downloaded AEM packages, etc) + tmp_dir: aem/home/tmp + # Location of supportive tools (downloaded Java, OakRun, unpacked AEM SDK) + tool_dir: aem/home/opt + +log: + level: info + timestamp_format: "2006-01-02 15:04:05" + full_timestamp: true + +input: + format: yml + file: STDIN + +output: + format: text + log: + # File path of logs written especially when output format is different than 'text' + file: aem/home/var/log/aem.log + # Controls where outputs and logs should be written to when format is 'text' (console|file|both) + mode: both diff --git a/examples/local/main.tf b/examples/local/main.tf new file mode 100644 index 0000000..44c4611 --- /dev/null +++ b/examples/local/main.tf @@ -0,0 +1,10 @@ +terraform { + required_providers { + aem = { + source = "registry.terraform.io/wttech/aem" + version = "< 2.0.0" + } + } +} + +provider "aem" {} diff --git a/internal/client/client_manager.go b/internal/client/client_manager.go index 8f37b5d..02d69bb 100644 --- a/internal/client/client_manager.go +++ b/internal/client/client_manager.go @@ -48,6 +48,8 @@ func (c ClientManager) connection(typeName string, settings map[string]string) ( commandWaitMin: cast.ToDuration(settings["command_wait_min"]), commandWaitMax: cast.ToDuration(settings["command_wait_max"]), }, nil + case "local": + return &LocalConnection{}, nil } return nil, fmt.Errorf("unknown AEM client type: %s", typeName) } diff --git a/internal/client/connection_local.go b/internal/client/connection_local.go new file mode 100644 index 0000000..7637417 --- /dev/null +++ b/internal/client/connection_local.go @@ -0,0 +1,46 @@ +package client + +import ( + "fmt" + "os/exec" + "strings" +) + +type LocalConnection struct { +} + +func (a *LocalConnection) Info() string { + return "local environment" +} + +func (a *LocalConnection) User() string { + return "" +} + +func (a *LocalConnection) Connect() error { + return nil +} + +func (a *LocalConnection) Disconnect() error { + return nil +} + +func (a *LocalConnection) Command(cmdLine []string) ([]byte, error) { + var alterCmdLine []string + for _, cmdElem := range cmdLine { + alterCmdLine = append(alterCmdLine, strings.Trim(cmdElem, `"`)) + } + cmd := exec.Command(alterCmdLine[0], alterCmdLine[1:]...) + output, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("local: error executing command: %v", err) + } + return output, nil +} + +func (a *LocalConnection) CopyFile(localPath string, remotePath string) error { + cmd := fmt.Sprintf("cp %s %s", localPath, remotePath) + cmdLine := []string{"sh", "-c", "\"" + cmd + "\""} + _, err := a.Command(cmdLine) + return err +} diff --git a/internal/provider/instance_client.go b/internal/provider/instance_client.go index fab7aa9..25882d2 100644 --- a/internal/provider/instance_client.go +++ b/internal/provider/instance_client.go @@ -86,8 +86,15 @@ func (ic *InstanceClient) create() error { return nil } +func (ic *InstanceClient) serviceName() string { + if ic.data.System.ServiceName.ValueString() != "" { + return ic.data.System.ServiceName.ValueString() + } + return ServiceName +} + func (ic *InstanceClient) saveProfileScript() error { - envFile := fmt.Sprintf("/etc/profile.d/%s.sh", ServiceName) + envFile := fmt.Sprintf("/etc/profile.d/%s.sh", ic.serviceName()) var systemEnvMap map[string]string ic.data.System.Env.ElementsAs(ic.ctx, &systemEnvMap, true) @@ -106,6 +113,10 @@ func (ic *InstanceClient) saveProfileScript() error { } func (ic *InstanceClient) configureService() error { + if !ic.data.System.ServiceEnabled.ValueBool() { + return nil + } + user := ic.data.System.User.ValueString() if user == "" { user = ic.cl.Connection().User() @@ -122,7 +133,7 @@ func (ic *InstanceClient) configureService() error { if err != nil { return fmt.Errorf("unable to template AEM system service definition: %w", err) } - serviceFile := fmt.Sprintf("/etc/systemd/system/%s.service", ServiceName) + serviceFile := fmt.Sprintf("/etc/systemd/system/%s.service", ic.serviceName()) if err := ic.cl.FileWrite(serviceFile, serviceTemplated); err != nil { return fmt.Errorf("unable to write AEM system service definition '%s': %w", serviceFile, err) } @@ -134,10 +145,14 @@ func (ic *InstanceClient) configureService() error { } func (ic *InstanceClient) runServiceAction(action string) error { + if !ic.data.System.ServiceEnabled.ValueBool() { + return nil + } + ic.cl.Sudo = true defer func() { ic.cl.Sudo = false }() - outBytes, err := ic.cl.RunShellCommand(fmt.Sprintf("systemctl %s %s.service", action, ServiceName), ".") + outBytes, err := ic.cl.RunShellCommand(fmt.Sprintf("systemctl %s %s.service", action, ic.serviceName()), ".") if err != nil { return fmt.Errorf("unable to perform AEM system service action '%s': %w", action, err) } diff --git a/internal/provider/instance_model.go b/internal/provider/instance_model.go index d1314f4..e0cc979 100644 --- a/internal/provider/instance_model.go +++ b/internal/provider/instance_model.go @@ -30,12 +30,14 @@ type InstanceResourceModel struct { } `tfsdk:"client"` Files types.Map `tfsdk:"files"` System struct { - DataDir types.String `tfsdk:"data_dir"` - WorkDir types.String `tfsdk:"work_dir"` - Env types.Map `tfsdk:"env"` - ServiceConfig types.String `tfsdk:"service_config"` - User types.String `tfsdk:"user"` - Bootstrap InstanceScript `tfsdk:"bootstrap"` + DataDir types.String `tfsdk:"data_dir"` + WorkDir types.String `tfsdk:"work_dir"` + Env types.Map `tfsdk:"env"` + ServiceEnabled types.Bool `tfsdk:"service_enabled"` + ServiceName types.String `tfsdk:"service_name"` + ServiceConfig types.String `tfsdk:"service_config"` + User types.String `tfsdk:"user"` + Bootstrap InstanceScript `tfsdk:"bootstrap"` } `tfsdk:"system"` Compose struct { Download types.Bool `tfsdk:"download"` @@ -169,6 +171,18 @@ func (r *InstanceResource) Schema(ctx context.Context, req resource.SchemaReques Optional: true, Default: stringdefault.StaticString("/tmp/aemc"), }, + "service_enabled": schema.BoolAttribute{ + MarkdownDescription: "Enabled the AEM system service (systemd).", + Optional: true, + Computed: true, + Default: booldefault.StaticBool(true), + }, + "service_name": schema.StringAttribute{ + MarkdownDescription: "Name of the AEM system service (systemd).", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, "service_config": schema.StringAttribute{ MarkdownDescription: "Contents of the AEM system service definition file (systemd).", Optional: true,