Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instance create with state retrieval #8

Merged
merged 11 commits into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ make testacc

Run command: `sh develop.sh <example_path> <tf_args>`.

For example: `sh develop.sh examples/ssh plan`.
For example:

- `sh develop.sh examples/ssh plan`
- `sh develop.sh examples/ssh apply -auto-approve`
- `sh develop.sh examples/ssh destroy -auto-approve`

## Debugging the Provider

Expand Down
3 changes: 3 additions & 0 deletions apply.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

sh run.sh examples/ssh apply -auto-approve
2 changes: 1 addition & 1 deletion develop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ then
fi

TF_CLI_CONFIG_FILE="$(pwd)/dev_overrides.tfrc"
TF_LOG=info # TODO or info?
TF_LOG=debug

echo "Executing Terraform command at dir: $TF_DIR"
(export TF_CLI_CONFIG_FILE && export TF_LOG && cd "$TF_DIR" && terraform "${@:2}")
5 changes: 5 additions & 0 deletions examples/ssh/aem.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ resource "aem_instance" "single" {
config_file = "aem/default/etc/aem.yml"
}
}


output "aem_instances" {
value = aem_instance.single.instances
}
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ require (
github.com/hashicorp/terraform-plugin-framework v1.3.5
github.com/hashicorp/terraform-plugin-go v0.18.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/melbahja/goph v1.3.1
github.com/spf13/cast v1.5.0
golang.org/x/crypto v0.11.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -40,7 +44,6 @@ require (
github.com/kr/fs v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/melbahja/goph v1.3.1 // indirect
github.com/mitchellh/cli v1.1.5 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
Expand All @@ -51,11 +54,9 @@ require (
github.com/posener/complete v1.2.3 // indirect
github.com/russross/blackfriday v1.6.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/zclconf/go-cty v1.13.2 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
Expand Down Expand Up @@ -222,6 +222,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
15 changes: 11 additions & 4 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ func (c Client) Connect() error {
return c.connection.Connect()
}

func (c Client) ConnectWithRetry(callback func()) error {
timeout := time.Minute * 5
func (c Client) ConnectWithRetry(timeout time.Duration, callback func()) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
for {
Expand Down Expand Up @@ -73,7 +72,7 @@ func (c Client) Run(cmdLine []string) (*goph.Cmd, error) {

func (c Client) SetupEnv() error {
file, err := os.CreateTemp(os.TempDir(), "tf-provider-aem-env-*.sh")
path := os.TempDir() + "/" + file.Name()
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)
Expand Down Expand Up @@ -132,7 +131,7 @@ func (c Client) DirEnsure(path string) error {
func (c Client) FileExists(path string) (bool, error) {
out, err := c.RunShell(fmt.Sprintf("test -f %s && echo '0' || echo '1'", path))
if err != nil {
return false, err
return false, fmt.Errorf("cannot check if file exists '%s': %w", path, err)
}
return strings.TrimSpace(string(out)) == "0", nil
}
Expand All @@ -147,6 +146,14 @@ func (c Client) FileMove(oldPath string, newPath string) error {
return nil
}

func (c Client) DirExists(path string) (bool, error) {
out, err := c.RunShell(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)
}
return strings.TrimSpace(string(out)) == "0", nil
}

func (c Client) DirCopy(localPath string, remotePath string, override bool) error {
if err := c.DirEnsure(remotePath); err != nil {
return err
Expand Down
21 changes: 1 addition & 20 deletions internal/provider/client_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,11 @@ package provider

import (
"context"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/wttech/terraform-provider-aem/internal/client"
)

type ClientCreateContext[T interface{}] struct {
type ClientContext[T interface{}] struct {
cl *client.Client
ctx context.Context
data T
req resource.CreateRequest
resp *resource.CreateResponse
}

type ClientDeleteContext[T interface{}] struct {
cl *client.Client
ctx context.Context
data T
req resource.DeleteRequest
resp *resource.DeleteResponse
}

type ClientReadContext[T interface{}] struct {
cl *client.Client
ctx context.Context
data T
req resource.ReadRequest
resp *resource.ReadResponse
}
119 changes: 119 additions & 0 deletions internal/provider/instance_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package provider

import (
"fmt"
"github.com/hashicorp/terraform-plugin-log/tflog"
"gopkg.in/yaml.v3"
)

type InstanceClient ClientContext[InstanceResourceModel]

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

func (ic *InstanceClient) Close() error {
return ic.cl.Disconnect()
}

// TODO chown data dir to ssh user or 'aem' user (create him maybe)
func (ic *InstanceClient) prepareDataDir() error {
/* TODO to avoid re-uploading library files (probably temporary)
if _, err := ic.cl.RunShell(fmt.Sprintf("rm -fr %s", ic.DataDir())); err != nil {
return fmt.Errorf("cannot clean up AEM data directory: %w", err)
}
*/
if _, err := ic.cl.RunShell(fmt.Sprintf("mkdir -p %s", ic.DataDir())); err != nil {
return fmt.Errorf("cannot create AEM data directory: %w", err)
}
return nil
}

func (ic *InstanceClient) installComposeWrapper() error {
exists, err := ic.cl.FileExists(fmt.Sprintf("%s/aemw", ic.DataDir()))
if err != nil {
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()))
tflog.Info(ic.ctx, string(out))
if err != nil {
return fmt.Errorf("cannot download AEM Compose CLI wrapper: %w", err)
}
}
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 {
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)
}
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()))
if err != nil {
return fmt.Errorf("unable to create AEM instance: %w", err)
}

textStr := string(textOut) // TODO how about streaming it line by line to tflog ;)
tflog.Info(ic.ctx, "Created AEM instance(s)")
tflog.Info(ic.ctx, textStr) // TODO consider checking 'changed' flag here if needed

return nil
}

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()))
if err != nil {
return fmt.Errorf("unable to launch AEM instance: %w", err)
}

textStr := string(textOut) // TODO how about streaming it line by line to tflog ;)
tflog.Info(ic.ctx, "Launched AEM instance(s)")
tflog.Info(ic.ctx, textStr) // TODO consider checking 'changed' flag here if needed

return nil
}

type InstanceStatus struct {
Data struct {
Instances []struct {
ID string `yaml:"id"`
URL string `yaml:"url"`
AemVersion string `yaml:"aem_version"`
Attributes []string `yaml:"attributes"`
RunModes []string `yaml:"run_modes"`
HealthChecks []string `yaml:"health_checks"`
Dir string `yaml:"dir"`
} `yaml:"instances"`
}
}

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()))
if err != nil {
return status, err
}
if err := yaml.Unmarshal(yamlBytes, &status); err != nil {
return status, fmt.Errorf("unable to parse AEM instance status: %w", err)
}
return status, nil
}
Loading
Loading