Add local directory (#201)
* feat: add local directory management --------- Signed-off-by: matttrach <matt.trachier@suse.com>
This commit is contained in:
parent
bd0cb43401
commit
a1147e54ec
|
|
@ -27,11 +27,10 @@ testacc: build
|
|||
gotestsum --format standard-verbose --jsonfile report.json --post-run-command "./summarize.sh" -- ./... -v -p=1 -timeout=300s; \
|
||||
popd;
|
||||
|
||||
debug: build
|
||||
et: build
|
||||
export REPO_ROOT="../../../."; \
|
||||
export TF_LOG=DEBUG; \
|
||||
pushd ./test; \
|
||||
gotestsum --format standard-verbose --jsonfile report.json --post-run-command "./summarize.sh" -- ./... -v -p=1 -timeout=300s; \
|
||||
gotestsum --format standard-verbose --jsonfile report.json --post-run-command "./summarize.sh" -- ./... -v -p=1 -timeout=300s -run=$(t); \
|
||||
popd;
|
||||
|
||||
.PHONY: fmt lint build install generate test testacc debug
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "file_local_directory Data Source - file"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
LocalDirectory File DataSource
|
||||
---
|
||||
|
||||
# file_local_directory (Data Source)
|
||||
|
||||
LocalDirectory File DataSource
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
# tflint-ignore: terraform_unused_declarations
|
||||
data "file_local_directory" "basic_example" {
|
||||
path = "example_directory"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `path` (String) Path to directory.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `files` (Attributes List) List of information about files in the directory. (see [below for nested schema](#nestedatt--files))
|
||||
- `id` (String) Identifier derived from sha256 hash of path.
|
||||
- `permissions` (String) Permissions of the directory.
|
||||
|
||||
<a id="nestedatt--files"></a>
|
||||
### Nested Schema for `files`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `is_directory` (String) A string representation of whether or not the item is a directory or a file. This will be 'true' if the item is a directory, or 'false' if it isn't.
|
||||
- `last_modified` (String) The UTC date of the last time the file was updated.
|
||||
- `name` (String) The file's name.
|
||||
- `permissions` (String) The file's permissions mode expressed in string format, eg. '0600'.
|
||||
- `size` (String) The file's size in bytes.
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "file_snapshot Data Source - file"
|
||||
page_title: "file_local_snapshot Data Source - file"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
File Snapshot data source.
|
||||
This data source retrieves the contents of a file from the output of a file_snapshot datasource.Warning! Using this resource places the plain text contents of the snapshot in your state file.
|
||||
File LocalSnapshot data source.
|
||||
This data source retrieves the contents of a file from the output of a file_local_snapshot datasource.Warning! Using this resource places the plain text contents of the snapshot in your state file.
|
||||
---
|
||||
|
||||
# file_snapshot (Data Source)
|
||||
# file_local_snapshot (Data Source)
|
||||
|
||||
File Snapshot data source.
|
||||
This data source retrieves the contents of a file from the output of a file_snapshot datasource.Warning! Using this resource places the plain text contents of the snapshot in your state file.
|
||||
File LocalSnapshot data source.
|
||||
This data source retrieves the contents of a file from the output of a file_local_snapshot datasource.Warning! Using this resource places the plain text contents of the snapshot in your state file.
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "file_local_directory Resource - file"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Local Directory resource.
|
||||
---
|
||||
|
||||
# file_local_directory (Resource)
|
||||
|
||||
Local Directory resource.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "file_local_directory" "basic_example" {
|
||||
path = "path/to/new/directory"
|
||||
permissions = "0700"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `path` (String) Directory path, required. All subdirectories will also be created. Changing this forces recreate.
|
||||
|
||||
### Optional
|
||||
|
||||
- `permissions` (String) The directory permissions to assign to the directory, defaults to '0700'. In order to automatically create subdirectories the owner must have execute access, ie. '0600' or less prevents the provider from creating subdirectories.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `created` (String) The top level directory created. eg. if 'path' = '/path/to/new/directory' and '/path/to' already exists, but the rest doesn't, then 'created' will be '/path/to/new'. This path will be recursively removed during destroy and recreate actions.
|
||||
- `id` (String) Identifier derived from sha256 hash of path.
|
||||
|
||||
## Import
|
||||
|
||||
Import is supported using the following syntax:
|
||||
|
||||
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
|
||||
|
||||
```shell
|
||||
# IDENTIFIER="$(echo -n "path/to/file" sha256sum | awk '{print $1}')"
|
||||
terraform import file_local_directory.example "IDENTIFIER"
|
||||
|
||||
# after this is run you will need to refine the resource further by setting the path and created properties.
|
||||
```
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "file_snapshot Resource - file"
|
||||
page_title: "file_local_snapshot Resource - file"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
File Snapshot resource.
|
||||
File LocalSnapshot resource.
|
||||
This resource saves some content in state and doesn't update it until the trigger argument changes. The refresh phase doesn't update state, instead the state can only change on create or update and only when the update_trigger argument changes.
|
||||
---
|
||||
|
||||
# file_snapshot (Resource)
|
||||
# file_local_snapshot (Resource)
|
||||
|
||||
File Snapshot resource.
|
||||
File LocalSnapshot resource.
|
||||
This resource saves some content in state and doesn't update it until the trigger argument changes. The refresh phase doesn't update state, instead the state can only change on create or update and only when the update_trigger argument changes.
|
||||
|
||||
## Example Usage
|
||||
|
|
@ -20,7 +20,7 @@ resource "file_local" "snapshot_file_basic_example" {
|
|||
name = "snapshot_resource_basic_example.txt"
|
||||
contents = "this is an example file that is used to show how snapshots work"
|
||||
}
|
||||
resource "file_snapshot" "basic_example" {
|
||||
resource "file_local_snapshot" "basic_example" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_basic_example,
|
||||
]
|
||||
|
|
@ -28,21 +28,21 @@ resource "file_snapshot" "basic_example" {
|
|||
update_trigger = "an arbitrary string"
|
||||
}
|
||||
output "snapshot_basic" {
|
||||
value = file_snapshot.basic_example.snapshot
|
||||
value = file_local_snapshot.basic_example.snapshot
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# A more advanced use case:
|
||||
# We use a file_local resource to write a local file in the current directory
|
||||
# then we create a snapshot of the file using file_snapshot
|
||||
# then we create a snapshot of the file using file_local_snapshot
|
||||
# then we update the file using a terraform_data resource
|
||||
# then we get the contents of the file using a file_local datasource
|
||||
# then we output both the file_local datasource and file_snapshot resource, observing that they are different
|
||||
# then we output both the file_local datasource and file_local_snapshot resource, observing that they are different
|
||||
resource "file_local" "snapshot_file_example" {
|
||||
name = "snapshot_resource_test.txt"
|
||||
contents = "this is an example file that is used to show how snapshots work"
|
||||
}
|
||||
resource "file_snapshot" "file_example" {
|
||||
resource "file_local_snapshot" "file_example" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_example,
|
||||
]
|
||||
|
|
@ -52,7 +52,7 @@ resource "file_snapshot" "file_example" {
|
|||
resource "terraform_data" "update_file" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_example,
|
||||
file_snapshot.file_example,
|
||||
file_local_snapshot.file_example,
|
||||
]
|
||||
provisioner "local-exec" {
|
||||
command = <<-EOT
|
||||
|
|
@ -63,7 +63,7 @@ resource "terraform_data" "update_file" {
|
|||
data "file_local" "snapshot_file_example_after_update" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_example,
|
||||
file_snapshot.file_example,
|
||||
file_local_snapshot.file_example,
|
||||
terraform_data.update_file,
|
||||
]
|
||||
name = "snapshot_resource_test.txt"
|
||||
|
|
@ -75,7 +75,7 @@ output "file" {
|
|||
# this updates a file that is used to show how snapshots work
|
||||
}
|
||||
output "snapshot" {
|
||||
value = base64decode(file_snapshot.file_example.snapshot)
|
||||
value = base64decode(file_local_snapshot.file_example.snapshot)
|
||||
sensitive = true
|
||||
# this is an example file that is used to show how snapshots work
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
# tflint-ignore: terraform_unused_declarations
|
||||
data "file_local_directory" "basic_example" {
|
||||
path = "example_directory"
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# IDENTIFIER="$(echo -n "path/to/file" sha256sum | awk '{print $1}')"
|
||||
terraform import file_local_directory.example "IDENTIFIER"
|
||||
|
||||
# after this is run you will need to refine the resource further by setting the path and created properties.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
resource "file_local_directory" "basic_example" {
|
||||
path = "path/to/new/directory"
|
||||
permissions = "0700"
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ resource "file_local" "snapshot_file_basic_example" {
|
|||
name = "snapshot_resource_basic_example.txt"
|
||||
contents = "this is an example file that is used to show how snapshots work"
|
||||
}
|
||||
resource "file_snapshot" "basic_example" {
|
||||
resource "file_local_snapshot" "basic_example" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_basic_example,
|
||||
]
|
||||
|
|
@ -12,21 +12,21 @@ resource "file_snapshot" "basic_example" {
|
|||
update_trigger = "an arbitrary string"
|
||||
}
|
||||
output "snapshot_basic" {
|
||||
value = file_snapshot.basic_example.snapshot
|
||||
value = file_local_snapshot.basic_example.snapshot
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# A more advanced use case:
|
||||
# We use a file_local resource to write a local file in the current directory
|
||||
# then we create a snapshot of the file using file_snapshot
|
||||
# then we create a snapshot of the file using file_local_snapshot
|
||||
# then we update the file using a terraform_data resource
|
||||
# then we get the contents of the file using a file_local datasource
|
||||
# then we output both the file_local datasource and file_snapshot resource, observing that they are different
|
||||
# then we output both the file_local datasource and file_local_snapshot resource, observing that they are different
|
||||
resource "file_local" "snapshot_file_example" {
|
||||
name = "snapshot_resource_test.txt"
|
||||
contents = "this is an example file that is used to show how snapshots work"
|
||||
}
|
||||
resource "file_snapshot" "file_example" {
|
||||
resource "file_local_snapshot" "file_example" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_example,
|
||||
]
|
||||
|
|
@ -36,7 +36,7 @@ resource "file_snapshot" "file_example" {
|
|||
resource "terraform_data" "update_file" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_example,
|
||||
file_snapshot.file_example,
|
||||
file_local_snapshot.file_example,
|
||||
]
|
||||
provisioner "local-exec" {
|
||||
command = <<-EOT
|
||||
|
|
@ -47,7 +47,7 @@ resource "terraform_data" "update_file" {
|
|||
data "file_local" "snapshot_file_example_after_update" {
|
||||
depends_on = [
|
||||
file_local.snapshot_file_example,
|
||||
file_snapshot.file_example,
|
||||
file_local_snapshot.file_example,
|
||||
terraform_data.update_file,
|
||||
]
|
||||
name = "snapshot_resource_test.txt"
|
||||
|
|
@ -59,7 +59,7 @@ output "file" {
|
|||
# this updates a file that is used to show how snapshots work
|
||||
}
|
||||
output "snapshot" {
|
||||
value = base64decode(file_snapshot.file_example.snapshot)
|
||||
value = base64decode(file_local_snapshot.file_example.snapshot)
|
||||
sensitive = true
|
||||
# this is an example file that is used to show how snapshots work
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Local Directory Use Case
|
||||
|
||||
This is a more advanced use case for adding a directory.
|
||||
|
||||
The goal of this use case is to retrieve the files in the directory at a specific point in time.
|
||||
We don't want the live data because we add files that we don't want included in the output.
|
||||
|
||||
These are the steps:
|
||||
1. we generate a directory
|
||||
2. add files to it
|
||||
3. get the directory data
|
||||
4. save the directory data to a file in the directory
|
||||
5. snapshot the directory data
|
||||
6. output the snapshot
|
||||
|
||||
The resulting output will always be the files we first placed in the directory excluding the directory data file.
|
||||
|
||||
On the initial run the directory data will match the snapshot,
|
||||
but on subsequent runs the refresh phase will update the directory data to include the directory data file.
|
||||
Our snapshot data will always exclude this file though, since it only updates when we alter the update trigger.
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
|
||||
provider "file" {}
|
||||
|
||||
locals {
|
||||
path = var.path
|
||||
}
|
||||
|
||||
resource "file_local_directory" "basic" {
|
||||
path = local.path
|
||||
}
|
||||
|
||||
resource "file_local" "a" {
|
||||
depends_on = [
|
||||
file_local_directory.basic,
|
||||
]
|
||||
name = "a"
|
||||
directory = local.path
|
||||
contents = "An example file to place in the directory."
|
||||
}
|
||||
resource "file_local" "b" {
|
||||
depends_on = [
|
||||
file_local_directory.basic,
|
||||
]
|
||||
name = "b"
|
||||
directory = local.path
|
||||
contents = "An example file to place in the directory."
|
||||
}
|
||||
resource "file_local" "c" {
|
||||
depends_on = [
|
||||
file_local_directory.basic,
|
||||
]
|
||||
name = "c"
|
||||
directory = local.path
|
||||
contents = "An example file to place in the directory."
|
||||
}
|
||||
|
||||
data "file_local_directory" "basic" {
|
||||
depends_on = [
|
||||
file_local_directory.basic,
|
||||
file_local.a,
|
||||
file_local.b,
|
||||
file_local.c,
|
||||
]
|
||||
path = local.path
|
||||
}
|
||||
|
||||
resource "file_local" "directory_info" {
|
||||
depends_on = [
|
||||
file_local_directory.basic,
|
||||
file_local.a,
|
||||
file_local.b,
|
||||
file_local.c,
|
||||
data.file_local_directory.basic,
|
||||
]
|
||||
name = "directory_info.txt"
|
||||
directory = local.path
|
||||
contents = jsonencode(data.file_local_directory.basic)
|
||||
}
|
||||
|
||||
resource "file_local_snapshot" "directory_snapshot" {
|
||||
depends_on = [
|
||||
file_local_directory.basic,
|
||||
file_local.a,
|
||||
file_local.b,
|
||||
file_local.c,
|
||||
data.file_local_directory.basic,
|
||||
file_local.directory_info,
|
||||
]
|
||||
name = "directory_info.txt"
|
||||
directory = local.path
|
||||
update_trigger = "manual"
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
|
||||
output "directory_resource" {
|
||||
value = jsonencode(file_local_directory.basic)
|
||||
}
|
||||
|
||||
output "directory_data_source" {
|
||||
value = jsonencode(data.file_local_directory.basic)
|
||||
}
|
||||
|
||||
output "snapshot" {
|
||||
value = base64decode(file_local_snapshot.directory_snapshot.snapshot)
|
||||
sensitive = true
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
variable "path" {
|
||||
type = string
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
required_providers {
|
||||
file = {
|
||||
source = "rancher/file"
|
||||
version = ">= 0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Local Directory Use Case
|
||||
|
||||
This is the most basic use case for adding a directory.
|
||||
It creates the directory at the path specified, generating all subdirectories necessary to do so.
|
||||
It records the created directories and deletes only the ones it created on destroy.
|
||||
|
||||
This example will only set the required fields, all options accept the default value.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
terraform {
|
||||
backend "local" {}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
|
||||
provider "file" {}
|
||||
|
||||
locals {
|
||||
path = var.path
|
||||
}
|
||||
|
||||
resource "file_local_directory" "basic" {
|
||||
path = local.path
|
||||
}
|
||||
|
||||
data "file_local_directory" "basic" {
|
||||
depends_on = [
|
||||
file_local_directory.basic,
|
||||
]
|
||||
path = local.path
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
output "directory_resource" {
|
||||
value = jsonencode(file_local_directory.basic)
|
||||
}
|
||||
|
||||
output "directory_data_source" {
|
||||
value = jsonencode(data.file_local_directory.basic)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
variable "path" {
|
||||
type = string
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
required_providers {
|
||||
file = {
|
||||
source = "rancher/file"
|
||||
version = ">= 0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
terraform {
|
||||
backend "local" {}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ resource "file_local" "snapshot_use_case_basic" {
|
|||
directory = local.directory
|
||||
contents = local.pesky_id
|
||||
}
|
||||
resource "file_snapshot" "use_case_basic" {
|
||||
resource "file_local_snapshot" "use_case_basic" {
|
||||
depends_on = [
|
||||
file_local.snapshot_use_case_basic,
|
||||
]
|
||||
|
|
@ -4,6 +4,6 @@ output "pesky_id" {
|
|||
}
|
||||
|
||||
output "snapshot" {
|
||||
value = base64decode(file_snapshot.use_case_basic.snapshot)
|
||||
value = base64decode(file_local_snapshot.use_case_basic.snapshot)
|
||||
sensitive = true
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
required_providers {
|
||||
file = {
|
||||
source = "rancher/file"
|
||||
version = ">= 0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Compressed Snapshot Use Case
|
||||
|
||||
This is an example of how you could use the file_snapshot resource.
|
||||
This is an example of how you could use the file_local_snapshot resource.
|
||||
WARNING! Please remember that Terraform must load the entire state into memory,
|
||||
make sure you have the resources available on the machine running Terraform to handle any file you save like this.
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
terraform {
|
||||
backend "local" {}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ resource "file_local" "snapshot_use_case_compressed" {
|
|||
directory = local.directory
|
||||
contents = local.pesky_id
|
||||
}
|
||||
resource "file_snapshot" "use_case_compressed" {
|
||||
resource "file_local_snapshot" "use_case_compressed" {
|
||||
depends_on = [
|
||||
file_local.snapshot_use_case_compressed,
|
||||
]
|
||||
|
|
@ -25,11 +25,11 @@ resource "file_snapshot" "use_case_compressed" {
|
|||
update_trigger = local.update
|
||||
compress = true
|
||||
}
|
||||
data "file_snapshot" "use_case_compressed" {
|
||||
data "file_local_snapshot" "use_case_compressed" {
|
||||
depends_on = [
|
||||
file_local.snapshot_use_case_compressed,
|
||||
file_snapshot.use_case_compressed,
|
||||
file_local_snapshot.use_case_compressed,
|
||||
]
|
||||
contents = file_snapshot.use_case_compressed.snapshot
|
||||
contents = file_local_snapshot.use_case_compressed.snapshot
|
||||
decompress = true
|
||||
}
|
||||
|
|
@ -4,6 +4,6 @@ output "pesky_id" {
|
|||
}
|
||||
|
||||
output "snapshot" {
|
||||
value = data.file_snapshot.use_case_compressed.data
|
||||
value = data.file_local_snapshot.use_case_compressed.data
|
||||
sensitive = true
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
required_providers {
|
||||
file = {
|
||||
source = "rancher/file"
|
||||
version = ">= 0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1758262103,
|
||||
"narHash": "sha256-aBGl3XEOsjWw6W3AHiKibN7FeoG73dutQQEqnd/etR8=",
|
||||
"lastModified": 1759070547,
|
||||
"narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "12bd230118a1901a4a5d393f9f56b6ad7e571d01",
|
||||
"rev": "647e5c14cbd5067f44ac86b74f014962df460840",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package directory_client
|
||||
|
||||
type DirectoryClient interface {
|
||||
Create(path string, permissions string) (string, error) // Base of the newly created path (used in destroy), error
|
||||
// If directory isn't found the error message must have err.Error() == "directory not found"
|
||||
Read(path string) (string, map[string]map[string]string, error) // permissions, files info map, error
|
||||
Update(path string, permissions string) error
|
||||
Delete(path string) error // "path" should be the return from Create
|
||||
CreateFile(path string, data string, permissions string, lastModified string) error // create a file in the given directory
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package directory_client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ DirectoryClient = &MemoryDirectoryClient{} // make sure the MemoryDirectoryClient implements the DirectoryClient
|
||||
|
||||
type MemoryDirectoryClient struct {
|
||||
directory map[string]interface{}
|
||||
}
|
||||
|
||||
func (c *MemoryDirectoryClient) Create(path string, permissions string) (string, error) {
|
||||
|
||||
path = filepath.Clean(path)
|
||||
var base string
|
||||
if filepath.IsAbs(path) {
|
||||
base = filepath.VolumeName(path) + string(filepath.Separator)
|
||||
} else {
|
||||
p := strings.Split(path, string(filepath.Separator))
|
||||
base = p[0]
|
||||
}
|
||||
|
||||
c.directory = make(map[string]interface{})
|
||||
c.directory["permissions"] = permissions
|
||||
c.directory["path"] = path
|
||||
c.directory["base"] = base
|
||||
c.directory["info"] = map[string]map[string]string{}
|
||||
return base, nil
|
||||
}
|
||||
|
||||
func (c *MemoryDirectoryClient) Read(path string) (string, map[string]map[string]string, error) {
|
||||
if c.directory == nil {
|
||||
return "", nil, fmt.Errorf("directory not found")
|
||||
}
|
||||
permissions, _ := c.directory["permissions"].(string)
|
||||
info, _ := c.directory["info"].(map[string]map[string]string)
|
||||
return permissions, info, nil
|
||||
}
|
||||
|
||||
func (c *MemoryDirectoryClient) Update(path string, permissions string) error {
|
||||
c.directory["permissions"] = permissions
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *MemoryDirectoryClient) Delete(path string) error {
|
||||
c.directory = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *MemoryDirectoryClient) CreateFile(path string, data string, permissions string, lastModified string) error {
|
||||
if c.directory == nil {
|
||||
return fmt.Errorf("directory not found")
|
||||
}
|
||||
info, ok := c.directory["info"].(map[string]map[string]string)
|
||||
if !ok {
|
||||
return fmt.Errorf("directory info not found")
|
||||
}
|
||||
info[path] = map[string]string{
|
||||
"Size": fmt.Sprintf("%d", len(data)),
|
||||
"Mode": permissions,
|
||||
"ModTime": lastModified,
|
||||
"IsDir": "false",
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
package directory_client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var _ DirectoryClient = &OsDirectoryClient{} // make sure the OsDirectoryClient implements the DirectoryClient
|
||||
|
||||
type OsDirectoryClient struct {
|
||||
}
|
||||
|
||||
func (c *OsDirectoryClient) Create(path string, permissions string) (string, error) {
|
||||
created, err := makePath(path, permissions)
|
||||
if len(created) > 0 {
|
||||
fmt.Printf("created: %#v", created)
|
||||
return created[0], err
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OsDirectoryClient) Read(path string) (string, map[string]map[string]string, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil, fmt.Errorf("directory not found")
|
||||
}
|
||||
return "", nil, err
|
||||
}
|
||||
mode := fmt.Sprintf("%#o", info.Mode().Perm())
|
||||
|
||||
data, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
files := make(map[string]map[string]string)
|
||||
for _, file := range data {
|
||||
var isDir string
|
||||
fileInfo, err := file.Info()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if fileInfo.IsDir() {
|
||||
isDir = "true"
|
||||
} else {
|
||||
isDir = "false"
|
||||
}
|
||||
files[file.Name()] = map[string]string{
|
||||
"Size": strconv.FormatInt(fileInfo.Size(), 10),
|
||||
"Mode": fmt.Sprintf("%#o", fileInfo.Mode().Perm()),
|
||||
"ModTime": fileInfo.ModTime().String(),
|
||||
"IsDir": isDir,
|
||||
}
|
||||
}
|
||||
return mode, files, nil
|
||||
}
|
||||
|
||||
// The only thing that can be updated is the permissions.
|
||||
func (c *OsDirectoryClient) Update(path string, permissions string) error {
|
||||
modeInt, err := strconv.ParseUint(permissions, 8, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(path, os.FileMode(modeInt))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *OsDirectoryClient) Delete(path string) error {
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
func makePath(path string, permissions string) ([]string, error) {
|
||||
var created []string
|
||||
info, err := os.Stat(path)
|
||||
if err == nil {
|
||||
if info.IsDir() {
|
||||
return created, nil // Path already exists and is a directory.
|
||||
}
|
||||
// Path exists but is a file, which is an error.
|
||||
return nil, fmt.Errorf("path '%s' exists and is not a directory", path)
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
// There was an error, but not that the directory doesn't exist, something is up with the file system.
|
||||
return nil, err
|
||||
}
|
||||
// From here we know the filesystem is ready and the path doesn't exist.
|
||||
|
||||
parent := filepath.Dir(path)
|
||||
|
||||
if path == parent {
|
||||
// If we have reached a path with no parent then return the empty list.
|
||||
// This breaks the recursion.
|
||||
return created, nil
|
||||
}
|
||||
|
||||
// Start a recursion.
|
||||
// This will recurse until path = parent, where parentCreated will be the empty list.
|
||||
parentCreated, err := makePath(parent, permissions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Add the parent's created directories to our list.
|
||||
created = append(created, parentCreated...)
|
||||
|
||||
// The first time this point is reached we know:
|
||||
// - "created" is an empty list.
|
||||
// - the parent directory exists and is a valid directory.
|
||||
// - the filesystem is ready and the current path doesn't exist.
|
||||
// This means we are good to create the current path and return it.
|
||||
// Any other time we know:
|
||||
// - the parent directory exists and is a valid directory.
|
||||
// - the filesystem is ready and the current path doesn't exist.
|
||||
// - there was no error in the previous recursion.
|
||||
// This means the previous recursion must have successfully generated a directory.
|
||||
// In any recursion cycle from this point the parent directory exists and is valid.
|
||||
|
||||
modeInt, err := strconv.ParseUint(permissions, 8, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.Mkdir(path, os.FileMode(modeInt)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// We successfully created the directory, add it to our list.
|
||||
created = append(created, path)
|
||||
}
|
||||
|
||||
return created, nil
|
||||
}
|
||||
|
||||
// Added to help with testing, use the file client to create files in production.
|
||||
func (c *OsDirectoryClient) CreateFile(path string, data string, permissions string, lastModified string) error {
|
||||
modeInt, err := strconv.ParseUint(permissions, 8, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, []byte(data), os.FileMode(modeInt))
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ type FileClient interface {
|
|||
Read(directory string, name string) (string, string, error) // permissions, contents, error
|
||||
Update(currentDirectory string, currentName string, newDirectory string, newName string, data string, permissions string) error
|
||||
Delete(directory string, name string) error
|
||||
|
||||
Compress(directory string, name string, compressedName string) error
|
||||
Encode(directory string, name string, encodedName string) error
|
||||
Hash(directory string, name string) (string, error) // Sha256Hash, error
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package file_local_directory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
c "github.com/rancher/terraform-provider-file/internal/provider/directory_client"
|
||||
)
|
||||
|
||||
// The `var _` is a special Go construct that results in an unusable variable.
|
||||
// The purpose of these lines is to make sure our LocalDirectoryFileResource correctly implements the `resource.Resource“ interface.
|
||||
// These will fail at compilation time if the implementation is not satisfied.
|
||||
var _ datasource.DataSource = &LocalDirectoryDataSource{}
|
||||
|
||||
func NewLocalDirectoryDataSource() datasource.DataSource {
|
||||
return &LocalDirectoryDataSource{}
|
||||
}
|
||||
|
||||
type LocalDirectoryDataSource struct {
|
||||
client c.DirectoryClient
|
||||
}
|
||||
|
||||
type LocalDirectoryDataSourceModel struct {
|
||||
Id types.String `tfsdk:"id"`
|
||||
Path types.String `tfsdk:"path"`
|
||||
Permissions types.String `tfsdk:"permissions"`
|
||||
Files []LocalDirectoryFileInfoModel `tfsdk:"files"`
|
||||
}
|
||||
|
||||
type LocalDirectoryFileInfoModel struct {
|
||||
Name types.String `tfsdk:"name"`
|
||||
Size types.String `tfsdk:"size"`
|
||||
Permissions types.String `tfsdk:"permissions"`
|
||||
LastModified types.String `tfsdk:"last_modified"`
|
||||
IsDirectory types.String `tfsdk:"is_directory"`
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_local_directory" // file_local_directory datasource
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: "LocalDirectory File DataSource",
|
||||
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"path": schema.StringAttribute{
|
||||
MarkdownDescription: "Path to directory.",
|
||||
Required: true,
|
||||
},
|
||||
"permissions": schema.StringAttribute{
|
||||
MarkdownDescription: "Permissions of the directory.",
|
||||
Computed: true,
|
||||
},
|
||||
"files": schema.ListNestedAttribute{
|
||||
MarkdownDescription: "List of information about files in the directory.",
|
||||
Computed: true,
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"name": schema.StringAttribute{
|
||||
MarkdownDescription: "The file's name. ",
|
||||
Computed: true,
|
||||
},
|
||||
"size": schema.StringAttribute{
|
||||
MarkdownDescription: "The file's size in bytes. ",
|
||||
Computed: true,
|
||||
},
|
||||
"permissions": schema.StringAttribute{
|
||||
MarkdownDescription: "The file's permissions mode expressed in string format, eg. '0600'. ",
|
||||
Computed: true,
|
||||
},
|
||||
"last_modified": schema.StringAttribute{
|
||||
MarkdownDescription: "The UTC date of the last time the file was updated. ",
|
||||
Computed: true,
|
||||
},
|
||||
"is_directory": schema.StringAttribute{
|
||||
MarkdownDescription: "A string representation of whether or not the item is a directory or a file. " +
|
||||
"This will be 'true' if the item is a directory, or 'false' if it isn't.",
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"id": schema.StringAttribute{
|
||||
MarkdownDescription: "Identifier derived from sha256 hash of path. ",
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Read runs before all other resources are run, datasources only get the Read function.
|
||||
func (r *LocalDirectoryDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Request Object: %#v", req))
|
||||
|
||||
if r.client == nil {
|
||||
tflog.Debug(ctx, "Configuring client with default OsDirectoryClient.")
|
||||
r.client = &c.OsDirectoryClient{}
|
||||
}
|
||||
|
||||
var config LocalDirectoryDataSourceModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
id := config.Id.ValueString()
|
||||
path := config.Path.ValueString()
|
||||
|
||||
perm, files, err := r.client.Read(path)
|
||||
if err != nil && err.Error() == "directory not found" {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("failed to read directory", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(path))
|
||||
id = hex.EncodeToString(hasher.Sum(nil))
|
||||
config.Id = types.StringValue(id)
|
||||
}
|
||||
config.Permissions = types.StringValue(perm)
|
||||
|
||||
config.Files = []LocalDirectoryFileInfoModel{}
|
||||
for fileName, fileData := range files {
|
||||
fileInfo := LocalDirectoryFileInfoModel{
|
||||
Name: types.StringValue(fileName),
|
||||
Size: types.StringValue(fileData["Size"]),
|
||||
Permissions: types.StringValue(fileData["Mode"]),
|
||||
LastModified: types.StringValue(fileData["ModTime"]),
|
||||
IsDirectory: types.StringValue(fileData["IsDir"]),
|
||||
}
|
||||
config.Files = append(config.Files, fileInfo)
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
|
||||
tflog.Debug(ctx, fmt.Sprintf("Response Object: %#v", *resp))
|
||||
}
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package file_local_directory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
c "github.com/rancher/terraform-provider-file/internal/provider/directory_client"
|
||||
)
|
||||
|
||||
const (
|
||||
// echo -n '/tmp/foo' | sha256sum | awk '{print $1}' #.
|
||||
testDirectoryId = "e2e1dcd28fea64180e4cd859b299ce67c4c02a3cbd49eca0042f7b5b47d241b5"
|
||||
testDirectoryPath = "/tmp/foo"
|
||||
defaultDirectoryPerm = "0755"
|
||||
)
|
||||
|
||||
func TestLocalDirectoryDataSourceMetadata(t *testing.T) {
|
||||
t.Run("Metadata function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryDataSource
|
||||
want datasource.MetadataResponse
|
||||
}{
|
||||
{"Basic test", LocalDirectoryDataSource{}, datasource.MetadataResponse{TypeName: "file_local_directory"}},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res := datasource.MetadataResponse{}
|
||||
tc.fit.Metadata(context.Background(), datasource.MetadataRequest{ProviderTypeName: "file"}, &res)
|
||||
got := res
|
||||
if got != tc.want {
|
||||
t.Errorf("%#v.Metadata() is %v; want %v", tc.fit, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalDirectoryDataSourceRead(t *testing.T) {
|
||||
t.Run("Read function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryDataSource
|
||||
have datasource.ReadRequest
|
||||
want datasource.ReadResponse
|
||||
setup map[string]interface{}
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
LocalDirectoryDataSource{client: &c.MemoryDirectoryClient{}},
|
||||
// have
|
||||
getReadDataSourceRequest(t, map[string]interface{}{
|
||||
"path": testDirectoryPath,
|
||||
}),
|
||||
// want
|
||||
getReadDataSourceResponse(t, map[string]interface{}{
|
||||
"id": testDirectoryId,
|
||||
"path": testDirectoryPath,
|
||||
"permissions": defaultDirectoryPerm,
|
||||
"files": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": filepath.Join(testDirectoryPath, "test_file_a"),
|
||||
"size": "10",
|
||||
"permissions": "0700",
|
||||
"last_modified": "2025-09-29 16:09:15.039952008 +0000 UTC",
|
||||
"is_directory": "false",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": filepath.Join(testDirectoryPath, "test_file_b"),
|
||||
"size": "100",
|
||||
"permissions": "0400",
|
||||
"last_modified": "2021-02-18 00:56:32 +0000 UTC",
|
||||
"is_directory": "false",
|
||||
},
|
||||
},
|
||||
}),
|
||||
// setup
|
||||
map[string]interface{}{
|
||||
"path": testDirectoryPath,
|
||||
"permissions": defaultDirectoryPerm,
|
||||
"files": []map[string]string{
|
||||
{
|
||||
"name": "test_file_a",
|
||||
"size": "10",
|
||||
"permissions": "0700",
|
||||
"lastModified": "2025-09-29 16:09:15.039952008 +0000 UTC",
|
||||
},
|
||||
{
|
||||
"name": "test_file_b",
|
||||
"size": "100",
|
||||
"permissions": "0400",
|
||||
"lastModified": "2021-02-18 00:56:32 +0000 UTC",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
path, ok := tc.setup["path"].(string)
|
||||
if !ok {
|
||||
t.Fatalf("path is not a string")
|
||||
}
|
||||
permissions, ok := tc.setup["permissions"].(string)
|
||||
if !ok {
|
||||
t.Fatalf("permissions is not a string")
|
||||
}
|
||||
files, ok := tc.setup["files"].([]map[string]string)
|
||||
if !ok {
|
||||
t.Fatalf("files is not a []map[string]string")
|
||||
}
|
||||
created, err := tc.fit.client.Create(path, permissions)
|
||||
if err != nil {
|
||||
t.Errorf("Error setting up: %v", err)
|
||||
return
|
||||
}
|
||||
characterSet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
rand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for _, file := range files {
|
||||
size, err := strconv.Atoi(file["size"])
|
||||
if err != nil {
|
||||
t.Errorf("could not convert size '%s' to an integer: %v", file["size"], err)
|
||||
return
|
||||
}
|
||||
b := make([]byte, size)
|
||||
for i := range b {
|
||||
b[i] = characterSet[rand.Intn(len(characterSet))]
|
||||
}
|
||||
contents := string(b)
|
||||
err = tc.fit.client.CreateFile(filepath.Join(path, file["name"]), contents, file["permissions"], file["lastModified"])
|
||||
if err != nil {
|
||||
t.Errorf("Error setting up: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if err := tc.fit.client.Delete(created); err != nil {
|
||||
t.Errorf("Error tearing down: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
r := getReadDataSourceResponseContainer()
|
||||
tc.fit.Read(context.Background(), tc.have, &r)
|
||||
got := r
|
||||
val := map[string]tftypes.Value{}
|
||||
err = tc.want.State.Raw.As(&val)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting tc.want.State.Raw to map: %v", err)
|
||||
return
|
||||
}
|
||||
rawWantFiles := val["files"]
|
||||
|
||||
err = got.State.Raw.As(&val)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting got.State.Raw to map: %v", err)
|
||||
return
|
||||
}
|
||||
rawGotFiles := val["files"]
|
||||
|
||||
if diff := cmp.Diff(rawWantFiles, rawGotFiles); diff != "" {
|
||||
t.Errorf("Read() mismatch (-want +got):\n%s", diff)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
t.Errorf("Read() mismatch (-want +got):\n%s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//* Helpers *//
|
||||
|
||||
func getReadDataSourceRequest(t *testing.T, data map[string]interface{}) datasource.ReadRequest {
|
||||
objType := getDataObjectAttributeTypes()
|
||||
val := buildValue(t, objType, data)
|
||||
|
||||
return datasource.ReadRequest{
|
||||
Config: tfsdk.Config{
|
||||
Raw: val,
|
||||
Schema: getLocalDirectoryDataSourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getReadDataSourceResponseContainer() datasource.ReadResponse {
|
||||
return datasource.ReadResponse{
|
||||
State: tfsdk.State{Schema: getLocalDirectoryDataSourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
|
||||
func getReadDataSourceResponse(t *testing.T, data map[string]interface{}) datasource.ReadResponse {
|
||||
objType := getDataObjectAttributeTypes()
|
||||
val := buildValue(t, objType, data)
|
||||
|
||||
return datasource.ReadResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: val,
|
||||
Schema: getLocalDirectoryDataSourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//* Helper's Helpers *//
|
||||
|
||||
func buildValue(t *testing.T, tfType tftypes.Type, data interface{}) tftypes.Value {
|
||||
if data == nil {
|
||||
return tftypes.NewValue(tfType, nil)
|
||||
}
|
||||
|
||||
switch typ := tfType.(type) {
|
||||
case tftypes.Object:
|
||||
dataMap, ok := data.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected map[string]interface{} for tftypes.Object, got %T", data)
|
||||
}
|
||||
attrValues := make(map[string]tftypes.Value)
|
||||
for name, attrType := range typ.AttributeTypes {
|
||||
attrValues[name] = buildValue(t, attrType, dataMap[name])
|
||||
}
|
||||
return tftypes.NewValue(typ, attrValues)
|
||||
|
||||
case tftypes.List:
|
||||
dataSlice, ok := data.([]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected []interface{} for tftypes.List, got %T", data)
|
||||
}
|
||||
elemValues := make([]tftypes.Value, 0, len(dataSlice))
|
||||
for _, v := range dataSlice {
|
||||
elemValues = append(elemValues, buildValue(t, typ.ElementType, v))
|
||||
}
|
||||
return tftypes.NewValue(typ, elemValues)
|
||||
|
||||
default:
|
||||
// Handle primitive types
|
||||
if tfType.Is(tftypes.String) {
|
||||
val, ok := data.(string)
|
||||
if !ok {
|
||||
t.Fatalf("Expected string for tftypes.String, got %T", data)
|
||||
}
|
||||
return tftypes.NewValue(tfType, val)
|
||||
}
|
||||
if tfType.Is(tftypes.Number) {
|
||||
var numVal interface{}
|
||||
switch v := data.(type) {
|
||||
case int:
|
||||
numVal = v
|
||||
case float64:
|
||||
numVal = v
|
||||
default:
|
||||
t.Fatalf("Expected int or float64 for tftypes.Number, got %T", data)
|
||||
}
|
||||
return tftypes.NewValue(tfType, numVal)
|
||||
}
|
||||
if tfType.Is(tftypes.Bool) {
|
||||
val, ok := data.(bool)
|
||||
if !ok {
|
||||
t.Fatalf("Expected bool for tftypes.Bool, got %T", data)
|
||||
}
|
||||
return tftypes.NewValue(tfType, val)
|
||||
}
|
||||
|
||||
t.Fatalf("Unsupported tftype: %T", tfType)
|
||||
return tftypes.Value{}
|
||||
}
|
||||
}
|
||||
|
||||
func getDataObjectAttributeTypes() tftypes.Object {
|
||||
return tftypes.Object{
|
||||
AttributeTypes: map[string]tftypes.Type{
|
||||
"id": tftypes.String,
|
||||
"path": tftypes.String,
|
||||
"permissions": tftypes.String,
|
||||
"files": tftypes.List{
|
||||
ElementType: tftypes.Object{
|
||||
AttributeTypes: map[string]tftypes.Type{
|
||||
"name": tftypes.String,
|
||||
"size": tftypes.String,
|
||||
"permissions": tftypes.String,
|
||||
"last_modified": tftypes.String,
|
||||
"is_directory": tftypes.String,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getLocalDirectoryDataSourceSchema() *datasource.SchemaResponse {
|
||||
var testResource LocalDirectoryDataSource
|
||||
r := &datasource.SchemaResponse{}
|
||||
testResource.Schema(context.Background(), datasource.SchemaRequest{}, r)
|
||||
return r
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package file_local_directory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
c "github.com/rancher/terraform-provider-file/internal/provider/directory_client"
|
||||
)
|
||||
|
||||
// The `var _` is a special Go construct that results in an unusable variable.
|
||||
// The purpose of these lines is to make sure our LocalDirectoryFileResource correctly implements the `resource.Resource“ interface.
|
||||
// These will fail at compilation time if the implementation is not satisfied.
|
||||
var _ resource.Resource = &LocalDirectoryResource{}
|
||||
var _ resource.ResourceWithImportState = &LocalDirectoryResource{}
|
||||
|
||||
func NewLocalDirectoryResource() resource.Resource {
|
||||
return &LocalDirectoryResource{}
|
||||
}
|
||||
|
||||
type LocalDirectoryResource struct {
|
||||
client c.DirectoryClient
|
||||
}
|
||||
|
||||
// LocalDirectoryResourceModel describes the resource data model.
|
||||
type LocalDirectoryResourceModel struct {
|
||||
Id types.String `tfsdk:"id"`
|
||||
Path types.String `tfsdk:"path"`
|
||||
Permissions types.String `tfsdk:"permissions"`
|
||||
Created types.String `tfsdk:"created"`
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_local_directory" // file_local_directory resource
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: "Local Directory resource.",
|
||||
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"path": schema.StringAttribute{
|
||||
MarkdownDescription: "Directory path, required. All subdirectories will also be created. Changing this forces recreate.",
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"permissions": schema.StringAttribute{
|
||||
MarkdownDescription: "The directory permissions to assign to the directory, defaults to '0700'. " +
|
||||
"In order to automatically create subdirectories the owner must have execute access, " +
|
||||
"ie. '0600' or less prevents the provider from creating subdirectories.",
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Default: stringdefault.StaticString("0700"),
|
||||
},
|
||||
"id": schema.StringAttribute{
|
||||
MarkdownDescription: "Identifier derived from sha256 hash of path. ",
|
||||
Computed: true,
|
||||
},
|
||||
"created": schema.StringAttribute{
|
||||
MarkdownDescription: "The top level directory created. " +
|
||||
"eg. if 'path' = '/path/to/new/directory' and '/path/to' already exists, " +
|
||||
"but the rest doesn't, then 'created' will be '/path/to/new'. " +
|
||||
"This path will be recursively removed during destroy and recreate actions.",
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the provider for the resource if necessary.
|
||||
func (r *LocalDirectoryResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// We should:
|
||||
// - generate reality and state in the Create function
|
||||
// - update state to match reality in the Read function
|
||||
// - update state to config and update reality to config in the Update function by looking for differences in the state and the config (trust read to collect reality)
|
||||
// - destroy reality and state in the Destroy function
|
||||
|
||||
func (r *LocalDirectoryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Request Object: %#v", req))
|
||||
var err error
|
||||
|
||||
if r.client == nil {
|
||||
tflog.Debug(ctx, "Configuring client with default OsDirectoryClient.")
|
||||
r.client = &c.OsDirectoryClient{}
|
||||
}
|
||||
|
||||
var plan LocalDirectoryResourceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
path := plan.Path.ValueString()
|
||||
permString := plan.Permissions.ValueString()
|
||||
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(path))
|
||||
id := hex.EncodeToString(hasher.Sum(nil))
|
||||
plan.Id = types.StringValue(id)
|
||||
|
||||
cutPath, err := r.client.Create(path, permString)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error creating file: ", err.Error())
|
||||
return
|
||||
}
|
||||
plan.Created = types.StringValue(cutPath)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
tflog.Debug(ctx, fmt.Sprintf("Response Object: %#v", *resp))
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Request Object: %#v", req))
|
||||
|
||||
if r.client == nil {
|
||||
tflog.Debug(ctx, "Configuring client with default OsDirectoryClient.")
|
||||
r.client = &c.OsDirectoryClient{}
|
||||
}
|
||||
|
||||
var state LocalDirectoryResourceModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
sPath := state.Path.ValueString()
|
||||
sPerm := state.Permissions.ValueString()
|
||||
|
||||
perm, data, err := r.client.Read(sPath)
|
||||
if err != nil && err.Error() == "directory not found" {
|
||||
// force recreate if directory not found
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("Read data: %#v", data))
|
||||
|
||||
if perm != sPerm {
|
||||
// update the state with the actual mode
|
||||
state.Permissions = types.StringValue(perm)
|
||||
}
|
||||
|
||||
// Only update permissions because id, path, and created should never change.
|
||||
// The directory resource manages a new directory, it is not meant to pull file information.
|
||||
// To retrieve file information in a directory, use the directory data source.
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
tflog.Debug(ctx, fmt.Sprintf("Response Object: %#v", *resp))
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Request Object: %#v", req))
|
||||
|
||||
if r.client == nil {
|
||||
tflog.Debug(ctx, "Configuring client with default OsDirectoryClient.")
|
||||
r.client = &c.OsDirectoryClient{}
|
||||
}
|
||||
|
||||
// Plan represents what is in the config, so plan = config
|
||||
var config LocalDirectoryResourceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &config)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
cPath := config.Path.ValueString()
|
||||
cPerm := config.Permissions.ValueString()
|
||||
|
||||
// Read updates state with reality, so state = reality
|
||||
var reality LocalDirectoryResourceModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &reality)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
rPerm := reality.Permissions.ValueString()
|
||||
|
||||
if cPerm != rPerm {
|
||||
// Only update permissions because id, path, and created should never change.
|
||||
err := r.client.Update(cPath, cPerm)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error updating directory permissions: ", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
|
||||
tflog.Debug(ctx, fmt.Sprintf("Response Object: %#v", *resp))
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Request Object: %#v", req))
|
||||
|
||||
// Allow the ability to inject a file client, but use the OsDirectoryClient by default.
|
||||
if r.client == nil {
|
||||
tflog.Debug(ctx, "Configuring client with default OsDirectoryClient.")
|
||||
r.client = &c.OsDirectoryClient{}
|
||||
}
|
||||
|
||||
var state LocalDirectoryResourceModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
sCutPath := state.Created.ValueString()
|
||||
|
||||
if err := r.client.Delete(sCutPath); err != nil {
|
||||
resp.Diagnostics.AddError("Failed to delete directory: ", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Response Object: %#v", *resp))
|
||||
}
|
||||
|
||||
func (r *LocalDirectoryResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
|
||||
}
|
||||
|
|
@ -0,0 +1,618 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package file_local_directory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
c "github.com/rancher/terraform-provider-file/internal/provider/directory_client"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultId = ""
|
||||
defaultPerm = "0700"
|
||||
defaultCreated = ""
|
||||
testPath = "path/to/new/directory"
|
||||
// echo -n "path/to/new/directory" | sha256sum | awk '{print $1}' #.
|
||||
testId = "2d020a0327fe0a114bf587a2b24894d67654203b0bd4428546ad5bf4ed7ed6a7"
|
||||
testCreated = "path"
|
||||
)
|
||||
|
||||
var booleanFields = []string{"fake"}
|
||||
|
||||
func TestLocalDirectoryResourceMetadata(t *testing.T) {
|
||||
t.Run("Metadata function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryResource
|
||||
want resource.MetadataResponse
|
||||
}{
|
||||
{"Basic test", LocalDirectoryResource{}, resource.MetadataResponse{TypeName: "file_local_directory"}},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res := resource.MetadataResponse{}
|
||||
tc.fit.Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "file"}, &res)
|
||||
got := res
|
||||
if got != tc.want {
|
||||
t.Errorf("%#v.Metadata() is %v; want %v", tc.fit, got, tc.want)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalDirectorySchema(t *testing.T) {
|
||||
t.Run("Schema function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryResource
|
||||
want resource.SchemaResponse
|
||||
}{
|
||||
{"Basic test", LocalDirectoryResource{}, *getLocalDirectoryResourceSchema()},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := resource.SchemaResponse{}
|
||||
tc.fit.Schema(context.Background(), resource.SchemaRequest{}, &r)
|
||||
got := r
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
t.Errorf("Schema() mismatch (-want +got):\n%s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalDirectoryResourceCreate(t *testing.T) {
|
||||
t.Run("Create function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryResource
|
||||
have resource.CreateRequest
|
||||
want resource.CreateResponse
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
LocalDirectoryResource{client: &c.MemoryDirectoryClient{}},
|
||||
// have
|
||||
getCreateRequest(t, map[string]string{
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
"id": defaultId,
|
||||
"created": defaultCreated,
|
||||
}),
|
||||
// want
|
||||
getCreateResponse(t, map[string]string{
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
"id": testId,
|
||||
"created": testCreated,
|
||||
}),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var plannedState LocalDirectoryResourceModel
|
||||
if diags := tc.have.Plan.Get(context.Background(), &plannedState); diags.HasError() {
|
||||
t.Errorf("Failed to get planned state: %v", diags)
|
||||
return
|
||||
}
|
||||
plannedPath := plannedState.Path.ValueString()
|
||||
r := getCreateResponseContainer()
|
||||
tc.fit.Create(context.Background(), tc.have, &r)
|
||||
defer func() {
|
||||
if err := tc.fit.client.Delete(plannedPath); err != nil {
|
||||
t.Errorf("Error cleaning up: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
got := r
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
t.Errorf("Create() mismatch (-want +got):\n%s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalDirectoryResourceRead(t *testing.T) {
|
||||
t.Run("Read function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryResource
|
||||
have resource.ReadRequest
|
||||
want resource.ReadResponse
|
||||
setup map[string]string
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
LocalDirectoryResource{client: &c.MemoryDirectoryClient{}},
|
||||
// have
|
||||
getReadRequest(t, map[string]string{
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"created": testCreated,
|
||||
"permissions": defaultPerm,
|
||||
}),
|
||||
// want
|
||||
getReadResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"created": testCreated,
|
||||
"permissions": defaultPerm,
|
||||
}),
|
||||
// setup
|
||||
map[string]string{
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
},
|
||||
},
|
||||
{
|
||||
"Updates permission",
|
||||
LocalDirectoryResource{client: &c.MemoryDirectoryClient{}},
|
||||
// have
|
||||
getReadRequest(t, map[string]string{
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"created": testCreated,
|
||||
"permissions": defaultPerm,
|
||||
}),
|
||||
// want
|
||||
getReadResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"created": testCreated,
|
||||
"permissions": "0777",
|
||||
}),
|
||||
// setup
|
||||
map[string]string{
|
||||
"path": testPath,
|
||||
"permissions": "0777",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
created, err := tc.fit.client.Create(tc.setup["path"], tc.setup["permissions"])
|
||||
if err != nil {
|
||||
t.Errorf("Error setting up: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := tc.fit.client.Delete(created); err != nil {
|
||||
t.Errorf("Error tearing down: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
r := getReadResponseContainer()
|
||||
tc.fit.Read(context.Background(), tc.have, &r)
|
||||
got := r
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
t.Errorf("Read() mismatch (-want +got):\n%s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
func TestLocalDirectoryResourceUpdate(t *testing.T) {
|
||||
t.Run("Update function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryResource
|
||||
have resource.UpdateRequest
|
||||
want resource.UpdateResponse
|
||||
setup map[string]string
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
LocalDirectoryResource{client: &c.MemoryDirectoryClient{}},
|
||||
// have
|
||||
getUpdateRequest(t, map[string]map[string]string{
|
||||
"priorState": {
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
"created": testCreated,
|
||||
},
|
||||
"plan": {
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
"created": testCreated,
|
||||
},
|
||||
}),
|
||||
// want
|
||||
getUpdateResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
"created": testCreated,
|
||||
}),
|
||||
// setup
|
||||
map[string]string{
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
},
|
||||
},
|
||||
{
|
||||
"Updates permissions",
|
||||
LocalDirectoryResource{client: &c.MemoryDirectoryClient{}},
|
||||
// have
|
||||
getUpdateRequest(t, map[string]map[string]string{
|
||||
"priorState": {
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
"created": testCreated,
|
||||
},
|
||||
"plan": {
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"permissions": "0755",
|
||||
"created": testCreated,
|
||||
},
|
||||
}),
|
||||
// want
|
||||
getUpdateResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"permissions": "0755",
|
||||
"created": testCreated,
|
||||
}),
|
||||
// setup
|
||||
map[string]string{
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
created, err := tc.fit.client.Create(tc.setup["path"], tc.setup["permissions"])
|
||||
if err != nil {
|
||||
t.Errorf("Error setting up: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := tc.fit.client.Delete(created); err != nil {
|
||||
t.Errorf("Error tearing down: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
r := getUpdateResponseContainer()
|
||||
tc.fit.Update(context.Background(), tc.have, &r)
|
||||
got := r
|
||||
var plannedState LocalDirectoryResourceModel
|
||||
if diags := tc.have.Plan.Get(context.Background(), &plannedState); diags.HasError() {
|
||||
t.Errorf("Failed to get planned state: %v", diags)
|
||||
return
|
||||
}
|
||||
plannedPath := plannedState.Path.ValueString()
|
||||
plannedPermissions := plannedState.Permissions.ValueString()
|
||||
permissionsAfterUpdate, _, err := tc.fit.client.Read(plannedPath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read directory for update verification: %s", err)
|
||||
return
|
||||
}
|
||||
if permissionsAfterUpdate != plannedPermissions {
|
||||
t.Errorf("Directory permissions were not updated correctly. Got %q, want %q", permissionsAfterUpdate, plannedPermissions)
|
||||
return
|
||||
}
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
t.Errorf("Update() mismatch (-want +got):\n%s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalDirectoryResourceDelete(t *testing.T) {
|
||||
t.Run("Delete function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit LocalDirectoryResource
|
||||
have resource.DeleteRequest
|
||||
want resource.DeleteResponse
|
||||
setup map[string]string
|
||||
}{
|
||||
{
|
||||
"Basic test",
|
||||
LocalDirectoryResource{client: &c.MemoryDirectoryClient{}},
|
||||
// have
|
||||
getDeleteRequest(t, map[string]string{
|
||||
"id": testId,
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
"created": testCreated,
|
||||
}),
|
||||
// want
|
||||
getDeleteResponse(),
|
||||
// setup
|
||||
map[string]string{
|
||||
"path": testPath,
|
||||
"permissions": defaultPerm,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := tc.fit.client.Create(tc.setup["path"], tc.setup["permissions"])
|
||||
if err != nil {
|
||||
t.Errorf("Error setting up: %v", err)
|
||||
return
|
||||
}
|
||||
r := getDeleteResponseContainer()
|
||||
tc.fit.Delete(context.Background(), tc.have, &r)
|
||||
got := r
|
||||
// Verify the directory was actually deleted
|
||||
if _, _, err := tc.fit.client.Read(tc.setup["path"]); err == nil || err.Error() != "directory not found" {
|
||||
if err == nil {
|
||||
t.Errorf("Expected directory to be deleted, but it still exists.")
|
||||
return
|
||||
}
|
||||
t.Errorf("Expected directory to be deleted, but it still exists. Error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
// verify that the directory was removed from state
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
t.Errorf("Update() mismatch (-want +got):\n%s", diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// *** Test Helper Functions *** //
|
||||
|
||||
func getCreateRequest(t *testing.T, data map[string]string) resource.CreateRequest {
|
||||
planMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
if value == "" {
|
||||
planMap[key] = tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue)
|
||||
} else {
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.CreateRequest{}
|
||||
}
|
||||
planMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
}
|
||||
} else {
|
||||
if value == "" {
|
||||
planMap[key] = tftypes.NewValue(tftypes.String, tftypes.UnknownValue)
|
||||
} else {
|
||||
planMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
planValue := tftypes.NewValue(getObjectAttributeTypes(), planMap)
|
||||
return resource.CreateRequest{
|
||||
Plan: tfsdk.Plan{
|
||||
Raw: planValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
func getCreateResponseContainer() resource.CreateResponse {
|
||||
return resource.CreateResponse{
|
||||
State: tfsdk.State{Schema: getLocalDirectoryResourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
func getCreateResponse(t *testing.T, data map[string]string) resource.CreateResponse {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.CreateResponse{}
|
||||
}
|
||||
stateMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
} else {
|
||||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getObjectAttributeTypes(), stateMap)
|
||||
return resource.CreateResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getReadRequest(t *testing.T, data map[string]string) resource.ReadRequest {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.ReadRequest{}
|
||||
}
|
||||
stateMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
} else {
|
||||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getObjectAttributeTypes(), stateMap)
|
||||
return resource.ReadRequest{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
func getReadResponseContainer() resource.ReadResponse {
|
||||
return resource.ReadResponse{
|
||||
State: tfsdk.State{Schema: getLocalDirectoryResourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
func getReadResponse(t *testing.T, data map[string]string) resource.ReadResponse {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.ReadResponse{}
|
||||
}
|
||||
stateMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
} else {
|
||||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getObjectAttributeTypes(), stateMap)
|
||||
return resource.ReadResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getUpdateRequest(t *testing.T, data map[string]map[string]string) resource.UpdateRequest {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data["priorState"] {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.UpdateRequest{}
|
||||
}
|
||||
stateMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
} else {
|
||||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
priorStateValue := tftypes.NewValue(getObjectAttributeTypes(), stateMap)
|
||||
|
||||
planMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data["plan"] {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
if value == "" {
|
||||
planMap[key] = tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue)
|
||||
} else {
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.UpdateRequest{}
|
||||
}
|
||||
planMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
}
|
||||
} else {
|
||||
if value == "" {
|
||||
planMap[key] = tftypes.NewValue(tftypes.String, tftypes.UnknownValue)
|
||||
} else {
|
||||
planMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
planValue := tftypes.NewValue(getObjectAttributeTypes(), planMap)
|
||||
|
||||
return resource.UpdateRequest{
|
||||
State: tfsdk.State{
|
||||
Raw: priorStateValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
Plan: tfsdk.Plan{
|
||||
Raw: planValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
func getUpdateResponseContainer() resource.UpdateResponse {
|
||||
return resource.UpdateResponse{
|
||||
State: tfsdk.State{Schema: getLocalDirectoryResourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
func getUpdateResponse(t *testing.T, data map[string]string) resource.UpdateResponse {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.UpdateResponse{}
|
||||
}
|
||||
stateMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
} else {
|
||||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getObjectAttributeTypes(), stateMap)
|
||||
return resource.UpdateResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getDeleteRequest(t *testing.T, data map[string]string) resource.DeleteRequest {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(booleanFields, key) { // booleanFields is a constant
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
t.Errorf("Error converting %s to bool %s: ", value, err.Error())
|
||||
return resource.DeleteRequest{}
|
||||
}
|
||||
stateMap[key] = tftypes.NewValue(tftypes.Bool, v)
|
||||
} else {
|
||||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getObjectAttributeTypes(), stateMap)
|
||||
return resource.DeleteRequest{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getLocalDirectoryResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
func getDeleteResponseContainer() resource.DeleteResponse {
|
||||
// A delete response does not need a schema as it results in a null state.
|
||||
return resource.DeleteResponse{}
|
||||
}
|
||||
func getDeleteResponse() resource.DeleteResponse {
|
||||
return resource.DeleteResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: tftypes.Value{},
|
||||
Schema: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getObjectAttributeTypes() tftypes.Object {
|
||||
return tftypes.Object{
|
||||
AttributeTypes: map[string]tftypes.Type{
|
||||
"path": tftypes.String,
|
||||
"permissions": tftypes.String,
|
||||
"created": tftypes.String,
|
||||
"id": tftypes.String,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getLocalDirectoryResourceSchema() *resource.SchemaResponse {
|
||||
var testResource LocalDirectoryResource
|
||||
r := &resource.SchemaResponse{}
|
||||
testResource.Schema(context.Background(), resource.SchemaRequest{}, r)
|
||||
return r
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package file_snapshot
|
||||
package file_local_snapshot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -19,29 +19,29 @@ import (
|
|||
// The `var _` is a special Go construct that results in an unusable variable.
|
||||
// The purpose of these lines is to make sure our LocalFileDataSource correctly implements the `datasource.DataSource“ interface.
|
||||
// These will fail at compilation time if the implementation is not satisfied.
|
||||
var _ datasource.DataSource = &SnapshotDataSource{}
|
||||
var _ datasource.DataSource = &LocalSnapshotDataSource{}
|
||||
|
||||
func NewSnapshotDataSource() datasource.DataSource {
|
||||
return &SnapshotDataSource{}
|
||||
func NewLocalSnapshotDataSource() datasource.DataSource {
|
||||
return &LocalSnapshotDataSource{}
|
||||
}
|
||||
|
||||
type SnapshotDataSource struct{}
|
||||
type LocalSnapshotDataSource struct{}
|
||||
|
||||
type SnapshotDataSourceModel struct {
|
||||
type LocalSnapshotDataSourceModel struct {
|
||||
Id types.String `tfsdk:"id"`
|
||||
Contents types.String `tfsdk:"contents"`
|
||||
Data types.String `tfsdk:"data"`
|
||||
Decompress types.Bool `tfsdk:"decompress"`
|
||||
}
|
||||
|
||||
func (r *SnapshotDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_snapshot" // file_snapshot
|
||||
func (r *LocalSnapshotDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_local_snapshot" // _local_snapshot
|
||||
}
|
||||
|
||||
func (r *SnapshotDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
func (r *LocalSnapshotDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: "File Snapshot data source. \n" +
|
||||
"This data source retrieves the contents of a file from the output of a file_snapshot datasource." +
|
||||
MarkdownDescription: "File LocalSnapshot data source. \n" +
|
||||
"This data source retrieves the contents of a file from the output of a file_local_snapshot datasource." +
|
||||
"Warning! Using this resource places the plain text contents of the snapshot in your state file.",
|
||||
|
||||
Attributes: map[string]schema.Attribute{
|
||||
|
|
@ -74,7 +74,7 @@ func (r *SnapshotDataSource) Schema(ctx context.Context, req datasource.SchemaRe
|
|||
}
|
||||
}
|
||||
|
||||
func (r *SnapshotDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
func (r *LocalSnapshotDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
// This only configures the provider, so anything here must be available in the provider package to configure.
|
||||
// If you want to configure a client, do that in the Create/Read/Update/Delete functions.
|
||||
|
|
@ -83,10 +83,10 @@ func (r *SnapshotDataSource) Configure(ctx context.Context, req datasource.Confi
|
|||
}
|
||||
}
|
||||
|
||||
func (r *SnapshotDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
func (r *LocalSnapshotDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Read Request Object: %+v", req))
|
||||
|
||||
var config SnapshotDataSourceModel
|
||||
var config LocalSnapshotDataSourceModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package file_snapshot
|
||||
package file_local_snapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -30,14 +30,14 @@ const (
|
|||
|
||||
var snapshotDataSourceBooleanFields = []string{"decompress"}
|
||||
|
||||
func TestSnapshotDataSourceMetadata(t *testing.T) {
|
||||
func TestLocalSnapshotDataSourceMetadata(t *testing.T) {
|
||||
t.Run("Metadata function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotDataSource
|
||||
fit LocalSnapshotDataSource
|
||||
want datasource.MetadataResponse
|
||||
}{
|
||||
{"Basic test", SnapshotDataSource{}, datasource.MetadataResponse{TypeName: "file_snapshot"}},
|
||||
{"Basic test", LocalSnapshotDataSource{}, datasource.MetadataResponse{TypeName: "file_local_snapshot"}},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
|
@ -52,14 +52,14 @@ func TestSnapshotDataSourceMetadata(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSnapshotDataSourceSchema(t *testing.T) {
|
||||
func TestLocalSnapshotDataSourceSchema(t *testing.T) {
|
||||
t.Run("Schema function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotDataSource
|
||||
fit LocalSnapshotDataSource
|
||||
want datasource.SchemaResponse
|
||||
}{
|
||||
{"Basic test", SnapshotDataSource{}, *getSnapshotDataSourceSchema()},
|
||||
{"Basic test", LocalSnapshotDataSource{}, *getLocalSnapshotDataSourceSchema()},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
|
@ -74,26 +74,26 @@ func TestSnapshotDataSourceSchema(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSnapshotDataSourceRead(t *testing.T) {
|
||||
func TestLocalSnapshotDataSourceRead(t *testing.T) {
|
||||
t.Run("Read function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotDataSource
|
||||
fit LocalSnapshotDataSource
|
||||
have datasource.ReadRequest
|
||||
want datasource.ReadResponse
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
SnapshotDataSource{},
|
||||
LocalSnapshotDataSource{},
|
||||
// have
|
||||
getSnapshotDataSourceReadRequest(t, map[string]string{
|
||||
getLocalSnapshotDataSourceReadRequest(t, map[string]string{
|
||||
"id": "", // id is computed.
|
||||
"data": "", // data is computed.
|
||||
"contents": testDataEncoded,
|
||||
"decompress": defaultDecompress,
|
||||
}),
|
||||
// want
|
||||
getSnapshotDataSourceReadResponse(t, map[string]string{
|
||||
getLocalSnapshotDataSourceReadResponse(t, map[string]string{
|
||||
"id": testDataEncodedId,
|
||||
"data": testDataContents,
|
||||
"contents": testDataEncoded,
|
||||
|
|
@ -102,16 +102,16 @@ func TestSnapshotDataSourceRead(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"Compressed",
|
||||
SnapshotDataSource{},
|
||||
LocalSnapshotDataSource{},
|
||||
// have
|
||||
getSnapshotDataSourceReadRequest(t, map[string]string{
|
||||
getLocalSnapshotDataSourceReadRequest(t, map[string]string{
|
||||
"id": "", // id is computed.
|
||||
"data": "", // data is computed.
|
||||
"contents": testDataCompressed,
|
||||
"decompress": "true",
|
||||
}),
|
||||
// want
|
||||
getSnapshotDataSourceReadResponse(t, map[string]string{
|
||||
getLocalSnapshotDataSourceReadResponse(t, map[string]string{
|
||||
"id": testDataCompressedId,
|
||||
"data": testDataContents,
|
||||
"contents": testDataCompressed,
|
||||
|
|
@ -121,7 +121,7 @@ func TestSnapshotDataSourceRead(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := getSnapshotDataSourceReadResponseContainer()
|
||||
r := getLocalSnapshotDataSourceReadResponseContainer()
|
||||
tc.fit.Read(context.Background(), tc.have, &r)
|
||||
got := r
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
|
|
@ -135,7 +135,7 @@ func TestSnapshotDataSourceRead(t *testing.T) {
|
|||
// *** Test Helper Functions *** //
|
||||
|
||||
// Read.
|
||||
func getSnapshotDataSourceReadRequest(t *testing.T, data map[string]string) datasource.ReadRequest {
|
||||
func getLocalSnapshotDataSourceReadRequest(t *testing.T, data map[string]string) datasource.ReadRequest {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotDataSourceBooleanFields, key) { // snapshotDataSourceBooleanFields is a constant
|
||||
|
|
@ -148,22 +148,22 @@ func getSnapshotDataSourceReadRequest(t *testing.T, data map[string]string) data
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getSnapshotDataSourceAttributeTypes(), stateMap)
|
||||
stateValue := tftypes.NewValue(getLocalSnapshotDataSourceAttributeTypes(), stateMap)
|
||||
return datasource.ReadRequest{
|
||||
Config: tfsdk.Config{
|
||||
Raw: stateValue,
|
||||
Schema: getSnapshotDataSourceSchema().Schema,
|
||||
Schema: getLocalSnapshotDataSourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotDataSourceReadResponseContainer() datasource.ReadResponse {
|
||||
func getLocalSnapshotDataSourceReadResponseContainer() datasource.ReadResponse {
|
||||
return datasource.ReadResponse{
|
||||
State: tfsdk.State{Schema: getSnapshotDataSourceSchema().Schema},
|
||||
State: tfsdk.State{Schema: getLocalSnapshotDataSourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotDataSourceReadResponse(t *testing.T, data map[string]string) datasource.ReadResponse {
|
||||
func getLocalSnapshotDataSourceReadResponse(t *testing.T, data map[string]string) datasource.ReadResponse {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotDataSourceBooleanFields, key) { // snapshotDataSourceBooleanFields is a constant
|
||||
|
|
@ -176,17 +176,17 @@ func getSnapshotDataSourceReadResponse(t *testing.T, data map[string]string) dat
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getSnapshotDataSourceAttributeTypes(), stateMap)
|
||||
stateValue := tftypes.NewValue(getLocalSnapshotDataSourceAttributeTypes(), stateMap)
|
||||
return datasource.ReadResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getSnapshotDataSourceSchema().Schema,
|
||||
Schema: getLocalSnapshotDataSourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// The helpers helpers.
|
||||
func getSnapshotDataSourceAttributeTypes() tftypes.Object {
|
||||
func getLocalSnapshotDataSourceAttributeTypes() tftypes.Object {
|
||||
return tftypes.Object{
|
||||
AttributeTypes: map[string]tftypes.Type{
|
||||
"id": tftypes.String,
|
||||
|
|
@ -197,8 +197,8 @@ func getSnapshotDataSourceAttributeTypes() tftypes.Object {
|
|||
}
|
||||
}
|
||||
|
||||
func getSnapshotDataSourceSchema() *datasource.SchemaResponse {
|
||||
var testDataSource SnapshotDataSource
|
||||
func getLocalSnapshotDataSourceSchema() *datasource.SchemaResponse {
|
||||
var testDataSource LocalSnapshotDataSource
|
||||
r := &datasource.SchemaResponse{}
|
||||
testDataSource.Schema(context.Background(), datasource.SchemaRequest{}, r)
|
||||
return r
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package file_snapshot
|
||||
package file_local_snapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -20,34 +20,34 @@ import (
|
|||
// The `var _` is a special Go construct that results in an unusable variable.
|
||||
// The purpose of these lines is to make sure our LocalFileResource correctly implements the `resource.Resource“ interface.
|
||||
// These will fail at compilation time if the implementation is not satisfied.
|
||||
var _ resource.Resource = &SnapshotResource{}
|
||||
var _ resource.ResourceWithImportState = &SnapshotResource{}
|
||||
var _ resource.Resource = &LocalSnapshotResource{}
|
||||
var _ resource.ResourceWithImportState = &LocalSnapshotResource{}
|
||||
|
||||
func NewSnapshotResource() resource.Resource {
|
||||
return &SnapshotResource{}
|
||||
func NewLocalSnapshotResource() resource.Resource {
|
||||
return &LocalSnapshotResource{}
|
||||
}
|
||||
|
||||
type SnapshotResource struct {
|
||||
type LocalSnapshotResource struct {
|
||||
client c.FileClient
|
||||
}
|
||||
|
||||
// SnapshotResourceModel describes the resource data model.
|
||||
type SnapshotResourceModel struct {
|
||||
// LocalSnapshotResourceModel describes the resource data model.
|
||||
type LocalSnapshotResourceModel struct {
|
||||
Id types.String `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Directory types.String `tfsdk:"directory"`
|
||||
Snapshot types.String `tfsdk:"snapshot"`
|
||||
LocalSnapshot types.String `tfsdk:"snapshot"`
|
||||
UpdateTrigger types.String `tfsdk:"update_trigger"`
|
||||
Compress types.Bool `tfsdk:"compress"`
|
||||
}
|
||||
|
||||
func (r *SnapshotResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_snapshot" // file_snapshot
|
||||
func (r *LocalSnapshotResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_local_snapshot" // file_local_snapshot
|
||||
}
|
||||
|
||||
func (r *SnapshotResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
func (r *LocalSnapshotResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: "File Snapshot resource. \n" +
|
||||
MarkdownDescription: "File LocalSnapshot resource. \n" +
|
||||
"This resource saves some content in state and doesn't update it until the trigger argument changes. " +
|
||||
"The refresh phase doesn't update state, instead " +
|
||||
"the state can only change on create or update and only when the update_trigger argument changes.",
|
||||
|
|
@ -100,7 +100,7 @@ func (r *SnapshotResource) Schema(ctx context.Context, req resource.SchemaReques
|
|||
}
|
||||
}
|
||||
|
||||
func (r *SnapshotResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
func (r *LocalSnapshotResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
// This only configures the provider, so anything here must be available in the provider package to configure.
|
||||
// If you want to configure a client, do that in the Create/Read/Update/Delete functions.
|
||||
|
|
@ -115,7 +115,7 @@ func (r *SnapshotResource) Configure(ctx context.Context, req resource.Configure
|
|||
// - update reality and state to match plan in the Update function (don't compare old state, just override)
|
||||
// - destroy reality in the Destroy function (state is handled automatically)
|
||||
|
||||
func (r *SnapshotResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
func (r *LocalSnapshotResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Create Request Object: %+v", req))
|
||||
|
||||
if r.client == nil {
|
||||
|
|
@ -123,7 +123,7 @@ func (r *SnapshotResource) Create(ctx context.Context, req resource.CreateReques
|
|||
r.client = &c.OsFileClient{}
|
||||
}
|
||||
|
||||
var plan SnapshotResourceModel
|
||||
var plan LocalSnapshotResourceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -152,7 +152,7 @@ func (r *SnapshotResource) Create(ctx context.Context, req resource.CreateReques
|
|||
resp.Diagnostics.AddError("Error reading encoded file: ", err.Error())
|
||||
return
|
||||
}
|
||||
plan.Snapshot = types.StringValue(encodedContents)
|
||||
plan.LocalSnapshot = types.StringValue(encodedContents)
|
||||
|
||||
hash, err := r.client.Hash(pDir, pName)
|
||||
if err != nil {
|
||||
|
|
@ -180,10 +180,10 @@ func (r *SnapshotResource) Create(ctx context.Context, req resource.CreateReques
|
|||
tflog.Debug(ctx, fmt.Sprintf("Create Response Object: %+v", *resp))
|
||||
}
|
||||
|
||||
func (r *SnapshotResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
func (r *LocalSnapshotResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Read Request Object: %+v", req))
|
||||
|
||||
var state SnapshotResourceModel
|
||||
var state LocalSnapshotResourceModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -198,7 +198,7 @@ func (r *SnapshotResource) Read(ctx context.Context, req resource.ReadRequest, r
|
|||
// a difference between the plan and the state has been found.
|
||||
// we want to update reality and state to match the plan.
|
||||
// our snapshot will only update if the update trigger has changed.
|
||||
func (r *SnapshotResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
func (r *LocalSnapshotResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Update Request Object: %+v", req))
|
||||
|
||||
if r.client == nil {
|
||||
|
|
@ -206,7 +206,7 @@ func (r *SnapshotResource) Update(ctx context.Context, req resource.UpdateReques
|
|||
r.client = &c.OsFileClient{}
|
||||
}
|
||||
|
||||
var plan SnapshotResourceModel
|
||||
var plan LocalSnapshotResourceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -216,14 +216,14 @@ func (r *SnapshotResource) Update(ctx context.Context, req resource.UpdateReques
|
|||
pDir := plan.Directory.ValueString()
|
||||
pUpdateTrigger := plan.UpdateTrigger.ValueString()
|
||||
|
||||
var state SnapshotResourceModel
|
||||
var state LocalSnapshotResourceModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
sUpdateTrigger := state.UpdateTrigger.ValueString()
|
||||
sSnapshot := state.Snapshot.ValueString()
|
||||
sLocalSnapshot := state.LocalSnapshot.ValueString()
|
||||
sCompress := state.Compress.ValueBool()
|
||||
sId := state.Id.ValueString()
|
||||
|
||||
|
|
@ -252,10 +252,10 @@ func (r *SnapshotResource) Update(ctx context.Context, req resource.UpdateReques
|
|||
resp.Diagnostics.AddError("Error reading encoded file: ", err.Error())
|
||||
return
|
||||
}
|
||||
plan.Snapshot = types.StringValue(encodedContents)
|
||||
plan.LocalSnapshot = types.StringValue(encodedContents)
|
||||
} else {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Update trigger hasn't changed, keeping previous snapshot (%s).", sSnapshot))
|
||||
plan.Snapshot = types.StringValue(sSnapshot)
|
||||
tflog.Debug(ctx, fmt.Sprintf("Update trigger hasn't changed, keeping previous snapshot (%s).", sLocalSnapshot))
|
||||
plan.LocalSnapshot = types.StringValue(sLocalSnapshot)
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, fmt.Sprintf("Setting state to this plan: \n%+v", &plan))
|
||||
|
|
@ -276,7 +276,7 @@ func (r *SnapshotResource) Update(ctx context.Context, req resource.UpdateReques
|
|||
tflog.Debug(ctx, fmt.Sprintf("Update Response Object: %+v", *resp))
|
||||
}
|
||||
|
||||
func (r *SnapshotResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
func (r *LocalSnapshotResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
tflog.Debug(ctx, fmt.Sprintf("Delete Request Object: %+v", req))
|
||||
|
||||
// delete is a no-op
|
||||
|
|
@ -284,6 +284,6 @@ func (r *SnapshotResource) Delete(ctx context.Context, req resource.DeleteReques
|
|||
tflog.Debug(ctx, fmt.Sprintf("Delete Response Object: %+v", *resp))
|
||||
}
|
||||
|
||||
func (r *SnapshotResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
func (r *LocalSnapshotResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package file_snapshot
|
||||
package file_local_snapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -34,14 +34,14 @@ const (
|
|||
|
||||
var snapshotResourceBooleanFields = []string{"compress"}
|
||||
|
||||
func TestSnapshotResourceMetadata(t *testing.T) {
|
||||
func TestLocalSnapshotResourceMetadata(t *testing.T) {
|
||||
t.Run("Metadata function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotResource
|
||||
fit LocalSnapshotResource
|
||||
want resource.MetadataResponse
|
||||
}{
|
||||
{"Basic test", SnapshotResource{}, resource.MetadataResponse{TypeName: "file_snapshot"}},
|
||||
{"Basic test", LocalSnapshotResource{}, resource.MetadataResponse{TypeName: "file_local_snapshot"}},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
|
@ -56,14 +56,14 @@ func TestSnapshotResourceMetadata(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSnapshotResourceSchema(t *testing.T) {
|
||||
func TestLocalSnapshotResourceSchema(t *testing.T) {
|
||||
t.Run("Schema function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotResource
|
||||
fit LocalSnapshotResource
|
||||
want resource.SchemaResponse
|
||||
}{
|
||||
{"Basic test", SnapshotResource{}, *getSnapshotResourceSchema()},
|
||||
{"Basic test", LocalSnapshotResource{}, *getLocalSnapshotResourceSchema()},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
|
@ -78,20 +78,20 @@ func TestSnapshotResourceSchema(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSnapshotResourceCreate(t *testing.T) {
|
||||
func TestLocalSnapshotResourceCreate(t *testing.T) {
|
||||
t.Run("Create function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotResource
|
||||
fit LocalSnapshotResource
|
||||
have resource.CreateRequest
|
||||
want resource.CreateResponse
|
||||
setup map[string]string
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
SnapshotResource{client: &c.MemoryFileClient{}},
|
||||
LocalSnapshotResource{client: &c.MemoryFileClient{}},
|
||||
// have
|
||||
getSnapshotResourceCreateRequest(t, map[string]string{
|
||||
getLocalSnapshotResourceCreateRequest(t, map[string]string{
|
||||
"id": "",
|
||||
"snapshot": "",
|
||||
"name": testName,
|
||||
|
|
@ -100,7 +100,7 @@ func TestSnapshotResourceCreate(t *testing.T) {
|
|||
"compress": defaultCompress,
|
||||
}),
|
||||
// want
|
||||
getSnapshotResourceCreateResponse(t, map[string]string{
|
||||
getLocalSnapshotResourceCreateResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
"name": testName,
|
||||
|
|
@ -118,7 +118,7 @@ func TestSnapshotResourceCreate(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := getSnapshotResourceCreateResponseContainer()
|
||||
r := getLocalSnapshotResourceCreateResponseContainer()
|
||||
if err := tc.fit.client.Create(tc.setup["directory"], tc.setup["name"], tc.setup["contents"], defaultPermissions); err != nil {
|
||||
t.Errorf("Error setting up: %v", err)
|
||||
}
|
||||
|
|
@ -137,19 +137,19 @@ func TestSnapshotResourceCreate(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
func TestSnapshotResourceRead(t *testing.T) {
|
||||
func TestLocalSnapshotResourceRead(t *testing.T) {
|
||||
t.Run("Read function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotResource
|
||||
fit LocalSnapshotResource
|
||||
have resource.ReadRequest
|
||||
want resource.ReadResponse
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
SnapshotResource{},
|
||||
LocalSnapshotResource{},
|
||||
// have
|
||||
getSnapshotResourceReadRequest(t, map[string]string{
|
||||
getLocalSnapshotResourceReadRequest(t, map[string]string{
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
"name": testName,
|
||||
|
|
@ -158,7 +158,7 @@ func TestSnapshotResourceRead(t *testing.T) {
|
|||
"compress": defaultCompress,
|
||||
}),
|
||||
// want
|
||||
getSnapshotResourceReadResponse(t, map[string]string{
|
||||
getLocalSnapshotResourceReadResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
"name": testName,
|
||||
|
|
@ -170,7 +170,7 @@ func TestSnapshotResourceRead(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := getSnapshotResourceReadResponseContainer()
|
||||
r := getLocalSnapshotResourceReadResponseContainer()
|
||||
tc.fit.Read(context.Background(), tc.have, &r)
|
||||
got := r
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
|
|
@ -181,20 +181,20 @@ func TestSnapshotResourceRead(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSnapshotResourceUpdate(t *testing.T) {
|
||||
func TestLocalSnapshotResourceUpdate(t *testing.T) {
|
||||
t.Run("Update function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotResource
|
||||
fit LocalSnapshotResource
|
||||
have resource.UpdateRequest
|
||||
want resource.UpdateResponse
|
||||
setup map[string]string
|
||||
}{
|
||||
{
|
||||
"Basic",
|
||||
SnapshotResource{client: &c.MemoryFileClient{}},
|
||||
LocalSnapshotResource{client: &c.MemoryFileClient{}},
|
||||
// have
|
||||
getSnapshotResourceUpdateRequest(t, map[string]map[string]string{
|
||||
getLocalSnapshotResourceUpdateRequest(t, map[string]map[string]string{
|
||||
"priorState": {
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
|
|
@ -213,7 +213,7 @@ func TestSnapshotResourceUpdate(t *testing.T) {
|
|||
},
|
||||
}),
|
||||
// want
|
||||
getSnapshotResourceUpdateResponse(t, map[string]string{
|
||||
getLocalSnapshotResourceUpdateResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
"name": testName,
|
||||
|
|
@ -230,9 +230,9 @@ func TestSnapshotResourceUpdate(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"Updates when trigger changes",
|
||||
SnapshotResource{client: &c.MemoryFileClient{}},
|
||||
LocalSnapshotResource{client: &c.MemoryFileClient{}},
|
||||
// have
|
||||
getSnapshotResourceUpdateRequest(t, map[string]map[string]string{
|
||||
getLocalSnapshotResourceUpdateRequest(t, map[string]map[string]string{
|
||||
"priorState": {
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
|
|
@ -251,7 +251,7 @@ func TestSnapshotResourceUpdate(t *testing.T) {
|
|||
},
|
||||
}),
|
||||
// want
|
||||
getSnapshotResourceUpdateResponse(t, map[string]string{
|
||||
getLocalSnapshotResourceUpdateResponse(t, map[string]string{
|
||||
"id": testId, // id shouldn't change
|
||||
"snapshot": "dGhlc2UgY29udGVudHMgYXJlIHVwZGF0ZWQgZm9yIHRlc3Rpbmc=", // echo -n "these contents are updated for testing" | base64 -w 0 #.
|
||||
"name": testName,
|
||||
|
|
@ -268,9 +268,9 @@ func TestSnapshotResourceUpdate(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"Doesn't update when trigger stays the same",
|
||||
SnapshotResource{client: &c.MemoryFileClient{}},
|
||||
LocalSnapshotResource{client: &c.MemoryFileClient{}},
|
||||
// have
|
||||
getSnapshotResourceUpdateRequest(t, map[string]map[string]string{
|
||||
getLocalSnapshotResourceUpdateRequest(t, map[string]map[string]string{
|
||||
"priorState": {
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
|
|
@ -289,7 +289,7 @@ func TestSnapshotResourceUpdate(t *testing.T) {
|
|||
},
|
||||
}),
|
||||
// want
|
||||
getSnapshotResourceUpdateResponse(t, map[string]string{
|
||||
getLocalSnapshotResourceUpdateResponse(t, map[string]string{
|
||||
"id": testId,
|
||||
"snapshot": testEncoded,
|
||||
"name": testName,
|
||||
|
|
@ -307,7 +307,7 @@ func TestSnapshotResourceUpdate(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := getSnapshotResourceUpdateResponseContainer()
|
||||
r := getLocalSnapshotResourceUpdateResponseContainer()
|
||||
if err := tc.fit.client.Create(tc.setup["directory"], tc.setup["name"], tc.setup["contents"], defaultPermissions); err != nil {
|
||||
t.Errorf("Error setting up: %v", err)
|
||||
}
|
||||
|
|
@ -327,19 +327,19 @@ func TestSnapshotResourceUpdate(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSnapshotResourceDelete(t *testing.T) {
|
||||
func TestLocalSnapshotResourceDelete(t *testing.T) {
|
||||
t.Run("Delete function", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fit SnapshotResource
|
||||
fit LocalSnapshotResource
|
||||
have resource.DeleteRequest
|
||||
want resource.DeleteResponse
|
||||
}{
|
||||
{
|
||||
"Basic test",
|
||||
SnapshotResource{client: &c.MemoryFileClient{}},
|
||||
LocalSnapshotResource{client: &c.MemoryFileClient{}},
|
||||
// have
|
||||
getSnapshotResourceDeleteRequest(t, map[string]string{
|
||||
getLocalSnapshotResourceDeleteRequest(t, map[string]string{
|
||||
"id": testId,
|
||||
"name": testName,
|
||||
"directory": defaultDirectory,
|
||||
|
|
@ -348,12 +348,12 @@ func TestSnapshotResourceDelete(t *testing.T) {
|
|||
"compress": defaultCompress,
|
||||
}),
|
||||
// want
|
||||
getSnapshotResourceDeleteResponse(),
|
||||
getLocalSnapshotResourceDeleteResponse(),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := getSnapshotResourceDeleteResponseContainer()
|
||||
r := getLocalSnapshotResourceDeleteResponseContainer()
|
||||
tc.fit.Delete(context.Background(), tc.have, &r)
|
||||
got := r
|
||||
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||
|
|
@ -366,7 +366,7 @@ func TestSnapshotResourceDelete(t *testing.T) {
|
|||
|
||||
// *** Test Helper Functions *** //
|
||||
// Create.
|
||||
func getSnapshotResourceCreateRequest(t *testing.T, data map[string]string) resource.CreateRequest {
|
||||
func getLocalSnapshotResourceCreateRequest(t *testing.T, data map[string]string) resource.CreateRequest {
|
||||
planMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotResourceBooleanFields, key) { // snapshotResourceBooleanFields is a constant
|
||||
|
|
@ -387,22 +387,22 @@ func getSnapshotResourceCreateRequest(t *testing.T, data map[string]string) reso
|
|||
}
|
||||
}
|
||||
}
|
||||
planValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), planMap)
|
||||
planValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), planMap)
|
||||
return resource.CreateRequest{
|
||||
Plan: tfsdk.Plan{
|
||||
Raw: planValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceCreateResponseContainer() resource.CreateResponse {
|
||||
func getLocalSnapshotResourceCreateResponseContainer() resource.CreateResponse {
|
||||
return resource.CreateResponse{
|
||||
State: tfsdk.State{Schema: getSnapshotResourceSchema().Schema},
|
||||
State: tfsdk.State{Schema: getLocalSnapshotResourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceCreateResponse(t *testing.T, data map[string]string) resource.CreateResponse {
|
||||
func getLocalSnapshotResourceCreateResponse(t *testing.T, data map[string]string) resource.CreateResponse {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotResourceBooleanFields, key) { // snapshotResourceBooleanFields is a constant
|
||||
|
|
@ -415,17 +415,17 @@ func getSnapshotResourceCreateResponse(t *testing.T, data map[string]string) res
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), stateMap)
|
||||
stateValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), stateMap)
|
||||
return resource.CreateResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Read.
|
||||
func getSnapshotResourceReadRequest(t *testing.T, data map[string]string) resource.ReadRequest {
|
||||
func getLocalSnapshotResourceReadRequest(t *testing.T, data map[string]string) resource.ReadRequest {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotResourceBooleanFields, key) { // snapshotResourceBooleanFields is a constant
|
||||
|
|
@ -438,22 +438,22 @@ func getSnapshotResourceReadRequest(t *testing.T, data map[string]string) resour
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), stateMap)
|
||||
stateValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), stateMap)
|
||||
return resource.ReadRequest{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceReadResponseContainer() resource.ReadResponse {
|
||||
func getLocalSnapshotResourceReadResponseContainer() resource.ReadResponse {
|
||||
return resource.ReadResponse{
|
||||
State: tfsdk.State{Schema: getSnapshotResourceSchema().Schema},
|
||||
State: tfsdk.State{Schema: getLocalSnapshotResourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceReadResponse(t *testing.T, data map[string]string) resource.ReadResponse {
|
||||
func getLocalSnapshotResourceReadResponse(t *testing.T, data map[string]string) resource.ReadResponse {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotResourceBooleanFields, key) { // snapshotResourceBooleanFields is a constant
|
||||
|
|
@ -466,17 +466,17 @@ func getSnapshotResourceReadResponse(t *testing.T, data map[string]string) resou
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), stateMap)
|
||||
stateValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), stateMap)
|
||||
return resource.ReadResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Update.
|
||||
func getSnapshotResourceUpdateRequest(t *testing.T, data map[string]map[string]string) resource.UpdateRequest {
|
||||
func getLocalSnapshotResourceUpdateRequest(t *testing.T, data map[string]map[string]string) resource.UpdateRequest {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data["priorState"] {
|
||||
if slices.Contains(snapshotResourceBooleanFields, key) { // snapshotResourceBooleanFields is a constant
|
||||
|
|
@ -489,7 +489,7 @@ func getSnapshotResourceUpdateRequest(t *testing.T, data map[string]map[string]s
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
priorStateValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), stateMap)
|
||||
priorStateValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), stateMap)
|
||||
|
||||
planMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data["plan"] {
|
||||
|
|
@ -511,27 +511,27 @@ func getSnapshotResourceUpdateRequest(t *testing.T, data map[string]map[string]s
|
|||
}
|
||||
}
|
||||
}
|
||||
planValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), planMap)
|
||||
planValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), planMap)
|
||||
|
||||
return resource.UpdateRequest{
|
||||
State: tfsdk.State{
|
||||
Raw: priorStateValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
Plan: tfsdk.Plan{
|
||||
Raw: planValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceUpdateResponseContainer() resource.UpdateResponse {
|
||||
func getLocalSnapshotResourceUpdateResponseContainer() resource.UpdateResponse {
|
||||
return resource.UpdateResponse{
|
||||
State: tfsdk.State{Schema: getSnapshotResourceSchema().Schema},
|
||||
State: tfsdk.State{Schema: getLocalSnapshotResourceSchema().Schema},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceUpdateResponse(t *testing.T, data map[string]string) resource.UpdateResponse {
|
||||
func getLocalSnapshotResourceUpdateResponse(t *testing.T, data map[string]string) resource.UpdateResponse {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotResourceBooleanFields, key) { // snapshotResourceBooleanFields is a constant
|
||||
|
|
@ -544,17 +544,17 @@ func getSnapshotResourceUpdateResponse(t *testing.T, data map[string]string) res
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), stateMap)
|
||||
stateValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), stateMap)
|
||||
return resource.UpdateResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Delete.
|
||||
func getSnapshotResourceDeleteRequest(t *testing.T, data map[string]string) resource.DeleteRequest {
|
||||
func getLocalSnapshotResourceDeleteRequest(t *testing.T, data map[string]string) resource.DeleteRequest {
|
||||
stateMap := make(map[string]tftypes.Value)
|
||||
for key, value := range data {
|
||||
if slices.Contains(snapshotResourceBooleanFields, key) { // snapshotResourceBooleanFields is a constant
|
||||
|
|
@ -567,21 +567,21 @@ func getSnapshotResourceDeleteRequest(t *testing.T, data map[string]string) reso
|
|||
stateMap[key] = tftypes.NewValue(tftypes.String, value)
|
||||
}
|
||||
}
|
||||
stateValue := tftypes.NewValue(getSnapshotResourceAttributeTypes(), stateMap)
|
||||
stateValue := tftypes.NewValue(getLocalSnapshotResourceAttributeTypes(), stateMap)
|
||||
return resource.DeleteRequest{
|
||||
State: tfsdk.State{
|
||||
Raw: stateValue,
|
||||
Schema: getSnapshotResourceSchema().Schema,
|
||||
Schema: getLocalSnapshotResourceSchema().Schema,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceDeleteResponseContainer() resource.DeleteResponse {
|
||||
func getLocalSnapshotResourceDeleteResponseContainer() resource.DeleteResponse {
|
||||
// A delete response does not need a schema as it results in a null state.
|
||||
return resource.DeleteResponse{}
|
||||
}
|
||||
|
||||
func getSnapshotResourceDeleteResponse() resource.DeleteResponse {
|
||||
func getLocalSnapshotResourceDeleteResponse() resource.DeleteResponse {
|
||||
return resource.DeleteResponse{
|
||||
State: tfsdk.State{
|
||||
Raw: tftypes.Value{},
|
||||
|
|
@ -591,7 +591,7 @@ func getSnapshotResourceDeleteResponse() resource.DeleteResponse {
|
|||
}
|
||||
|
||||
// The helpers helpers.
|
||||
func getSnapshotResourceAttributeTypes() tftypes.Object {
|
||||
func getLocalSnapshotResourceAttributeTypes() tftypes.Object {
|
||||
return tftypes.Object{
|
||||
AttributeTypes: map[string]tftypes.Type{
|
||||
"id": tftypes.String,
|
||||
|
|
@ -604,8 +604,8 @@ func getSnapshotResourceAttributeTypes() tftypes.Object {
|
|||
}
|
||||
}
|
||||
|
||||
func getSnapshotResourceSchema() *resource.SchemaResponse {
|
||||
var testResource SnapshotResource
|
||||
func getLocalSnapshotResourceSchema() *resource.SchemaResponse {
|
||||
var testResource LocalSnapshotResource
|
||||
r := &resource.SchemaResponse{}
|
||||
testResource.Schema(context.Background(), resource.SchemaRequest{}, r)
|
||||
return r
|
||||
|
|
@ -10,7 +10,8 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/rancher/terraform-provider-file/internal/provider/file_local"
|
||||
"github.com/rancher/terraform-provider-file/internal/provider/file_snapshot"
|
||||
"github.com/rancher/terraform-provider-file/internal/provider/file_local_directory"
|
||||
"github.com/rancher/terraform-provider-file/internal/provider/file_local_snapshot"
|
||||
)
|
||||
|
||||
// The `var _` is a special Go construct that results in an unusable variable.
|
||||
|
|
@ -51,14 +52,16 @@ func (p *FileProvider) Configure(ctx context.Context, req provider.ConfigureRequ
|
|||
func (p *FileProvider) Resources(ctx context.Context) []func() resource.Resource {
|
||||
return []func() resource.Resource{
|
||||
file_local.NewLocalResource,
|
||||
file_snapshot.NewSnapshotResource,
|
||||
file_local_snapshot.NewLocalSnapshotResource,
|
||||
file_local_directory.NewLocalDirectoryResource,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *FileProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
|
||||
return []func() datasource.DataSource{
|
||||
file_local.NewLocalDataSource,
|
||||
file_snapshot.NewSnapshotDataSource,
|
||||
file_local_snapshot.NewLocalSnapshotDataSource,
|
||||
file_local_directory.NewLocalDirectoryDataSource,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gruntwork-io/terratest/modules/terraform"
|
||||
util "github.com/rancher/terraform-provider-file/test"
|
||||
)
|
||||
|
||||
func TestLocalDirectoryAdvanced(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id := util.GetId()
|
||||
directory := "local_directory_advanced"
|
||||
repoRoot, err := util.GetRepoRoot(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting git root directory: %v", err)
|
||||
}
|
||||
exampleDir := filepath.Join(repoRoot, "examples", "use-cases", directory)
|
||||
testDir := filepath.Join(repoRoot, "test", "data", id)
|
||||
newDir := filepath.Join(repoRoot, "test", "data", id, "newDirectory", "subDirectory")
|
||||
|
||||
err = util.Setup(t, id, "test/data")
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, &terraform.Options{})
|
||||
t.Fatalf("Error creating test data directories: %s", err)
|
||||
}
|
||||
statePath := filepath.Join(testDir, "tfstate")
|
||||
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
|
||||
TerraformDir: exampleDir,
|
||||
Vars: map[string]interface{}{
|
||||
"path": newDir,
|
||||
},
|
||||
BackendConfig: map[string]interface{}{
|
||||
"path": statePath,
|
||||
},
|
||||
EnvVars: map[string]string{
|
||||
"TF_DATA_DIR": testDir,
|
||||
"TF_CLI_CONFIG_FILE": filepath.Join(repoRoot, "test", ".terraformrc"),
|
||||
"TF_IN_AUTOMATION": "1",
|
||||
"TF_CLI_ARGS_init": "-no-color",
|
||||
"TF_CLI_ARGS_plan": "-no-color",
|
||||
"TF_CLI_ARGS_apply": "-no-color",
|
||||
"TF_CLI_ARGS_destroy": "-no-color",
|
||||
"TF_CLI_ARGS_output": "-no-color",
|
||||
},
|
||||
RetryableTerraformErrors: util.GetRetryableTerraformErrors(),
|
||||
NoColor: true,
|
||||
Upgrade: true,
|
||||
// ExtraArgs: terraform.ExtraArgs{ Output: []string{"-json"} },
|
||||
})
|
||||
|
||||
_, err = terraform.InitAndApplyE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
t.Fatalf("Error creating file: %s", err)
|
||||
}
|
||||
_, err = terraform.OutputAllE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Output failed, moving along...")
|
||||
}
|
||||
|
||||
directoryExists, err := util.CheckFileExists(filepath.Join(newDir))
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
t.Fatalf("Error checking file: %s", err)
|
||||
}
|
||||
if !directoryExists {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Log("Test failed...")
|
||||
} else {
|
||||
t.Log("Test passed...")
|
||||
}
|
||||
t.Log("Test complete, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gruntwork-io/terratest/modules/terraform"
|
||||
util "github.com/rancher/terraform-provider-file/test"
|
||||
)
|
||||
|
||||
func TestLocalDirectoryBasic(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id := util.GetId()
|
||||
directory := "local_directory_basic"
|
||||
repoRoot, err := util.GetRepoRoot(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting git root directory: %v", err)
|
||||
}
|
||||
exampleDir := filepath.Join(repoRoot, "examples", "use-cases", directory)
|
||||
testDir := filepath.Join(repoRoot, "test", "data", id)
|
||||
newDir := filepath.Join(repoRoot, "test", "data", id, "newDirectory")
|
||||
|
||||
err = util.Setup(t, id, "test/data")
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, &terraform.Options{})
|
||||
t.Fatalf("Error creating test data directories: %s", err)
|
||||
}
|
||||
statePath := filepath.Join(testDir, "tfstate")
|
||||
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
|
||||
TerraformDir: exampleDir,
|
||||
Vars: map[string]interface{}{
|
||||
"path": newDir,
|
||||
},
|
||||
BackendConfig: map[string]interface{}{
|
||||
"path": statePath,
|
||||
},
|
||||
EnvVars: map[string]string{
|
||||
"TF_DATA_DIR": testDir,
|
||||
"TF_CLI_CONFIG_FILE": filepath.Join(repoRoot, "test", ".terraformrc"),
|
||||
"TF_IN_AUTOMATION": "1",
|
||||
"TF_CLI_ARGS_init": "-no-color",
|
||||
"TF_CLI_ARGS_plan": "-no-color",
|
||||
"TF_CLI_ARGS_apply": "-no-color",
|
||||
"TF_CLI_ARGS_destroy": "-no-color",
|
||||
"TF_CLI_ARGS_output": "-no-color",
|
||||
},
|
||||
RetryableTerraformErrors: util.GetRetryableTerraformErrors(),
|
||||
NoColor: true,
|
||||
Upgrade: true,
|
||||
// ExtraArgs: terraform.ExtraArgs{ Output: []string{"-json"} },
|
||||
})
|
||||
|
||||
_, err = terraform.InitAndApplyE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
t.Fatalf("Error creating file: %s", err)
|
||||
}
|
||||
_, err = terraform.OutputAllE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Output failed, moving along...")
|
||||
}
|
||||
|
||||
directoryExists, err := util.CheckFileExists(filepath.Join(newDir))
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
t.Fatalf("Error checking file: %s", err)
|
||||
}
|
||||
if !directoryExists {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Log("Test failed...")
|
||||
} else {
|
||||
t.Log("Test passed...")
|
||||
}
|
||||
t.Log("Test complete, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gruntwork-io/terratest/modules/terraform"
|
||||
util "github.com/rancher/terraform-provider-file/test"
|
||||
)
|
||||
|
||||
func TestLocalDirectoryExample(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id := util.GetId()
|
||||
directory := "file_local_directory"
|
||||
repoRoot, err := util.GetRepoRoot(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting git root directory: %v", err)
|
||||
}
|
||||
exampleDir := filepath.Join(repoRoot, "examples", "resources", directory)
|
||||
testDir := filepath.Join(repoRoot, "test", "data", id)
|
||||
|
||||
err = util.Setup(t, id, "test/data")
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, &terraform.Options{})
|
||||
t.Fatalf("Error creating test data directories: %s", err)
|
||||
}
|
||||
statePath := filepath.Join(testDir, "tfstate")
|
||||
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
|
||||
TerraformDir: exampleDir,
|
||||
Vars: map[string]interface{}{},
|
||||
BackendConfig: map[string]interface{}{
|
||||
"path": statePath,
|
||||
},
|
||||
EnvVars: map[string]string{
|
||||
"TF_DATA_DIR": testDir,
|
||||
"TF_CLI_CONFIG_FILE": filepath.Join(repoRoot, "test", ".terraformrc"),
|
||||
"TF_IN_AUTOMATION": "1",
|
||||
"TF_CLI_ARGS_init": "-no-color",
|
||||
"TF_CLI_ARGS_plan": "-no-color",
|
||||
"TF_CLI_ARGS_apply": "-no-color",
|
||||
"TF_CLI_ARGS_destroy": "-no-color",
|
||||
"TF_CLI_ARGS_output": "-no-color",
|
||||
},
|
||||
RetryableTerraformErrors: util.GetRetryableTerraformErrors(),
|
||||
NoColor: true,
|
||||
Upgrade: true,
|
||||
// ExtraArgs: terraform.ExtraArgs{ Output: []string{"-json"} },
|
||||
})
|
||||
|
||||
_, err = terraform.InitAndApplyE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
t.Fatalf("Error creating file: %s", err)
|
||||
}
|
||||
_, err = terraform.OutputAllE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Output failed, moving along...")
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Log("Test failed...")
|
||||
} else {
|
||||
t.Log("Test passed...")
|
||||
}
|
||||
t.Log("Test complete, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gruntwork-io/terratest/modules/terraform"
|
||||
util "github.com/rancher/terraform-provider-file/test"
|
||||
)
|
||||
|
||||
func TestLocalDirectorySubdirectory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id := util.GetId()
|
||||
directory := "local_directory_basic"
|
||||
repoRoot, err := util.GetRepoRoot(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting git root directory: %v", err)
|
||||
}
|
||||
exampleDir := filepath.Join(repoRoot, "examples", "use-cases", directory)
|
||||
testDir := filepath.Join(repoRoot, "test", "data", id)
|
||||
newDir := filepath.Join(repoRoot, "test", "data", id, "newDirectory", "newSubdirectory")
|
||||
|
||||
err = util.Setup(t, id, "test/data")
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, &terraform.Options{})
|
||||
t.Fatalf("Error creating test data directories: %s", err)
|
||||
}
|
||||
statePath := filepath.Join(testDir, "tfstate")
|
||||
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
|
||||
TerraformDir: exampleDir,
|
||||
Vars: map[string]interface{}{
|
||||
"path": newDir,
|
||||
},
|
||||
BackendConfig: map[string]interface{}{
|
||||
"path": statePath,
|
||||
},
|
||||
EnvVars: map[string]string{
|
||||
"TF_DATA_DIR": testDir,
|
||||
"TF_CLI_CONFIG_FILE": filepath.Join(repoRoot, "test", ".terraformrc"),
|
||||
"TF_IN_AUTOMATION": "1",
|
||||
"TF_CLI_ARGS_init": "-no-color",
|
||||
"TF_CLI_ARGS_plan": "-no-color",
|
||||
"TF_CLI_ARGS_apply": "-no-color",
|
||||
"TF_CLI_ARGS_destroy": "-no-color",
|
||||
"TF_CLI_ARGS_output": "-no-color",
|
||||
},
|
||||
RetryableTerraformErrors: util.GetRetryableTerraformErrors(),
|
||||
NoColor: true,
|
||||
Upgrade: true,
|
||||
// ExtraArgs: terraform.ExtraArgs{ Output: []string{"-json"} },
|
||||
})
|
||||
|
||||
_, err = terraform.InitAndApplyE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
t.Fatalf("Error creating file: %s", err)
|
||||
}
|
||||
_, err = terraform.OutputAllE(t, terraformOptions)
|
||||
if err != nil {
|
||||
t.Log("Output failed, moving along...")
|
||||
}
|
||||
|
||||
directoryExists, err := util.CheckFileExists(newDir)
|
||||
if err != nil {
|
||||
t.Log("Test failed, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
t.Fatalf("Error checking file: %s", err)
|
||||
}
|
||||
if !directoryExists {
|
||||
t.Log("Directory doesn't exist")
|
||||
t.Fail()
|
||||
} else {
|
||||
t.Log("Directory exists")
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Log("Test failed...")
|
||||
} else {
|
||||
t.Log("Test passed...")
|
||||
}
|
||||
t.Log("Test complete, tearing down...")
|
||||
util.TearDown(t, testDir, terraformOptions)
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ func TestSnapshotBasic(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
id := util.GetId()
|
||||
directory := "snapshot_basic"
|
||||
directory := "local_snapshot_basic"
|
||||
repoRoot, err := util.GetRepoRoot(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting git root directory: %v", err)
|
||||
|
|
@ -13,7 +13,7 @@ func TestSnapshotExample(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
id := util.GetId()
|
||||
directory := "file_snapshot"
|
||||
directory := "file_local_snapshot"
|
||||
repoRoot, err := util.GetRepoRoot(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting git root directory: %v", err)
|
||||
|
|
@ -13,7 +13,7 @@ func TestSnapshotCompressed(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
id := util.GetId()
|
||||
directory := "snapshot_compressed"
|
||||
directory := "local_snapshot_compressed"
|
||||
repoRoot, err := util.GetRepoRoot(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting git root directory: %v", err)
|
||||
Loading…
Reference in New Issue