Skip to content
Open
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
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM alpine

RUN apk add --no-cache curl unzip bash git
ARG TERRAFORM_VERSION=1.5.7
RUN curl -fsSL -o /tmp/terraform.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
&& unzip /tmp/terraform.zip -d /usr/local/bin \
&& rm /tmp/terraform.zip
# Put the manager binary in /app
WORKDIR /
COPY ./manager .
RUN chmod +x ./manager
COPY gitconfig /.gitconfig

# Create required writable directories
RUN mkdir -p /tf /tmp /logs && chmod -R 777 /tf /logs /tmp

ENTRYPOINT ["/manager"]
2 changes: 2 additions & 0 deletions gitconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[credential]
helper = store --file=$GIT_CRED_DIR/.git-credentials
8 changes: 4 additions & 4 deletions internal/controller/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ type tfclient interface {
Outputs(ctx context.Context) ([]terraform.Output, error)
Resources(ctx context.Context) ([]string, error)
Diff(ctx context.Context, o ...terraform.Option) (bool, error)
Apply(ctx context.Context, o ...terraform.Option) error
Destroy(ctx context.Context, o ...terraform.Option) error
Apply(ctx context.Context, ws string, o ...terraform.Option) error
Destroy(ctx context.Context, ws string, o ...terraform.Option) error
DeleteCurrentWorkspace(ctx context.Context) error
GenerateChecksum(ctx context.Context) (string, error)
}
Expand Down Expand Up @@ -442,7 +442,7 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext
}

o = append(o, terraform.WithArgs(cr.Spec.ForProvider.ApplyArgs))
if err := c.tf.Apply(ctx, o...); err != nil {
if err := c.tf.Apply(ctx, cr.Name, o...); err != nil {
return managed.ExternalUpdate{}, errors.Wrap(err, errApply)
}

Expand Down Expand Up @@ -473,7 +473,7 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error {
}

o = append(o, terraform.WithArgs(cr.Spec.ForProvider.DestroyArgs))
return errors.Wrap(c.tf.Destroy(ctx, o...), errDestroy)
return errors.Wrap(c.tf.Destroy(ctx, cr.Name, o...), errDestroy)
}

//nolint:gocyclo
Expand Down
22 changes: 11 additions & 11 deletions internal/controller/workspace/workspace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ type MockTf struct {
MockOutputs func(ctx context.Context) ([]terraform.Output, error)
MockResources func(ctx context.Context) ([]string, error)
MockDiff func(ctx context.Context, o ...terraform.Option) (bool, error)
MockApply func(ctx context.Context, o ...terraform.Option) error
MockDestroy func(ctx context.Context, o ...terraform.Option) error
MockApply func(ctx context.Context, ws string, o ...terraform.Option) error
MockDestroy func(ctx context.Context, ws string, o ...terraform.Option) error
MockDeleteCurrentWorkspace func(ctx context.Context) error
MockGenerateChecksum func(ctx context.Context) (string, error)
}
Expand Down Expand Up @@ -103,12 +103,12 @@ func (tf *MockTf) Diff(ctx context.Context, o ...terraform.Option) (bool, error)
return tf.MockDiff(ctx, o...)
}

func (tf *MockTf) Apply(ctx context.Context, o ...terraform.Option) error {
return tf.MockApply(ctx, o...)
func (tf *MockTf) Apply(ctx context.Context, ws string, o ...terraform.Option) error {
return tf.MockApply(ctx, ws, o...)
}

func (tf *MockTf) Destroy(ctx context.Context, o ...terraform.Option) error {
return tf.MockDestroy(ctx, o...)
func (tf *MockTf) Destroy(ctx context.Context, ws string, o ...terraform.Option) error {
return tf.MockDestroy(ctx, ws, o...)
}

func (tf *MockTf) DeleteCurrentWorkspace(ctx context.Context) error {
Expand Down Expand Up @@ -1278,7 +1278,7 @@ func TestCreate(t *testing.T) {
reason: "We should return any error we encounter applying our Terraform configuration",
fields: fields{
tf: &MockTf{
MockApply: func(_ context.Context, _ ...terraform.Option) error { return errBoom },
MockApply: func(_ context.Context, ws string, _ ...terraform.Option) error { return errBoom },
},
},
args: args{
Expand All @@ -1292,7 +1292,7 @@ func TestCreate(t *testing.T) {
reason: "We should return any error we encounter getting our Terraform outputs",
fields: fields{
tf: &MockTf{
MockApply: func(_ context.Context, _ ...terraform.Option) error { return nil },
MockApply: func(_ context.Context, ws string, _ ...terraform.Option) error { return nil },
MockOutputs: func(ctx context.Context) ([]terraform.Output, error) { return nil, errBoom },
},
},
Expand All @@ -1307,7 +1307,7 @@ func TestCreate(t *testing.T) {
reason: "We should refresh our connection details with any updated outputs after successfully applying the Terraform configuration",
fields: fields{
tf: &MockTf{
MockApply: func(_ context.Context, _ ...terraform.Option) error { return nil },
MockApply: func(_ context.Context, ws string, _ ...terraform.Option) error { return nil },
MockGenerateChecksum: func(ctx context.Context) (string, error) { return tfChecksum, nil },
MockOutputs: func(ctx context.Context) ([]terraform.Output, error) {
return []terraform.Output{
Expand Down Expand Up @@ -1487,7 +1487,7 @@ func TestDelete(t *testing.T) {
reason: "We should return any error we encounter destroying our Terraform configuration",
fields: fields{
tf: &MockTf{
MockDestroy: func(_ context.Context, _ ...terraform.Option) error { return errBoom },
MockDestroy: func(_ context.Context, ws string, _ ...terraform.Option) error { return errBoom },
},
},
args: args{
Expand All @@ -1499,7 +1499,7 @@ func TestDelete(t *testing.T) {
reason: "We should not return an error if we successfully destroy the Terraform configuration",
fields: fields{
tf: &MockTf{
MockDestroy: func(_ context.Context, _ ...terraform.Option) error { return nil },
MockDestroy: func(_ context.Context, ws string, _ ...terraform.Option) error { return nil },
},
kube: &test.MockClient{
MockGet: test.NewMockGetFn(nil),
Expand Down
50 changes: 44 additions & 6 deletions internal/terraform/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -584,7 +585,7 @@ func (h Harness) Diff(ctx context.Context, o ...Option) (bool, error) {
}

// Apply a Terraform configuration.
func (h Harness) Apply(ctx context.Context, o ...Option) error {
func (h Harness) Apply(ctx context.Context, ws string, o ...Option) error {
ao := &options{}
for _, fn := range o {
fn(ao)
Expand All @@ -611,8 +612,12 @@ func (h Harness) Apply(ctx context.Context, o ...Option) error {
// In case of terraform apply
// 0 - Succeeded
// Non Zero output - Errored

log, err := runCommand(ctx, cmd)
f, err := os.Create(filepath.Join("/logs", ws))
if err != nil {
return err
}
defer f.Close()
log, err := runCommandv2(ctx, cmd, f)
switch cmd.ProcessState.ExitCode() {
case 0:
if h.EnableTerraformCLILogging {
Expand All @@ -629,7 +634,7 @@ func (h Harness) Apply(ctx context.Context, o ...Option) error {
}

// Destroy a Terraform configuration.
func (h Harness) Destroy(ctx context.Context, o ...Option) error {
func (h Harness) Destroy(ctx context.Context, ws string, o ...Option) error {
do := &options{}
for _, fn := range o {
fn(do)
Expand All @@ -652,8 +657,12 @@ func (h Harness) Destroy(ctx context.Context, o ...Option) error {
rwmutex.RLock()
defer rwmutex.RUnlock()
}

log, err := runCommand(ctx, cmd)
f, err := os.Create(filepath.Join("/logs", ws))
if err != nil {
return err
}
defer f.Close()
log, err := runCommandv2(ctx, cmd, f)

// In case of terraform destroy
// 0 - Succeeded
Expand Down Expand Up @@ -705,3 +714,32 @@ func runCommand(ctx context.Context, c *exec.Cmd) ([]byte, error) {
return res.out, res.err
}
}

// runCommand executes the requested command and sends the process SIGTERM if the context finishes before the command
func runCommandv2(ctx context.Context, c *exec.Cmd, f io.Writer) ([]byte, error) {
ch := make(chan cmdResult, 1)
go func() {
defer close(ch)
c.Stderr = f
c.Stdout = f
e := c.Run()
ch <- cmdResult{err: e}
}()
select {
case <-ctx.Done():
err := ctx.Err()
// This could be container termination or the reconciliation deadline was exceeded. Either way send a
// SIGTERM to the running process and wait for either the command to finish or the process to get killed.
e := c.Process.Signal(syscall.SIGTERM)
if e != nil {
return nil, errors.Wrap(errors.Wrap(err, errRunCommand), errors.Wrap(e, errSigTerm).Error())
}
e = c.Wait()
if e != nil {
return nil, errors.Wrap(errors.Wrap(err, errRunCommand), errors.Wrap(e, errWaitTerm).Error())
}
return nil, errors.Wrap(err, errRunCommand)
case res := <-ch:
return res.out, res.err
}
}