-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/deislabs/porter-terraform/pkg/terraform" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func buildInvokeCommand(mixin *terraform.Mixin) *cobra.Command { | ||
opts := terraform.ExecuteCommandOptions{} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "invoke", | ||
Short: "Execute the invoke functionality of this mixin", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return mixin.Execute(opts) | ||
}, | ||
} | ||
flags := cmd.Flags() | ||
flags.StringVar(&opts.Action, "action", "", "Custom action name to invoke.") | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Dockerfile | ||
.cnab |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
mixins: | ||
- terraform | ||
|
||
name: basic-tf-example | ||
version: 0.1.0 | ||
invocationImage: basic-tf-example:latest | ||
tag: deislabs/basic-tf-example-bundle:v0.1.0 | ||
|
||
outputs: | ||
- name: a | ||
type: string | ||
applyTo: | ||
- install | ||
- status | ||
- upgrade | ||
- name: b | ||
type: string | ||
applyTo: | ||
- status | ||
- upgrade | ||
- name: c | ||
type: string | ||
applyTo: | ||
- install | ||
- status | ||
|
||
customActions: | ||
plan: | ||
description: "Invoke 'terraform plan'" | ||
modifies: false | ||
stateless: true | ||
show: | ||
description: "Invoke 'terraform show'" | ||
modifies: false | ||
stateless: true | ||
printVersion: | ||
description: "Invoke 'terraform version'" | ||
modifies: false | ||
stateless: true | ||
|
||
install: | ||
- terraform: | ||
description: "Install Terraform assets" | ||
autoApprove: true | ||
outputs: | ||
- name: a | ||
- name: b | ||
- name: c | ||
|
||
upgrade: | ||
- terraform: | ||
description: "Upgrade Terraform assets" | ||
autoApprove: true | ||
outputs: | ||
- name: a | ||
- name: b | ||
- name: c | ||
|
||
show: | ||
- terraform: | ||
description: "Invoke 'terraform show'" | ||
|
||
plan: | ||
- terraform: | ||
description: "Invoke 'terraform plan'" | ||
|
||
# Note: this can't be 'version:' as this would conflict with top-level field | ||
# Hence the need for the 'command:' override | ||
printVersion: | ||
- terraform: | ||
description: "Invoke 'terraform version'" | ||
command: "version" | ||
|
||
uninstall: | ||
- terraform: | ||
autoApprove: true | ||
description: "Uninstall Terraform assets" | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
output "a" { | ||
value = "a" | ||
} | ||
|
||
output "b" { | ||
value = "b" | ||
} | ||
|
||
output "c" { | ||
value = "c" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package terraform | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
yaml "gopkg.in/yaml.v2" | ||
) | ||
|
||
type ExecuteCommandOptions struct { | ||
Action string | ||
} | ||
|
||
type ExecuteAction struct { | ||
Steps []ExecuteStep // using UnmarshalYAML so that we don't need a custom type per action | ||
} | ||
|
||
// UnmarshalYAML takes any yaml in this form | ||
// ACTION: | ||
// - terraform: ... | ||
// and puts the steps into the Action.Steps field | ||
func (a *ExecuteAction) UnmarshalYAML(unmarshal func(interface{}) error) error { | ||
actionMap := map[interface{}][]interface{}{} | ||
err := unmarshal(&actionMap) | ||
if err != nil { | ||
return errors.Wrap(err, "could not unmarshal yaml into an action map of terraform steps") | ||
} | ||
|
||
for _, stepMaps := range actionMap { | ||
b, err := yaml.Marshal(stepMaps) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var steps []ExecuteStep | ||
err = yaml.Unmarshal(b, &steps) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
a.Steps = append(a.Steps, steps...) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type ExecuteStep struct { | ||
ExecuteInstruction `yaml:"terraform"` | ||
} | ||
|
||
type ExecuteInstruction struct { | ||
// InstallAguments contains the usual terraform command args for re-use here | ||
InstallArguments `yaml:",inline"` | ||
|
||
// Command allows an override of the actual terraform command | ||
Command string `yaml:"command,omitempty"` | ||
} | ||
|
||
// Execute will reapply manifests using kubectl | ||
func (m *Mixin) Execute(opts ExecuteCommandOptions) error { | ||
|
||
payload, err := m.getPayloadData() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var action ExecuteAction | ||
err = yaml.Unmarshal(payload, &action) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(action.Steps) != 1 { | ||
return errors.Errorf("expected a single step, but got %d", len(action.Steps)) | ||
} | ||
|
||
step := action.Steps[0] | ||
|
||
if step.LogLevel != "" { | ||
os.Setenv("TF_LOG", step.LogLevel) | ||
} | ||
|
||
// First, change to specified working dir | ||
if err := os.Chdir(m.WorkingDir); err != nil { | ||
return fmt.Errorf("could not change directory to specified working dir: %s", err) | ||
} | ||
|
||
// Initialize Terraform | ||
fmt.Println("Initializing Terraform...") | ||
err = m.Init(step.BackendConfig) | ||
if err != nil { | ||
return fmt.Errorf("could not init terraform, %s", err) | ||
} | ||
|
||
command := opts.Action | ||
if step.Command != "" { | ||
command = step.Command | ||
} | ||
cmd := m.NewCommand("terraform", command) | ||
|
||
cmd.Stdout = m.Out | ||
cmd.Stderr = m.Err | ||
|
||
prettyCmd := fmt.Sprintf("%s %s", cmd.Path, strings.Join(cmd.Args, " ")) | ||
fmt.Fprintln(m.Out, prettyCmd) | ||
|
||
err = cmd.Start() | ||
if err != nil { | ||
return fmt.Errorf("could not execute command, %s: %s", prettyCmd, err) | ||
} | ||
err = cmd.Wait() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return m.handleOutputs(step.Outputs) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package terraform | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"os" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/deislabs/porter/pkg/test" | ||
"github.com/stretchr/testify/require" | ||
|
||
yaml "gopkg.in/yaml.v2" | ||
) | ||
|
||
type ExecuteTest struct { | ||
expectedCommand string | ||
executeAction ExecuteAction | ||
} | ||
|
||
func TestMixin_ExecuteStep(t *testing.T) { | ||
|
||
defaultAction := "foo" | ||
|
||
executeTests := []ExecuteTest{ | ||
{ | ||
expectedCommand: fmt.Sprintf("terraform %s", defaultAction), | ||
executeAction: ExecuteAction{ | ||
Steps: []ExecuteStep{ | ||
ExecuteStep{ | ||
ExecuteInstruction: ExecuteInstruction{ | ||
InstallArguments: InstallArguments{ | ||
Step: Step{ | ||
Description: "My Custom Terraform Action", | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
expectedCommand: "terraform version", | ||
executeAction: ExecuteAction{ | ||
Steps: []ExecuteStep{ | ||
ExecuteStep{ | ||
ExecuteInstruction: ExecuteInstruction{ | ||
Command: "version", | ||
InstallArguments: InstallArguments{ | ||
Step: Step{ | ||
Description: "My Custom Terraform Action", | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
defer os.Unsetenv(test.ExpectedCommandEnv) | ||
for _, executeTest := range executeTests { | ||
t.Run(executeTest.expectedCommand, func(t *testing.T) { | ||
os.Setenv(test.ExpectedCommandEnv, strings.Join([]string{ | ||
"terraform init", | ||
executeTest.expectedCommand, | ||
}, "\n")) | ||
|
||
b, err := yaml.Marshal(executeTest.executeAction) | ||
require.NoError(t, err) | ||
|
||
h := NewTestMixin(t) | ||
h.In = bytes.NewReader(b) | ||
|
||
// Set up working dir as current dir | ||
h.WorkingDir, err = os.Getwd() | ||
require.NoError(t, err) | ||
|
||
err = h.Execute(ExecuteCommandOptions{ | ||
Action: defaultAction, | ||
}) | ||
require.NoError(t, err) | ||
}) | ||
} | ||
} |