Skip to content

Commit

Permalink
Merge pull request #22 from wttech/script-inline-support
Browse files Browse the repository at this point in the history
Advanced script execution
  • Loading branch information
krystian-panek-vmltech authored Nov 16, 2023
2 parents d7a8d60 + d8e22fe commit 0b26094
Show file tree
Hide file tree
Showing 14 changed files with 568 additions and 369 deletions.
6 changes: 6 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ default: testacc
.PHONY: testacc
testacc:
TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m

lint:
golangci-lint run ./...

generate:
go generate ./...
50 changes: 27 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,34 @@ resource "aem_instance" "single" {
}
system {
data_dir = local.aem_single_compose_dir
bootstrap_script = <<SHELL
#!/bin/sh
(
echo "Mounting EBS volume into data directory"
sudo mkfs -t ext4 ${local.aem_single_data_device} && \
sudo mkdir -p ${local.aem_single_data_dir} && \
sudo mount ${local.aem_single_data_device} ${local.aem_single_data_dir} && \
sudo chown -R ${local.ssh_user} ${local.aem_single_data_dir} && \
echo '${local.aem_single_data_device} ${local.aem_single_data_dir} ext4 defaults 0 0' | sudo tee -a /etc/fstab
) && (
echo "Copying AEM library files"
sudo yum install -y unzip && \
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
unzip -q awscliv2.zip && \
sudo ./aws/install --update && \
mkdir -p "${local.aem_single_compose_dir}/aem/home/lib" && \
aws s3 cp --recursive --no-progress "s3://aemc/instance/classic/" "${local.aem_single_compose_dir}/aem/home/lib"
)
SHELL
data_dir = local.aem_single_compose_dir
bootstrap = {
inline = [
// mounting AWS EBS volume into data directory
"sudo mkfs -t ext4 ${local.aem_single_data_device}",
"sudo mkdir -p ${local.aem_single_data_dir}",
"sudo mount ${local.aem_single_data_device} ${local.aem_single_data_dir}",
"sudo chown -R ${local.ssh_user} ${local.aem_single_data_dir}",
"echo '${local.aem_single_data_device} ${local.aem_single_data_dir} ext4 defaults 0 0' | sudo tee -a /etc/fstab",
// installing AWS CLI
"sudo yum install -y unzip",
"curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o 'awscliv2.zip'",
"unzip -q awscliv2.zip",
"sudo ./aws/install --update",
]
}
}
compose {
create = {
inline = [
"mkdir -p '${local.aem_single_compose_dir}/aem/home/lib'",
"aws s3 cp --recursive --no-progress 's3://aemc/instance/classic/' '${local.aem_single_compose_dir}/aem/home/lib'",
"sh aemw instance init",
"sh aemw instance create",
]
}
}
compose {}
}
locals {
Expand All @@ -100,7 +105,6 @@ locals {
output "aem_instances" {
value = aem_instance.single.instances
}
```

## Requirements
Expand Down
48 changes: 42 additions & 6 deletions docs/resources/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ AEM Instance resource
### Optional

- `client` (Block, Optional) (see [below for nested schema](#nestedblock--client))
- `compose` (Block, Optional) (see [below for nested schema](#nestedblock--compose))
- `compose` (Block, Optional) AEM Compose CLI configuration (see [below for nested schema](#nestedblock--compose))
- `files` (Map of String) Files or directories to be copied into the machine
- `system` (Block, Optional) (see [below for nested schema](#nestedblock--system))

Expand All @@ -44,26 +44,62 @@ Optional:

Optional:

- `config` (String) Contents of the AEM Compose YML configuration file.
- `create_script` (String) Creates the instance or restores from backup. Forces instance recreation if changed.
- `delete_script` (String) Deletes the instance.
- `config` (String) Contents o f the AEM Compose YML configuration file.
- `create` (Attributes) Creates the instance or restores from backup, typically customized to provide AEM library files (quickstart.jar, license.properties, service packs) from alternative sources (e.g., AWS S3, Azure Blob Storage). Instance recreation is forced if changed. (see [below for nested schema](#nestedatt--compose--create))
- `delete` (Attributes) Deletes the instance. (see [below for nested schema](#nestedatt--compose--delete))
- `download` (Boolean) Toggle automatic AEM Compose CLI wrapper download. If set to false, assume the wrapper is present in the data directory.
- `launch_script` (String) Configures launched instance. Must be idempotent as it is executed always when changed. Typically used for setting up replication agents, installing service packs, etc.
- `launch` (Attributes) Configures launched instance. Must be idempotent as it is executed always when changed. Typically used for installing AEM service packs, setting up replication agents, etc. (see [below for nested schema](#nestedatt--compose--launch))
- `version` (String) Version of AEM Compose tool to use on remote AEM machine.

<a id="nestedatt--compose--create"></a>
### Nested Schema for `compose.create`

Optional:

- `inline` (List of String) Inline shell commands to be executed
- `script` (String) Multiline shell script to be executed


<a id="nestedatt--compose--delete"></a>
### Nested Schema for `compose.delete`

Optional:

- `inline` (List of String) Inline shell commands to be executed
- `script` (String) Multiline shell script to be executed


<a id="nestedatt--compose--launch"></a>
### Nested Schema for `compose.launch`

Optional:

- `inline` (List of String) Inline shell commands to be executed
- `script` (String) Multiline shell script to be executed



<a id="nestedblock--system"></a>
### Nested Schema for `system`

Optional:

- `bootstrap_script` (String) Script executed once after connecting to the instance. Typically used for: providing AEM library files (quickstart.jar, license.properties, service packs), mounting data volume, etc. Forces instance recreation if changed.
- `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 VM. (see [below for nested schema](#nestedatt--system--bootstrap))
- `data_dir` (String) Remote root path in which AEM Compose files and unpacked instances will be stored
- `env` (Map of String) Environment variables for AEM instances
- `service_config` (String) Contents of the AEM 'systemd' service definition file
- `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 in which AEM Compose TF provider temporary files will be stored

<a id="nestedatt--system--bootstrap"></a>
### Nested Schema for `system.bootstrap`

Optional:

- `inline` (List of String) Inline shell commands to be executed
- `script` (String) Multiline shell script to be executed



<a id="nestedatt--instances"></a>
### Nested Schema for `instances`
Expand Down
47 changes: 26 additions & 21 deletions examples/ssh/aem.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,34 @@ resource "aem_instance" "single" {
}

system {
data_dir = local.aem_single_compose_dir
bootstrap_script = <<SHELL
#!/bin/sh
(
echo "Mounting EBS volume into data directory"
sudo mkfs -t ext4 ${local.aem_single_data_device} && \
sudo mkdir -p ${local.aem_single_data_dir} && \
sudo mount ${local.aem_single_data_device} ${local.aem_single_data_dir} && \
sudo chown -R ${local.ssh_user} ${local.aem_single_data_dir} && \
echo '${local.aem_single_data_device} ${local.aem_single_data_dir} ext4 defaults 0 0' | sudo tee -a /etc/fstab
) && (
echo "Copying AEM library files"
sudo yum install -y unzip && \
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
unzip -q awscliv2.zip && \
sudo ./aws/install --update && \
mkdir -p "${local.aem_single_compose_dir}/aem/home/lib" && \
aws s3 cp --recursive --no-progress "s3://aemc/instance/classic/" "${local.aem_single_compose_dir}/aem/home/lib"
)
SHELL
data_dir = local.aem_single_compose_dir
bootstrap = {
inline = [
// mounting AWS EBS volume into data directory
"sudo mkfs -t ext4 ${local.aem_single_data_device}",
"sudo mkdir -p ${local.aem_single_data_dir}",
"sudo mount ${local.aem_single_data_device} ${local.aem_single_data_dir}",
"sudo chown -R ${local.ssh_user} ${local.aem_single_data_dir}",
"echo '${local.aem_single_data_device} ${local.aem_single_data_dir} ext4 defaults 0 0' | sudo tee -a /etc/fstab",
// installing AWS CLI
"sudo yum install -y unzip",
"curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o 'awscliv2.zip'",
"unzip -q awscliv2.zip",
"sudo ./aws/install --update",
]
}
}

compose {} // must be at least empty
compose {
create = {
inline = [
"mkdir -p '${local.aem_single_compose_dir}/aem/home/lib'",
"aws s3 cp --recursive --no-progress 's3://aemc/instance/classic/' '${local.aem_single_compose_dir}/aem/home/lib'",
"sh aemw instance init",
"sh aemw instance create",
]
}
}
}

locals {
Expand Down
37 changes: 22 additions & 15 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,20 @@ func (c Client) envScriptString() string {
return utils.EnvToScript(c.Env)
}

func (c Client) RunShellCommand(cmd string) ([]byte, error) {
return c.RunShellPurely(fmt.Sprintf("source %s && %s", c.envScriptPath(), cmd))
}

func (c Client) RunShellScript(cmdName string, cmdScript string, dir string) ([]byte, error) {
remotePath := fmt.Sprintf("%s/%s.sh", c.WorkDir, cmdName)
if err := c.FileWrite(remotePath, cmdScript); err != nil {
return nil, fmt.Errorf("cannot write temporary script at remote path '%s': %w", remotePath, err)
}
defer func() { _ = c.FileDelete(remotePath) }()
return c.RunShellCommand(fmt.Sprintf("cd %s && sh %s", dir, remotePath))
defer func() { _ = c.PathDelete(remotePath) }()
return c.RunShellCommand(fmt.Sprintf("sh %s", remotePath), dir)
}

func (c Client) RunShellCommand(cmd string, dir string) ([]byte, error) {
if dir == "" || dir == "." {
return c.RunShellPurely(fmt.Sprintf("source %s && %s", c.envScriptPath(), cmd))
}
return c.RunShellPurely(fmt.Sprintf("source %s && cd %s && %s", c.envScriptPath(), dir, cmd))
}

func (c Client) RunShellPurely(cmd string) ([]byte, error) {
Expand All @@ -113,7 +116,7 @@ func (c Client) RunShellPurely(cmd string) ([]byte, error) {
}
out, err := cmdObj.CombinedOutput()
if err != nil {
if len(out) > 0 { // TODO rethink error handling
if len(out) > 0 {
return nil, fmt.Errorf("cannot run command '%s': %w\n\n%s", cmdObj, err, string(out))
}
return nil, err
Expand Down Expand Up @@ -187,13 +190,6 @@ func (c Client) DirCopy(localPath string, remotePath string, override bool) erro
return nil
}

func (c Client) FileDelete(path string) error {
if _, err := c.RunShellPurely(fmt.Sprintf("rm -rf %s", path)); err != nil {
return fmt.Errorf("cannot delete file '%s': %w", path, err)
}
return nil
}

func (c Client) FileCopy(localPath string, remotePath string, override bool) error {
if !override {
exists, err := c.FileExists(remotePath)
Expand All @@ -213,7 +209,11 @@ func (c Client) FileCopy(localPath string, remotePath string, override bool) err
} else {
remoteTmpPath = fmt.Sprintf("%s.tmp", remotePath)
}
defer func() { _ = c.FileDelete(remoteTmpPath) }()
err := c.PathDelete(remoteTmpPath)
if err != nil {
return err
}
defer func() { _ = c.PathDelete(remoteTmpPath) }()
if err := c.connection.CopyFile(localPath, remoteTmpPath); err != nil {
return err
}
Expand All @@ -234,6 +234,13 @@ func (c Client) PathCopy(localPath string, remotePath string, override bool) err
return c.FileCopy(localPath, remotePath, override)
}

func (c Client) PathDelete(path string) error {
if _, err := c.RunShellPurely(fmt.Sprintf("rm -rf %s", path)); err != nil {
return fmt.Errorf("cannot delete file '%s': %w", path, err)
}
return nil
}

func (c Client) FileWrite(remotePath string, text string) error {
file, err := os.CreateTemp(os.TempDir(), "tf-provider-aem-*.tmp")
path := file.Name()
Expand Down
File renamed without changes.
23 changes: 23 additions & 0 deletions internal/provider/instance/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package instance

import _ "embed"

//go:embed aem.yml
var ConfigYML string

//go:embed systemd.conf
var ServiceConf string

var CreateScriptInline = []string{
`sh aemw instance init`,
`sh aemw instance create`,
}

var LaunchScriptInline = []string{
`sh aemw osgi config save --pid 'org.apache.sling.jcr.davex.impl.servlets.SlingDavExServlet' --input-string 'alias: /crx/server'`,
`sh aemw repl agent setup -A --location 'author' --name 'publish' --input-string '{enabled: true, transportUri: "http://localhost:4503/bin/receive?sling:authRequestLogin=1", transportUser: admin, transportPassword: admin, userId: admin}'`,
}

var DeleteScriptInline = []string{
`sh aemw instance delete`,
}
4 changes: 0 additions & 4 deletions internal/provider/instance/create.sh

This file was deleted.

3 changes: 0 additions & 3 deletions internal/provider/instance/delete.sh

This file was deleted.

18 changes: 0 additions & 18 deletions internal/provider/instance/embed.go

This file was deleted.

10 changes: 0 additions & 10 deletions internal/provider/instance/launch.sh

This file was deleted.

Loading

0 comments on commit 0b26094

Please sign in to comment.