Skip to content

Commit

Permalink
Callbacks names and minor API imprs
Browse files Browse the repository at this point in the history
  • Loading branch information
krystian-panek-vmltech committed Nov 9, 2023
1 parent d98c058 commit 73bee1b
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 71 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,6 @@ require (
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.1 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
78 changes: 43 additions & 35 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,13 @@ func (c Client) Connection() Connection {
return c.connection
}

func (c Client) Run(cmdLine []string) (*goph.Cmd, error) {
func (c Client) Command(cmdLine []string) (*goph.Cmd, error) {
return c.connection.Command(cmdLine)
}

func (c Client) SetupEnv() error {
file, err := os.CreateTemp(os.TempDir(), "tf-provider-aem-env-*.sh")
path := file.Name()
defer func() { _ = file.Close(); _ = os.Remove(path) }()
if err != nil {
return fmt.Errorf("cannot create temporary file for remote shell environment script: %w", err)
}
if _, err := file.WriteString(c.envScriptString()); err != nil {
return fmt.Errorf("cannot write temporary file for remote shell environment script: %w", err)
}
if err := c.FileCopy(path, c.envScriptPath(), true); err != nil {
return err
if err := c.FileWrite(c.envScriptPath(), c.envScriptString()); err != nil {
return fmt.Errorf("cannot setup environment script: %w", err)
}
return nil
}
Expand All @@ -101,29 +92,20 @@ func (c Client) envScriptString() string {
return sb.String()
}

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

func (c Client) RunShellScriptWithEnv(dir string, cmdScript string) ([]byte, error) {
file, err := os.CreateTemp(os.TempDir(), "tf-provider-aem-script-*.sh")
path := file.Name()
defer func() { _ = file.Close(); _ = os.Remove(path) }()
if err != nil {
return nil, fmt.Errorf("cannot create temporary file for remote shell script: %w", err)
}
if _, err := file.WriteString(cmdScript); err != nil {
return nil, fmt.Errorf("cannot write temporary file for remote shell script: %w", err)
func (c Client) RunShellScript(cmdName string, cmdScript string, dir string) ([]byte, error) {
remotePath := fmt.Sprintf("%s/%s.sh", c.EnvDir, cmdName)
if err := c.FileWrite(remotePath, cmdScript); err != nil {
return nil, fmt.Errorf("cannot write temporary script at remote path '%s': %w", remotePath, err)
}
remotePath := fmt.Sprintf("%s/%s", c.EnvDir, filepath.Base(file.Name()))
defer func() { _ = c.FileDelete(remotePath) }()
if err := c.FileCopy(path, remotePath, true); err != nil {
return nil, err
}
return c.RunShellWithEnv(fmt.Sprintf("cd %s && sh %s", dir, remotePath))
return c.RunShellCommand(fmt.Sprintf("cd %s && sh %s", dir, remotePath))
}

func (c Client) RunShell(cmd string) ([]byte, error) {
func (c Client) RunShellPurely(cmd string) ([]byte, error) {
cmdObj, err := c.connection.Command([]string{"sh", "-c", "\"" + cmd + "\""})
if err != nil {
return nil, fmt.Errorf("cannot create command '%s': %w", cmd, err)
Expand All @@ -139,15 +121,15 @@ func (c Client) RunShell(cmd string) ([]byte, error) {
}

func (c Client) DirEnsure(path string) error {
_, err := c.RunShell(fmt.Sprintf("mkdir -p %s", path))
_, err := c.RunShellPurely(fmt.Sprintf("mkdir -p %s", path))
if err != nil {
return fmt.Errorf("cannot ensure directory '%s': %w", path, err)
}
return nil
}

func (c Client) FileExists(path string) (bool, error) {
out, err := c.RunShell(fmt.Sprintf("test -f %s && echo '0' || echo '1'", path))
out, err := c.RunShellPurely(fmt.Sprintf("test -f %s && echo '0' || echo '1'", path))
if err != nil {
return false, fmt.Errorf("cannot check if file exists '%s': %w", path, err)
}
Expand All @@ -158,14 +140,14 @@ func (c Client) FileMove(oldPath string, newPath string) error {
if err := c.DirEnsure(filepath.Dir(newPath)); err != nil {
return err
}
if _, err := c.RunShell(fmt.Sprintf("mv %s %s", oldPath, newPath)); err != nil {
if _, err := c.RunShellPurely(fmt.Sprintf("mv %s %s", oldPath, newPath)); err != nil {
return fmt.Errorf("cannot move file '%s' to '%s': %w", oldPath, newPath, err)
}
return nil
}

func (c Client) DirExists(path string) (bool, error) {
out, err := c.RunShell(fmt.Sprintf("test -d %s && echo '0' || echo '1'", path))
out, err := c.RunShellPurely(fmt.Sprintf("test -d %s && echo '0' || echo '1'", path))
if err != nil {
return false, fmt.Errorf("cannot check if directory exists '%s': %w", path, err)
}
Expand Down Expand Up @@ -197,13 +179,12 @@ func (c Client) DirCopy(localPath string, remotePath string, override bool) erro
}

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

// TODO seems that if file exists it is not skipping copying file
func (c Client) FileCopy(localPath string, remotePath string, override bool) error {
if !override {
exists, err := c.FileExists(remotePath)
Expand All @@ -229,3 +210,30 @@ func (c Client) FileCopy(localPath string, remotePath string, override bool) err
}
return nil
}

func (c Client) PathCopy(localPath string, remotePath string, override bool) error {
stat, err := os.Stat(localPath)
if err != nil {
return fmt.Errorf("cannot stat path '%s': %w", localPath, err)
}
if stat.IsDir() {
return c.DirCopy(localPath, remotePath, override)
}
return c.FileCopy(localPath, remotePath, override)
}

func (c Client) FileWrite(remotePath string, text string) error {
file, err := os.CreateTemp(os.TempDir(), "tf-provider-aem-*.tmp")
path := file.Name()
defer func() { _ = file.Close(); _ = os.Remove(path) }()
if err != nil {
return fmt.Errorf("cannot create local writable temporary file to be copied to remote path '%s': %w", remotePath, err)
}
if _, err := file.WriteString(text); err != nil {
return fmt.Errorf("cannot write text to local temporary file to be copied to remote path '%s': %w", remotePath, err)
}
if err := c.FileCopy(path, remotePath, true); err != nil {
return err
}
return nil
}
50 changes: 26 additions & 24 deletions internal/provider/instance_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
type InstanceClient ClientContext[InstanceResourceModel]

func (ic *InstanceClient) DataDir() string {
return ic.data.Compose.DataDir.ValueString()
return ic.data.System.DataDir.ValueString()
}

func (ic *InstanceClient) Close() error {
Expand All @@ -18,7 +18,7 @@ func (ic *InstanceClient) Close() error {

// TODO chown data dir to ssh user or 'aem' user (create him maybe)

Check failure on line 19 in internal/provider/instance_client.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)

Check failure on line 19 in internal/provider/instance_client.go

View workflow job for this annotation

GitHub Actions / Build

Comment should end in a period (godot)
func (ic *InstanceClient) prepareDataDir() error {
if _, err := ic.cl.RunShell(fmt.Sprintf("mkdir -p %s", ic.DataDir())); err != nil {
if _, err := ic.cl.RunShellPurely(fmt.Sprintf("mkdir -p %s", ic.DataDir())); err != nil {
return fmt.Errorf("cannot create AEM data directory: %w", err)
}
return nil
Expand All @@ -30,7 +30,7 @@ func (ic *InstanceClient) installComposeWrapper() error {
return fmt.Errorf("cannot check if AEM Compose CLI wrapper is installed: %w", err)
}
if !exists {
out, err := ic.cl.RunShellWithEnv(fmt.Sprintf("cd %s && curl -s 'https://raw.githubusercontent.com/wttech/aemc/main/pkg/project/common/aemw' -o 'aemw'", ic.DataDir()))
out, err := ic.cl.RunShellCommand(fmt.Sprintf("cd %s && curl -s 'https://raw.githubusercontent.com/wttech/aemc/main/pkg/project/common/aemw' -o 'aemw'", ic.DataDir()))
tflog.Info(ic.ctx, string(out))
if err != nil {
return fmt.Errorf("cannot download AEM Compose CLI wrapper: %w", err)
Expand All @@ -39,27 +39,29 @@ func (ic *InstanceClient) installComposeWrapper() error {
return nil
}

func (ic *InstanceClient) copyConfigFile() error {
configFile := ic.data.Compose.ConfigFile.ValueString()
if err := ic.cl.FileCopy(configFile, fmt.Sprintf("%s/aem/default/etc/aem.yml", ic.DataDir()), true); err != nil {
func (ic *InstanceClient) writeConfigFile() error {
configYAML := ic.data.Compose.Config.ValueString()
if err := ic.cl.FileWrite(fmt.Sprintf("%s/aem/default/etc/aem.yml", ic.DataDir()), configYAML); err != nil {
return fmt.Errorf("unable to copy AEM configuration file: %w", err)
}
return nil
}

func (ic *InstanceClient) copyLibraryDir() error {
localLibDir := ic.data.Compose.LibDir.ValueString()
remoteLibDir := fmt.Sprintf("%s/aem/home/lib", ic.DataDir())
if err := ic.cl.DirCopy(localLibDir, remoteLibDir, false); err != nil {
return fmt.Errorf("unable to copy AEM library dir: %w", err)
func (ic *InstanceClient) copyFiles() error {
var filesMap map[string]string
ic.data.Files.ElementsAs(ic.ctx, &filesMap, true)
for localPath, remotePath := range filesMap {
if err := ic.cl.PathCopy(localPath, remotePath, true); err != nil {
return fmt.Errorf("unable to copy path '%s' to '%s': %w", localPath, remotePath, err)
}
}
return nil
}

func (ic *InstanceClient) create() error {
tflog.Info(ic.ctx, "Creating AEM instance(s)")

textOut, err := ic.cl.RunShellWithEnv(fmt.Sprintf("cd %s && sh aemw instance create", ic.DataDir()))
textOut, err := ic.cl.RunShellCommand(fmt.Sprintf("cd %s && sh aemw instance create", ic.DataDir()))
if err != nil {
return fmt.Errorf("unable to create AEM instance: %w", err)
}
Expand All @@ -75,7 +77,7 @@ func (ic *InstanceClient) launch() error {
tflog.Info(ic.ctx, "Launching AEM instance(s)")

// TODO register systemd service instead and start it
textOut, err := ic.cl.RunShellWithEnv(fmt.Sprintf("cd %s && sh aemw instance launch", ic.DataDir()))
textOut, err := ic.cl.RunShellCommand(fmt.Sprintf("cd %s && sh aemw instance launch", ic.DataDir()))
if err != nil {
return fmt.Errorf("unable to launch AEM instance: %w", err)
}
Expand All @@ -92,7 +94,7 @@ func (ic *InstanceClient) terminate() error {
tflog.Info(ic.ctx, "Terminating AEM instance(s)")

// TODO use systemd service instead and stop it
textOut, err := ic.cl.RunShellWithEnv(fmt.Sprintf("cd %s && sh aemw instance terminate", ic.DataDir()))
textOut, err := ic.cl.RunShellCommand(fmt.Sprintf("cd %s && sh aemw instance terminate", ic.DataDir()))
if err != nil {
return fmt.Errorf("unable to terminate AEM instance: %w", err)
}
Expand All @@ -105,7 +107,7 @@ func (ic *InstanceClient) terminate() error {
}

func (ic *InstanceClient) deleteDataDir() error {
if _, err := ic.cl.RunShell(fmt.Sprintf("rm -fr %s", ic.DataDir())); err != nil {
if _, err := ic.cl.RunShellPurely(fmt.Sprintf("rm -fr %s", ic.DataDir())); err != nil {
return fmt.Errorf("cannot delete AEM data directory: %w", err)
}
return nil
Expand All @@ -127,7 +129,7 @@ type InstanceStatus struct {

func (ic *InstanceClient) ReadStatus() (InstanceStatus, error) {
var status InstanceStatus
yamlBytes, err := ic.cl.RunShellWithEnv(fmt.Sprintf("cd %s && sh aemw instance status --output-format yaml", ic.DataDir()))
yamlBytes, err := ic.cl.RunShellCommand(fmt.Sprintf("cd %s && sh aemw instance status --output-format yaml", ic.DataDir()))
if err != nil {
return status, err
}
Expand All @@ -138,27 +140,27 @@ func (ic *InstanceClient) ReadStatus() (InstanceStatus, error) {
}

// TODO when create fails this could be run twice; how about protecting it with lock?
func (ic *InstanceClient) bootstrap() error {
return ic.runHook("bootstrap", ".", ic.data.Hook.Bootstrap.ValueString())
func (ic *InstanceClient) runBootstrapHook() error {
return ic.runHook("bootstrap", ic.data.System.Bootstrap.ValueString(), ".")
}

// TODO when create fails this could be run twice; how about protecting it with lock?
func (ic *InstanceClient) initialize() error {
return ic.runHook("initialize", ic.DataDir(), ic.data.Hook.Initialize.ValueString())
func (ic *InstanceClient) runInitHook() error {
return ic.runHook("init", ic.data.Compose.Init.ValueString(), ic.DataDir())
}

func (ic *InstanceClient) provision() error {
return ic.runHook("provision", ic.DataDir(), ic.data.Hook.Provision.ValueString())
func (ic *InstanceClient) runLaunchHook() error {
return ic.runHook("launch", ic.data.Compose.Launch.ValueString(), ic.DataDir())
}

func (ic *InstanceClient) runHook(name string, dir, cmdScript string) error {
func (ic *InstanceClient) runHook(name, cmdScript, dir string) error {
if cmdScript == "" {
return nil
}

tflog.Info(ic.ctx, fmt.Sprintf("Executing instance hook '%s'", name))

textOut, err := ic.cl.RunShellScriptWithEnv(dir, cmdScript)
textOut, err := ic.cl.RunShellScript(name, cmdScript, dir)
if err != nil {
return fmt.Errorf("unable to execute hook '%s' properly: %w", name, err)
}
Expand Down
26 changes: 14 additions & 12 deletions internal/provider/instance_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ type InstanceResourceModel struct {
Compose struct {
Version types.String `tfsdk:"version"`
Config types.String `tfsdk:"config"`
Init types.String `tfsdk:"initialize"`
Launch types.String `tfsdk:"provision"`
Init types.String `tfsdk:"init"`
Launch types.String `tfsdk:"launch"`
} `tfsdk:"compose"`
Instances types.List `tfsdk:"instances"`
}
Expand Down Expand Up @@ -108,6 +108,7 @@ func (r *InstanceResource) Schema(ctx context.Context, req resource.SchemaReques
"service": schema.StringAttribute{
MarkdownDescription: "Contents of the 'systemd' service configuration file",
Optional: true,
Computed: true,
Default: stringdefault.StaticString(instance.ServiceTemplate),
},
"env": schema.MapAttribute{ // TODO handle it
Expand Down Expand Up @@ -246,8 +247,13 @@ func (r *InstanceResource) createOrUpdate(ctx context.Context, plan *tfsdk.Plan,
}
}(ic)

if err := ic.copyFiles(); err != nil {
diags.AddError("Unable to copy files", fmt.Sprintf("%s", err))
return
}

if create {
if err := ic.bootstrap(); err != nil {
if err := ic.runBootstrapHook(); err != nil {
diags.AddError("Unable to bootstrap AEM machine", fmt.Sprintf("%s", err))
return
}
Expand All @@ -260,16 +266,12 @@ func (r *InstanceResource) createOrUpdate(ctx context.Context, plan *tfsdk.Plan,
diags.AddError("Unable to install AEM Compose CLI", fmt.Sprintf("%s", err))
return
}
if err := ic.copyConfigFile(); err != nil {
diags.AddError("Unable to copy AEM configuration file", fmt.Sprintf("%s", err))
return
}
if err := ic.copyLibraryDir(); err != nil {
diags.AddError("Unable to copy AEM library dir", fmt.Sprintf("%s", err))
if err := ic.writeConfigFile(); err != nil {
diags.AddError("Unable to write AEM configuration file", fmt.Sprintf("%s", err))
return
}
if create {
if err := ic.initialize(); err != nil {
if err := ic.runInitHook(); err != nil {
diags.AddError("Unable to initialize AEM instance", fmt.Sprintf("%s", err))
return
}
Expand All @@ -282,7 +284,7 @@ func (r *InstanceResource) createOrUpdate(ctx context.Context, plan *tfsdk.Plan,
diags.AddError("Unable to launch AEM instance", fmt.Sprintf("%s", err))
return
}
if err := ic.provision(); err != nil {
if err := ic.runLaunchHook(); err != nil {
diags.AddError("Unable to provision AEM instance", fmt.Sprintf("%s", err))
return
}
Expand Down Expand Up @@ -344,7 +346,7 @@ func (r *InstanceResource) Read(ctx context.Context, req resource.ReadRequest, r

ic, err := r.client(ctx, model, time.Second*15)
if err != nil {
tflog.Info(ctx, "Cannot read AEM instance state as it is not possible to connect at the moment. Possible reasons: machine IP change is in progress, machine is not yet created or booting up, etc.")
tflog.Info(ctx, "Cannot read AEM instance state as it is not possible to connect at the moment. Possible reasons: machine IP change is in progress, machine is not yet created or booting up, etc.")
} else {
defer func(ic *InstanceClient) {
err := ic.Close()
Expand Down

0 comments on commit 73bee1b

Please sign in to comment.