Skip to content

Commit

Permalink
Enhancement for smooth commands update (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
rucciva committed Nov 8, 2020
1 parent b250ad5 commit b9e50f1
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 232 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,40 @@ provider "linux" {
user = "root"
password = "root"
}
resource "linux_directory" "directory" {
path = "/tmp/linux/dir"
owner = 1000
group = 1000
mode = "755"
overwrite = true
recycle_path = "/tmp/recycle"
}
resource "linux_file" "file" {
path = "/tmp/linux/file"
content = <<-EOF
hello world
EOF
owner = 1000
group = 1000
mode = "644"
overwrite = true
recycle_path = "/tmp/recycle"
}
resource "linux_script" "install_package" {
lifecycle_commands {
create = "apt update && apt install -y $PACKAGE_NAME=$PACKAGE_VERSION"
read = "apt-cache policy $PACKAGE_NAME | grep 'Installed:' | grep -v '(none)' | awk '{ print $2 }' | xargs | tr -d '\n'"
update = "apt update && apt install -y $PACKAGE_NAME=$PACKAGE_VERSION"
delete = "apt remove -y $PACKAGE_NAME"
}
environment = {
PACKAGE_NAME = "apache2"
PACKAGE_VERSION = "2.4.18-2ubuntu3.4"
}
}
```

Developing The Provider
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ resource "linux_file" "file" {
recycle_path = "/tmp/recycle"
}
resource "linux_script" "script" {
resource "linux_script" "install_package" {
lifecycle_commands {
create = "apt update && apt install -y $PACKAGE_NAME=$PACKAGE_VERSION"
read = "apt-cache policy $PACKAGE_NAME | grep 'Installed:' | grep -v '(none)' | awk '{ print $2 }' | xargs | tr -d '\n'"
Expand Down
16 changes: 9 additions & 7 deletions docs/resources/script.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Manage arbritrary resource by specifying scripts that will be executed remotely
## Example Usage

```hcl
resource "linux_script" "script" {
resource "linux_script" "install_package" {
lifecycle_commands {
create = "apt install -y $PACKAGE_NAME=$PACKAGE_VERSION"
read = "apt-cache policy $PACKAGE_NAME | grep 'Installed:' | grep -v '(none)' | awk '{ print $2 }' | xargs | tr -d '\n'"
Expand All @@ -23,7 +23,7 @@ resource "linux_script" "script" {

The following arguments are supported:

- `lifecycle_commands` - (Required) Block that contains commands to be remotely executed respectively in Create|Read|Update|Delete phase. For complex commands, use [the file function](https://www.terraform.io/docs/configuration/functions/file.html).
- `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.
Expand All @@ -32,12 +32,14 @@ The following arguments are supported:

### lifecycle_commands

The following arguments are supported:
Block that contains commands to be remotely executed respectively in **Create**,**Read**,**Update**, and **Delete** phase. For complex commands, use [the file function](https://www.terraform.io/docs/configuration/functions/file.html). The following arguments are supported:

- `create` - (Required, string) Commands that will be executed in **Create** phase.
- `read` - (Required, string) Commands that will be executed in Read phase and after execution of `create` or `update` commands. Terraform will record the output of these commands inside `output` attributes and trigger update/recreation when it changes (in **Read** phase only). If the result of running these commands produce an error, then it will give a signal for resource recreation. In this scenario, user have three options before applying the changes: (1) do nothing since the resource has indeed become absent, (2) manually modifying the linux machine so no error will be produced in the next run, (3) update the commands. It is recommended that this operations does not do any kind of 'write' operation.
- `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.

- `create` - (Required, string) Commands that will be execued in Create phase.
- `read` - (Required, string) Commands that will be execued in Read phase and after execution of `create` or `update` commands. Terraform will record the output of these commands inside `output` attributes and trigger update/recreation when it changes. If the result of running these commands is empty string, the resource is considered as destroyed. If instead execution error is produced, then it will trigger resource recreation, unless the script is updated and `update` is not empty.
- `update` - (Optional, string) Commands that will be execued in Update phase. Previous `output` are available from stdin. If `read` is updated, the next `terraform apply` will only execute the new `read` script. If `create` and/or `update` is the only things that are changed, then nothing is executed. Omiting this will disable Update phase and trigger resource recreation (Delete -> Create) each time terraform detect changes.
- `delete` - (Required, string) Commands that will be execued 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.

## Attribute Reference

Expand Down
34 changes: 15 additions & 19 deletions linux/directory-resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ func TestAccLinuxDirectoryBasic(t *testing.T) {
Provider: testAccProvider,
Directory: tNewTFMapDirectory().Without("owner", "group", "mode"),
}
conf2 := tfConf{
Provider: testAccProvider,
Directory: conf1.Directory.Copy().With("mode", "700"),
}
conf2 := conf1.Copy(func(tc *tfConf) {
tc.Directory.With("mode", "700")
})
conf3 := tfConf{
Provider: testAccProvider,
Directory: tNewTFMapDirectory(),
Expand Down Expand Up @@ -124,49 +123,46 @@ func testAccLinuxDirectoryBasicConfig(t *testing.T, conf tfConf) (s string) {
return
}

func TestAccLinuxDirectoryOverride(t *testing.T) {
func TestAccLinuxDirectoryOverwrite(t *testing.T) {
conf1 := tfConf{
Provider: testAccProvider,
Directory: tNewTFMapDirectory(),
}
conf2 := tfConf{
Provider: testAccProvider,
Directory: conf1.Directory.Copy().With("overwrite", "true"),
}
conf2 := conf1.Copy(func(tc *tfConf) {
tc.Directory.With("overwrite", "true")
})
conf3 := tfConf{
Provider: testAccProvider,
Directory: tNewTFMapDirectory(),
Extra: tfmap{"path_previous": conf1.Directory["path"]},
}
conf4 := tfConf{
Provider: testAccProvider,
Directory: conf3.Directory.Copy().With("overwrite", "true"),
Extra: tfmap{"path_previous": conf1.Directory["path"]},
}
conf4 := conf3.Copy(func(tc *tfConf) {
tc.Directory.With("overwrite", "true")
})
resource.Test(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{"null": {}},
PreCheck: testAccPreCheckConnection(t),
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccLinuxDirectoryeOverrideConfig(t, conf1),
Config: testAccLinuxDirectoryeOverwriteConfig(t, conf1),
ExpectError: regexp.MustCompile(" exist"),
},
{
Config: testAccLinuxDirectoryeOverrideConfig(t, conf2),
Config: testAccLinuxDirectoryeOverwriteConfig(t, conf2),
},
{
Config: testAccLinuxDirectoryeOverrideConfig(t, conf3),
Config: testAccLinuxDirectoryeOverwriteConfig(t, conf3),
ExpectError: regexp.MustCompile(" exist"),
},
{
Config: testAccLinuxDirectoryeOverrideConfig(t, conf4),
Config: testAccLinuxDirectoryeOverwriteConfig(t, conf4),
},
},
})
}

func testAccLinuxDirectoryeOverrideConfig(t *testing.T, conf tfConf) (s string) {
func testAccLinuxDirectoryeOverwriteConfig(t *testing.T, conf tfConf) (s string) {
tf := heredoc.Doc(`
provider "linux" {
{{- .Provider.Serialize | nindent 4 }}
Expand Down
22 changes: 9 additions & 13 deletions linux/file-resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ func TestAccLinuxFileBasic(t *testing.T) {
Provider: testAccProvider,
File: tNewTFMapFile().Without("owner", "group", "mode"),
}
conf2 := tfConf{
Provider: testAccProvider,
File: conf1.File.Copy().With("content", `"test"`),
}
conf2 := conf1.Copy(func(tc *tfConf) {
tc.File.With("content", `"test"`)
})
conf3 := tfConf{
Provider: testAccProvider,
File: tNewTFMapFile(),
Expand Down Expand Up @@ -116,20 +115,17 @@ func TestAccLinuxFileOverride(t *testing.T) {
Provider: testAccProvider,
File: tNewTFMapFile(),
}
conf2 := tfConf{
Provider: testAccProvider,
File: conf1.File.Copy().With("overwrite", "true"),
}
conf2 := conf1.Copy(func(tc *tfConf) {
tc.File.With("overwrite", "true")
})
conf3 := tfConf{
Provider: testAccProvider,
File: tNewTFMapFile(),
Extra: tfmap{"path_previous": conf1.File["path"]},
}
conf4 := tfConf{
Provider: testAccProvider,
File: conf3.File.Copy().With("overwrite", "true"),
Extra: tfmap{"path_previous": conf1.File["path"]},
}
conf4 := conf3.Copy(func(tc *tfConf) {
tc.File.With("overwrite", "true")
})

resource.Test(t, resource.TestCase{
ExternalProviders: map[string]resource.ExternalProvider{"null": {}},
Expand Down
Loading

0 comments on commit b9e50f1

Please sign in to comment.