Skip to content

Commit

Permalink
Merge branch 'release/v0.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
rucciva committed Nov 23, 2020
2 parents 6c4dc8f + 83472b1 commit 184d847
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 18 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ resource "linux_file" "file" {
recycle_path = "/tmp/recycle"
}
locals {
package_name = "apache2"
}
resource "linux_script" "install_package" {
lifecycle_commands {
create = "apt update && apt install -y $PACKAGE_NAME=$PACKAGE_VERSION"
Expand All @@ -52,9 +57,12 @@ resource "linux_script" "install_package" {
delete = "apt remove -y $PACKAGE_NAME"
}
environment = {
PACKAGE_NAME = "apache2"
PACKAGE_NAME = local.package_name
PACKAGE_VERSION = "2.4.18-2ubuntu3.4"
}
triggers = {
PACKAGE_NAME = local.package_name"
}
}
```

Expand Down
40 changes: 40 additions & 0 deletions docs/data-sources/script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# linux_script

Read arbritrary resource by specifying commands that will be uploaded and executed remotely.

## Example Usage

```hcl
locals {
package_name = "apache2"
}
resource "linux_script" "install_package" {
lifecycle_commands {
read = "apt-cache policy $PACKAGE_NAME | grep 'Installed:' | grep -v '(none)' | awk '{ print $2 }' | xargs | tr -d '\n'"
}
environment = {
PACKAGE_NAME = local.package_name
PACKAGE_VERSION = "2.4.18-2ubuntu3.4"
}
}
```

## Argument Reference

The following arguments are supported:

- `lifecycle_commands` - (Required) see [lifecycle_commands](#lifecycle_commands).
- `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.

### lifecycle_commands

Block that contains commands to be uploaded and remotely executed in Terraform.

- `read` - (Required, string) Commands that will be executed to obtain data regarding the arbritrary resource. Terraform will record the output of these commands inside `output` attributes.

## Attribute Reference

- `output` - (string) The raw output of `read` commands.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ require (
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/alessio/shellescape v1.3.0
github.com/alessio/shellescape v1.3.1
github.com/google/uuid v1.1.2
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/terraform v0.13.5
github.com/hashicorp/terraform-plugin-sdk/v2 v2.2.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0
github.com/huandu/xstrings v1.3.2 // indirect
github.com/spf13/cast v1.3.1
github.com/stretchr/testify v1.6.1
go.uber.org/zap v1.16.0
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
)

replace github.com/hashicorp/terraform => ./internal/workaround/hashicorp/terraform
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBb
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alessio/shellescape v1.3.0 h1:rlgsKOIa8j5fkSs7uqjlU2FkIdhuJWSQC1rQQybVD54=
github.com/alessio/shellescape v1.3.0/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/alessio/shellescape v1.3.1 h1:cY65176tt6U1YAH9JsxT1DyXdluB2NTR8aiZFaFUXxs=
github.com/alessio/shellescape v1.3.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U=
Expand Down Expand Up @@ -209,6 +211,8 @@ github.com/hashicorp/terraform-plugin-go v0.1.0 h1:kyXZ0nkHxiRev/q18N40IbRRk4AV0
github.com/hashicorp/terraform-plugin-go v0.1.0/go.mod h1:10V6F3taeDWVAoLlkmArKttR3IULlRWFAGtQIQTIDr4=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.2.0 h1:2m4uKA97R8ijHGLwhHdpSJyI8Op1FpS/ozpoF21jK7s=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.2.0/go.mod h1:+12dJQebYjuU/yiq94iZUPuC66abfRBrXdpVJia3ojk=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0 h1:Egv+R1tOOjPNz643KBTx3tLT6RdFGGYJcZlyLvrPcEU=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0/go.mod h1:+12dJQebYjuU/yiq94iZUPuC66abfRBrXdpVJia3ojk=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
Expand Down Expand Up @@ -402,6 +406,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgN
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down
9 changes: 6 additions & 3 deletions linux/directory-resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package linux
import (
"context"
"errors"
"regexp"

"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/spf13/cast"
)

Expand Down Expand Up @@ -36,9 +38,10 @@ var schemaDirectoryResource = map[string]*schema.Schema{
Default: 0,
},
attrDirectoryMode: {
Type: schema.TypeString,
Optional: true,
Default: "755",
Type: schema.TypeString,
Optional: true,
Default: "755",
ValidateFunc: validation.StringMatch(regexp.MustCompile("[0-7]{3}"), "Invalid linux permission"),
},
attrDirectoryOverwrite: {
Type: schema.TypeBool,
Expand Down
9 changes: 6 additions & 3 deletions linux/file-resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package linux
import (
"context"
"errors"
"regexp"

"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/spf13/cast"
)

Expand Down Expand Up @@ -45,9 +47,10 @@ var schemaFileResource = map[string]*schema.Schema{
Default: 0,
},
attrFileMode: {
Type: schema.TypeString,
Optional: true,
Default: "644",
Type: schema.TypeString,
Optional: true,
Default: "644",
ValidateFunc: validation.StringMatch(regexp.MustCompile("[0-7]{3}"), "Invalid linux permission"),
},
attrFileIgnoreContent: {
Type: schema.TypeBool,
Expand Down
4 changes: 3 additions & 1 deletion linux/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ func Provider() *schema.Provider {
}
return l, diags
},

DataSourcesMap: map[string]*schema.Resource{
"linux_script": scriptDataSource(),
},
ResourcesMap: map[string]*schema.Resource{
"linux_file": fileResource(),
"linux_directory": directoryResource(),
Expand Down
53 changes: 53 additions & 0 deletions linux/script-datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package linux

import (
"context"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

var schemaScriptDataSource = func() (m map[string]*schema.Schema) {
m = make(map[string]*schema.Schema)
for k, v := range schemaScriptResource {
switch k {
default:
m[k] = v

case attrScriptLifecycleCommands:
m[k] = new(schema.Schema)
*m[k] = *v
m[k].Elem = &schema.Resource{
Schema: map[string]*schema.Schema{
attrScriptLifecycleCommandRead: v.Elem.(*schema.Resource).Schema[attrScriptLifecycleCommandRead],
},
}

case attrScriptTriggers:
case attrScriptDirtyOutput:
case attrScriptFaultyOutput:
}
}
return
}()

type handlerScriptDataSource struct {
hsr handlerScriptResource
}

func (h handlerScriptDataSource) Read(ctx context.Context, rd *schema.ResourceData, meta interface{}) (d diag.Diagnostics) {
err := h.hsr.read(ctx, rd, meta.(*linux))
if err != nil {
d = diag.FromErr(err)
}
rd.SetId("static")
return
}

func scriptDataSource() *schema.Resource {
h := handlerScriptDataSource{hsr: handlerScriptResource{}}
return &schema.Resource{
Schema: schemaScriptDataSource,
ReadContext: h.Read,
}
}
100 changes: 100 additions & 0 deletions linux/script-datasource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package linux

import (
"fmt"
"regexp"
"strings"
"testing"

"github.com/MakeNowJust/heredoc"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/stretchr/testify/require"
)

func TestAccLinuxDataScriptBasic(t *testing.T) {
file := fmt.Sprintf(`"/tmp/linux-%s.yml"`, acctest.RandString(16))
conf := tfConf{
Provider: testAccProvider,
Script: tfScript{
Interpreter: tfList{`"sh"`, `"-c"`},
LifecycleCommands: tfmap{
attrScriptLifecycleCommandRead: `"cat $FILE"`,
attrScriptLifecycleCommandCreate: `"echo -n $CONTENT > $FILE"`,
attrScriptLifecycleCommandUpdate: `"echo -n $CONTENT > $FILE"`,
attrScriptLifecycleCommandDelete: `"rm $FILE"`,
},
Environment: tfmap{
"FILE": file,
"CONTENT": `"helloworld"`,
},
},
DataScript: tfScript{
Interpreter: tfList{`"sh"`, `"-c"`},
LifecycleCommands: tfmap{
attrScriptLifecycleCommandRead: `"cat $FILE"`,
},
Environment: tfmap{
"FILE": file,
},
},
}
failedScripte := conf.Copy(func(tc *tfConf) {
tc.DataScript.LifecycleCommands.With(attrScriptLifecycleCommandRead, `"cat $FILE.notexist"`)
})
updatedContent := conf.Copy(func(tc *tfConf) {
tc.Script.Environment.With("CONTENT", `"helloworld1"`)
})

resource.Test(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{"null": {}},
PreCheck: testAccPreCheckConnection(t),
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccLinuxDataScriptBasicConfig(t, conf),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("null_resource.output", "triggers.output", strings.Trim(conf.Script.Environment["CONTENT"], `"`)),
),
},
{
Config: testAccLinuxDataScriptBasicConfig(t, failedScripte),
ExpectError: regexp.MustCompile("No such file or directory"),
},
{
Config: testAccLinuxDataScriptBasicConfig(t, updatedContent),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("null_resource.output", "triggers.output", strings.Trim(updatedContent.Script.Environment["CONTENT"], `"`)),
),
},
},
})
}

func testAccLinuxDataScriptBasicConfig(t *testing.T, conf tfConf) (s string) {
tf := heredoc.Doc(`
provider "linux" {
{{- .Provider.Serialize | nindent 4 }}
}
resource "linux_script" "linux_script" {
{{ .Script.Serialize | nindent 4 }}
}
data "linux_script" "linux_script" {
depends_on = [ linux_script.linux_script ]
{{ .DataScript.Serialize | nindent 4 }}
}
resource "null_resource" "output" {
triggers = {
output = data.linux_script.linux_script.output
}
}
`)

s, err := conf.compile(tf)
t.Log(s)
require.NoError(t, err, "compile template failed")
return
}
2 changes: 0 additions & 2 deletions linux/script-resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,6 @@ func (h handlerScriptResource) restoreOldResourceData(rd *schema.ResourceData, e
}

func (h handlerScriptResource) UpdateCommands(ctx context.Context, rd *schema.ResourceData, meta interface{}) (d diag.Diagnostics) {
_ = h.restoreOldResourceData(rd, h.attrs(h.attrCommands(), h.attrInternal())) // just to be sure

if rd.HasChange(attrScriptLifecycleCommands + ".0." + attrScriptLifecycleCommandRead) {
err := h.read(ctx, rd, meta.(*linux))
if err != nil {
Expand Down
12 changes: 7 additions & 5 deletions linux/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,12 @@ func (t tfScript) Serialize() (s string, err error) {
}

type tfConf struct {
Provider tfmap
File tfmap
Directory tfmap
Script tfScript
Extra tfmap
Provider tfmap
File tfmap
Directory tfmap
Script tfScript
DataScript tfScript
Extra tfmap
}

func (c tfConf) compile(tmpl string) (string, error) {
Expand All @@ -196,6 +197,7 @@ func (c tfConf) Copy(modifers ...func(*tfConf)) (n tfConf) {
n.File = c.File.Copy()
n.Directory = c.Directory.Copy()
n.Script = c.Script.Copy()
n.DataScript = c.DataScript.Copy()
n.Extra = c.Extra.Copy()
for _, m := range modifers {
m(&n)
Expand Down

0 comments on commit 184d847

Please sign in to comment.