Skip to content

Commit

Permalink
Merge branch 'release/v0.3.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
rucciva committed Nov 10, 2020
2 parents aeff111 + c8b295b commit 443394e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 37 deletions.
10 changes: 6 additions & 4 deletions docs/resources/script.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ resource "linux_script" "install_package" {
The following arguments are supported:

- `lifecycle_commands` - (Required) see [lifecycle_commands](#lifecycle_commands).
- `triggers` - (Optional, string map) Attribute that will trigger resource recreation on changes just like the one in [null_resource](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource#triggers). Default empty map.
- `environment` - (Optional, string map) A list of linux environment that will be available in each `lifecycle_commands`. Default empty map.
- `sensitive_environment` - (Optional, string map) Just like `environment` except they don't show up in log files. In case of duplication, environment variables defined here will take precedence over the ones in `environment`. Default empty map.
- `interpreter` - (Optional, string list) Interpreter for running each `lifecycle_commands`. Default empty list.
- `working_directory` - (Optional, string) The working directory where each `lifecycle_commands` is executed. Default empty string.
- `environment` - (Optional, string map) A list of linux environment that will be available in each `lifecycle_commands`. Default empty map.
- `sensitive_environment` - (Optional, string map) Just like `environment` except they don't show up in log files. In case of duplication, environment variables defined here will take precedence over the ones in `environment`. Default empty map.
- `triggers` - (Optional, string map) Attribute that will trigger resource recreation on changes just like the one in [null_resource](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource#triggers). Default empty map.

### lifecycle_commands

Expand All @@ -39,7 +39,9 @@ Block that contains commands to be remotely executed respective to terraform's *
- `update` - (Optional, string) Commands that will be executed in **Update** phase. Previous `output` are accessible from stdin. Omiting this will trigger resource recreation (**Delete** -> **Create**) each time terraform detect changes.
- `delete` - (Required, string) Commands that will be executed in **Delete** phase.

When any of the commands is updated, then nothing will be executed (except for the current `read` commands since it will always be executed before changes are detected). This is to mimic the behavior of an updated provider's logic, that is no previous logics will be executed. If at the same time another arguments is updated, then error will be thrown.
### Resource Update

When any of the `lifecycle_commands` and/or `interpreter` are updated, then nothing will be executed (except for the current `read` commands with existing `interpreter` since it will always be executed before changes are detected). This is to mimic the behavior of an updated provider's instructions, where no previous instructions are executed. Changes to these two arguments must not be followed with changes to other arguments at the same time, or else an error will be thrown.

## Attribute Reference

Expand Down
61 changes: 35 additions & 26 deletions linux/script-resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import (
"github.com/spf13/cast"
)

type haschange interface {
HasChange(key string) bool
}

const (
attrScriptLifecycleCommands = "lifecycle_commands"
attrScriptLifecycleCommandCreate = "create"
Expand Down Expand Up @@ -107,16 +111,19 @@ var schemaScriptResource = map[string]*schema.Schema{
},
}

type handlerScriptResource struct{}
type handlerScriptResource struct {
}

func (h handlerScriptResource) attrCommands() map[string]bool {
return map[string]bool{attrScriptLifecycleCommands: true}
return map[string]bool{
attrScriptLifecycleCommands: true,
attrScriptInterpreter: true,
}
}
func (h handlerScriptResource) attrInputs() map[string]bool {
return map[string]bool{
attrScriptEnvironment: true,
attrScriptSensitiveEnvironment: true,
attrScriptInterpreter: true,
attrScriptWorkingDirectory: true,
}
}
Expand Down Expand Up @@ -147,6 +154,21 @@ func (h handlerScriptResource) attrs() (m map[string]bool) {
return
}

func (h handlerScriptResource) changed(rd haschange, attrs map[string]bool) (changed []string) {
for k := range attrs {
if rd.HasChange(k) {
changed = append(changed, k)
}
}
return
}
func (h handlerScriptResource) changedAttrInputs(rd haschange) (changed []string) {
return h.changed(rd, h.attrInputs())
}
func (h handlerScriptResource) changedAttrCommands(rd haschange) (changed []string) {
return h.changed(rd, h.attrCommands())
}

func (h handlerScriptResource) newScript(rd *schema.ResourceData, l *linux, attrLifeCycle string) (s *script) {
if rd == nil {
return
Expand Down Expand Up @@ -183,8 +205,7 @@ func (h handlerScriptResource) Read(ctx context.Context, rd *schema.ResourceData
old := cast.ToString(rd.Get(attrScriptOutput))

err := h.read(ctx, rd, meta.(*linux))
var errExit *remote.ExitError
switch {
switch errExit := (*remote.ExitError)(nil); {
case errors.As(err, &errExit):
_ = rd.Set(attrScriptReadFailed, true)
_ = rd.Set(attrScriptReadError, err.Error())
Expand Down Expand Up @@ -225,14 +246,9 @@ func (h handlerScriptResource) Create(ctx context.Context, rd *schema.ResourceDa
}

// WARN: see https://github.com/hashicorp/terraform-plugin-sdk/issues/476
func (h handlerScriptResource) restoreOldResourceData(rd *schema.ResourceData, except ...string) (err error) {
var exceptMap = map[string]bool{}
for _, k := range except {
exceptMap[k] = true
}

func (h handlerScriptResource) restoreOldResourceData(rd *schema.ResourceData, except map[string]bool) (err error) {
for k := range h.attrs() {
if exceptMap[k] {
if except != nil && except[k] {
continue
}
o, _ := rd.GetChange(k)
Expand All @@ -245,11 +261,11 @@ func (h handlerScriptResource) restoreOldResourceData(rd *schema.ResourceData, e
}

func (h handlerScriptResource) Update(ctx context.Context, rd *schema.ResourceData, meta interface{}) (d diag.Diagnostics) {
if rd.HasChange(attrScriptLifecycleCommands) {
if len(h.changedAttrCommands(rd)) > 0 {
// mimic the behaviour when terraform provider is updated,
// that is no old logic are executed and
// the new logic are run with the existing state and diff
_ = h.restoreOldResourceData(rd, attrScriptLifecycleCommands)
_ = h.restoreOldResourceData(rd, h.attrCommands())
return
}

Expand All @@ -258,7 +274,7 @@ func (h handlerScriptResource) Update(ctx context.Context, rd *schema.ResourceDa
oldOutput := cast.ToString(rd.Get(attrScriptOutput))
sc.stdin = strings.NewReader(oldOutput)
if _, err := sc.exec(ctx); err != nil {
_ = h.restoreOldResourceData(rd)
_ = h.restoreOldResourceData(rd, nil)
return diag.FromErr(err)
}

Expand All @@ -282,15 +298,10 @@ func (h handlerScriptResource) CustomizeDiff(c context.Context, rd *schema.Resou
return // no state
}

if rd.HasChange(attrScriptLifecycleCommands) {
var forbidden []string
for key := range h.attrInputs() {
if rd.HasChange(key) {
forbidden = append(forbidden, key)
}
}
if len(forbidden) > 0 {
return fmt.Errorf("update to `%s` should not be combined with update to other arguments: %s", attrScriptLifecycleCommands, strings.Join(forbidden, ","))
if cmd := h.changedAttrCommands(rd); len(cmd) > 0 {
if fbd := h.changedAttrInputs(rd); len(fbd) > 0 {
return fmt.Errorf("update to '%s' should not be combined with update to other arguments: %s",
strings.Join(cmd, ","), strings.Join(fbd, ","))
}
return // updated commands. let Update handle it.
}
Expand All @@ -314,8 +325,6 @@ func (h handlerScriptResource) CustomizeDiff(c context.Context, rd *schema.Resou
case strings.HasPrefix(key, attrScriptEnvironment):
fallthrough
case strings.HasPrefix(key, attrScriptSensitiveEnvironment):
fallthrough
case strings.HasPrefix(key, attrScriptInterpreter):
parts := strings.Split(key, ".")
key = strings.Join(parts[:len(parts)-1], ".")
}
Expand Down
15 changes: 10 additions & 5 deletions linux/script-resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ func TestAccLinuxScriptBasic(t *testing.T) {
},
},
}
conf2 := tfConf{
Provider: testAccProvider,
Script: conf1.Script.Copy(),
}
conf2.Script.Environment = conf2.Script.Environment.With("FILE", fmt.Sprintf(`"/tmp/linux/%s"`, acctest.RandString(16)))
conf2 := conf1.Copy(func(tc *tfConf) {
tc.Script.Environment.With("FILE", fmt.Sprintf(`"/tmp/linux/%s"`, acctest.RandString(16)))
})

resource.Test(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{"null": {}},
Expand Down Expand Up @@ -352,6 +350,9 @@ func TestAccLinuxScriptUpdatedScript(t *testing.T) {
UpdateFilePt2 := UpdateFilePt1.Copy(func(tc *tfConf) {
tc.Script.Environment = createFilePt2.Script.Environment.Copy().With("CONTENT", `"world1"`)
})
interpreterUpdated := UpdateFilePt2.Copy(func(tc *tfConf) {
tc.Script.Interpreter = tfList{`"/bin/sh"`}
})

resource.Test(t, resource.TestCase{
PreCheck: testAccPreCheckConnection(t),
Expand Down Expand Up @@ -402,6 +403,10 @@ func TestAccLinuxScriptUpdatedScript(t *testing.T) {
Config: testAccLinuxScriptUpdatedScriptConfig(t, UpdateFilePt2),
Check: resource.TestCheckResourceAttr("linux_script.script", "output", "world\nworld1"),
},
{
Config: testAccLinuxScriptUpdatedScriptConfig(t, interpreterUpdated),
Check: resource.TestCheckResourceAttr("linux_script.script", "output", "world\nworld1"),
},
},
})
}
Expand Down
3 changes: 1 addition & 2 deletions linux/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ func tNewTFMapFile() tfmap {
type tfList []string

func (l tfList) Copy() (c tfList) {
copy(c, l)
return
return append(c, l...)
}

func (l tfList) Serialize() (s string, err error) {
Expand Down

0 comments on commit 443394e

Please sign in to comment.