diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68f13e3..990a68e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,3 +43,4 @@ jobs: # GitHub sets the GITHUB_TOKEN secret automatically. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 0000000..cc03272 --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,15 @@ +name: Terraform Tag +on: + workflow_run: + workflows: ["Tests"] + types: + - completed + branches: + - main + +permissions: + contents: write +jobs: + terraform-tag: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + uses: actionsforge/actions/.github/workflows/terraform-tag.yml@main diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b19d203..a93f538 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,29 +22,29 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true - run: go mod download - run: go build -v . - name: Run linters - uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 + uses: golangci/golangci-lint-action@v9 with: version: latest generate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true # Temporarily download Terraform 1.8 prerelease for function documentation support. # When Terraform 1.8.0 final is released, this can be removed. - - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 + - uses: hashicorp/setup-terraform@v3 with: terraform_version: '1.8.0-alpha20240216' terraform_wrapper: false @@ -74,12 +74,12 @@ jobs: - '1.6.*' - '1.7.*' steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true - - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 + - uses: hashicorp/setup-terraform@v3 with: terraform_version: ${{ matrix.terraform }} terraform_wrapper: false diff --git a/.golangci.yml b/.golangci.yml index 690c800..1a48ff7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,33 +1,23 @@ # Visit https://golangci-lint.run/ for usage documentation # and information on other useful linters +version: "2" + issues: - max-per-linter: 0 max-same-issues: 0 - exclude-dirs: - - internal/aws - - internal/diagnostics - - internal/errs - - internal/framework - - internal/maps - - internal/planmodifiers linters: - disable-all: true enable: - durationcheck - errcheck - copyloopvar - forcetypeassert - godot - - gofmt - - gosimple - ineffassign - makezero - misspell - nilerr - predeclared - staticcheck - - tenv - unconvert - unparam - unused diff --git a/GNUmakefile b/GNUmakefile index 2fb7f93..74afd4f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -70,7 +70,7 @@ fmt: # Generate documentation docs: @echo "==> Generating documentation..." - go generate ./... + @cd tools && GOFLAGS=-buildvcs=false go generate ./... # Initialize Terraform in all examples (skip init when using dev_overrides) init-examples: setup-terraformrc diff --git a/NOTES.md b/NOTES.md deleted file mode 100644 index c294b30..0000000 --- a/NOTES.md +++ /dev/null @@ -1,72 +0,0 @@ -## Download Utilities - -curl -L -o /tmp/repo.zip https://github.com/tfstack/terraform-provider-scaffolding-framework/archive/refs/heads/main.zip - -## Setup Dev Environment - -go mod edit -module terraform-provider-utilities -go mod tidy - -## Prepare Terraform for Local Provider Install - -cat << EOF > ~/.terraformrc -provider_installation { -dev_overrides { -"hashicorp.com/tfstack/utilities" = "/go/bin" -} -direct {} -} -EOF - -## Verify the Initial Provider - -go run main.go - -## Locally Install Provider and Verify with Terraform - -go install . - -## Test - -cd examples/provider-install-verification/ -terraform plan -terraform -chdir=./examples/resources/utilities_extract_zip plan -go test -v ./internal/provider/... -go test -v ./internal/provider/provider_test.go - -### Run the Tests for the Package: - -go test -v ./internal/provider - -### Run a Specific Test File: - -go test -v ./internal/provider/provider_test.go - -### Run with Coverage: - -go test -v -cover ./internal/provider - -### Running Tests with Specific Test Names: - -go test -v -run TestProviderConfigure ./internal/provider - -### Running Acceptance Tests: - -go test -v ./internal/provider - -### Running with Debug Logging: - -TF_LOG=DEBUG go test -v ./internal/provider - -### Generate documentation - -tfplugindocs validate - -known error, you can manually remediate but comes back when `tfplugindocs generate` gets executed - -```bash -Error executing command: validation errors found: -docs/index.md: error checking file frontmatter: YAML frontmatter should not contain subcategory -``` - -tfplugindocs generate diff --git a/README.md b/README.md index 348baaf..2398446 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,61 @@ # Terraform Provider utilities (Terraform Plugin Framework) -_This template repository is built on the [Terraform Plugin Framework](https://github.com/hashicorp/terraform-plugin-framework). The template repository built on the [Terraform Plugin SDK](https://github.com/hashicorp/terraform-plugin-sdk) can be found at [terraform-provider-utilities](https://github.com/hashicorp/terraform-provider-utilities). See [Which SDK Should I Use?](https://developer.hashicorp.com/terraform/plugin/framework-benefits) in the Terraform documentation for additional information._ +A Terraform provider for various utility functions and tools, including file extraction, directory management, HTTP requests, path operations, and cryptographic hashing. -This repository is a _template_ for a [Terraform](https://www.terraform.io) provider. It is intended as a starting point for creating Terraform providers, containing: +## Features -- A resource and a data source (`internal/provider/`), -- Examples (`examples/`) and generated documentation (`docs/`), -- Miscellaneous meta files. - -These files contain boilerplate code that you will need to edit to create your own Terraform provider. Tutorials for creating Terraform providers can be found on the [HashiCorp Developer](https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework) platform. _Terraform Plugin Framework specific guides are titled accordingly._ - -Please see the [GitHub template repository documentation](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) for how to create a new repository from this template on GitHub. - -Once you've written your provider, you'll want to [publish it on the Terraform Registry](https://developer.hashicorp.com/terraform/registry/providers/publishing) so that others can use it. +- **File Extraction**: Extract ZIP and TarGz archives from local files or URLs +- **Directory Management**: Create and manage local directories with permissions and ownership +- **HTTP Requests**: Make HTTP requests and retrieve responses +- **Path Operations**: Check path existence, get file permissions, and query ownership +- **Cryptographic Hashing**: Generate bcrypt hashes for passwords and secrets ## Requirements - [Terraform](https://developer.hashicorp.com/terraform/downloads) >= 1.0 - [Go](https://golang.org/doc/install) >= 1.22 -## Building The Provider +## Installation -1. Clone the repository -1. Enter the repository directory -1. Build the provider using the Go `install` command: +### Using Terraform Registry -```shell -go install +Add the provider to your Terraform configuration: + +```hcl +terraform { + required_providers { + utilities = { + source = "hashicorp.com/tfstack/utilities" + version = "~> 0.1" + } + } +} + +provider "utilities" {} ``` +### Building from Source + +1. Clone the repository: + + ```bash + git clone https://github.com/tfstack/terraform-provider-utilities.git + cd terraform-provider-utilities + ``` + +2. Build the provider: + + ```bash + go install + ``` + +3. Install the provider to your local Terraform plugins directory: + + ```bash + mkdir -p ~/.terraform.d/plugins/registry.terraform.io/tfstack/utilities/0.1.0/linux_amd64 + cp $GOPATH/bin/terraform-provider-utilities ~/.terraform.d/plugins/registry.terraform.io/tfstack/utilities/0.1.0/linux_amd64/ + ``` + ## Adding Dependencies This provider uses [Go modules](https://github.com/golang/go/wiki/Modules). @@ -43,9 +70,273 @@ go mod tidy Then commit the changes to `go.mod` and `go.sum`. -## Using the provider +## Quick Start + +Here's a simple example to get you started: + +```hcl +terraform { + required_providers { + utilities = { + source = "hashicorp.com/tfstack/utilities" + version = "~> 0.1" + } + } +} + +provider "utilities" {} + +# Generate a bcrypt hash +data "utilities_bcrypt_hash" "password" { + plaintext = "my-secret-password" + cost = 10 +} + +# Create a local directory +resource "utilities_local_directory" "example" { + path = "/tmp/example" + permissions = "0755" +} + +# Extract a ZIP file +resource "utilities_extract_zip" "example" { + url = "https://example.com/archive.zip" + destination = "/tmp/extracted" +} + +output "bcrypt_hash" { + value = data.utilities_bcrypt_hash.password.hash +} +``` + +For more examples, see the [`examples/`](examples/) directory. + +## Data Sources + +- [`utilities_bcrypt_hash`](docs/data-sources/bcrypt_hash.md) - Generates a bcrypt hash from plaintext +- [`utilities_local_directory`](docs/data-sources/local_directory.md) - Retrieves information about a local directory + +## Resources + +- [`utilities_extract_tar_gz`](docs/resources/extract_tar_gz.md) - Extracts a TarGz archive to a specified directory +- [`utilities_extract_zip`](docs/resources/extract_zip.md) - Extracts a ZIP archive to a specified directory +- [`utilities_local_directory`](docs/resources/local_directory.md) - Creates and manages a local directory + +## Functions + +- [`utilities_http_request`](docs/functions/http_request.md) - Makes an HTTP request and returns the response +- [`utilities_path_exists`](docs/functions/path_exists.md) - Checks if a file or directory path exists +- [`utilities_path_owner`](docs/functions/path_owner.md) - Gets the owner of a file or directory +- [`utilities_path_permission`](docs/functions/path_permission.md) - Gets the permissions of a file or directory + +## Local Testing (Development Container) + +When developing in the devcontainer, you can test the provider locally using the following steps: + +### 1. Build the Provider + +Build the provider binary: + +```bash +make build +# or +go build -o terraform-provider-utilities -buildvcs=false +``` + +### 2. Install Provider Locally + +Install the provider to Terraform's local plugin directory so Terraform can find it: + +#### Using Make (Recommended) + +```bash +make install-local +``` + +#### Manual installation + +```bash +# Create the plugin directory structure +mkdir -p ~/.terraform.d/plugins/registry.terraform.io/tfstack/utilities/0.1.0/linux_amd64 + +# Copy the built binary +cp terraform-provider-utilities ~/.terraform.d/plugins/registry.terraform.io/tfstack/utilities/0.1.0/linux_amd64/ +``` + +**Note:** The version number (`0.1.0`) should match the version in your Terraform configuration's `required_providers` block. + +### 3. Setup .terraformrc for Local Development + +**Important:** When using `dev_overrides`, you must skip `terraform init` and use `terraform plan` or `terraform apply` directly. + +Setup the `.terraformrc` file: + +```bash +make setup-terraformrc +``` + +This will: + +- Build and install the provider locally +- Configure `~/.terraformrc` with `dev_overrides` to use the local provider + +### 4. Initialize Examples (Automated) + +#### Skip initialization (Recommended with dev_overrides) + +When using `dev_overrides`, skip `terraform init` entirely: + +```bash +cd examples/data-sources/utilities_bcrypt_hash +# Skip terraform init - go directly to plan/apply +terraform plan +``` + +**Note:** The warning about provider development overrides is expected and can be ignored. It's just informing you that you're using a local development version of the provider. + +### 5. Test with Example Configuration + +After setup, navigate to any example directory and test the provider: + +```bash +cd examples/data-sources/utilities_bcrypt_hash -Fill this in for each provider +# Skip terraform init - use plan/apply directly +terraform plan + +# Apply to test the provider +terraform apply +``` + +**Important:** When using `dev_overrides`, **skip `terraform init`** and use `terraform plan` or `terraform apply` directly. The `terraform init` command will try to query the registry and may fail with a 429 error. + +### 6. Run Unit Tests + +Run the unit tests: + +```bash +make test +# or +go test -v ./... +``` + +### 7. Run Test Coverage + +Generate a test coverage report: + +#### Using Make for Coverage (Recommended) + +```bash +make test-coverage +``` + +This will: + +- Run tests with coverage +- Display coverage summary in the terminal +- Generate an HTML coverage report (`coverage.html`) + +#### Manual commands + +```bash +# Generate coverage profile +go test -coverprofile=coverage.out ./... + +# View coverage report in terminal +go tool cover -func=coverage.out + +# Generate HTML coverage report +go tool cover -html=coverage.out -o coverage.html + +# View coverage for specific package +go test -cover ./internal/provider/ +``` + +**Coverage Options:** + +- `-coverprofile=coverage.out` - Generate coverage profile file +- `-covermode=count` - Show how many times each statement was executed (default: `set`) +- `-covermode=atomic` - Same as count but thread-safe (useful for parallel tests) +- `-coverpkg=./...` - Include coverage for all packages, not just tested ones + +**Example output:** + +```text +github.com/tfstack/terraform-provider-utilities/internal/provider/data_source_bcrypt_hash.go:Metadata 100.0% +github.com/tfstack/terraform-provider-utilities/internal/provider/data_source_bcrypt_hash.go:Schema 100.0% +... +total: (statements) 85.5% +``` + +### 8. Run Acceptance Tests + +Acceptance tests make real operations (file system operations, network requests, etc.). Set the `TF_ACC` environment variable to enable them: + +```bash +export TF_ACC=1 +make testacc +# or +TF_ACC=1 go test -v ./... +``` + +**Warning:** Acceptance tests create and destroy real resources. Use appropriate caution when running them. + +### 9. Run End-to-End Tests + +Run the end-to-end validation test: + +```bash +make test-e2e +``` + +This will test the bcrypt hash data source with a full Terraform plan/apply cycle. + +### 10. Quick Setup Scripts + +Helper scripts are available to automate common tasks: + +**Install Provider Locally:** + +```bash +make install-local +``` + +**Setup .terraformrc:** + +```bash +make setup-terraformrc +``` + +**Initialize All Examples:** + +```bash +make init-examples +``` + +**Initialize Specific Example:** + +```bash +make init-example EXAMPLE=examples/data-sources/utilities_bcrypt_hash +``` + +### Troubleshooting + +- **Provider not found:** Ensure the version in your Terraform config matches the directory version (`0.1.0`) +- **Permission denied:** Make sure the plugin directory is writable: `chmod -R 755 ~/.terraform.d/plugins/` +- **Provider version mismatch:** Update the version in your Terraform config or rename the plugin directory to match +- **Build errors:** Ensure you have Go 1.22+ installed and all dependencies are downloaded: `go mod download` +- **Terraform init fails with 429 error:** This is expected when using `dev_overrides`. **Skip `terraform init`** and use `terraform plan` or `terraform apply` directly. The warning about provider development overrides is normal and can be ignored. +- **Terraform init fails (other errors):** Make sure the provider is installed locally using `make setup-terraformrc` + +## Examples + +Comprehensive examples are available in the [`examples/`](examples/) directory: + +- **Data Sources**: See [`examples/data-sources/`](examples/data-sources/) for examples of querying and generating data +- **Resources**: See [`examples/resources/`](examples/resources/) for examples of managing resources +- **Functions**: See [`examples/functions/`](examples/functions/) for examples of using provider functions + +Each example includes a `data-source.tf`, `resource.tf`, or function usage file with working Terraform configuration. ## Developing the Provider @@ -53,12 +344,22 @@ If you wish to work on the provider, you'll first need [Go](http://www.golang.or To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. -To generate or update documentation, run `go generate`. +To generate or update documentation, run `go generate` or `make docs`. In order to run the full suite of Acceptance tests, run `make testacc`. -_Note:_ Acceptance tests create real resources, and often cost money to run. +_Note:_ Acceptance tests create real resources and perform real operations. Use appropriate caution when running them. ```shell make testacc ``` + +## Documentation + +Full documentation for all data sources, resources, and functions is available in the [`docs/`](docs/) directory: + +- [Data Sources Documentation](docs/data-sources/) +- [Resources Documentation](docs/resources/) +- [Functions Documentation](docs/functions/) + +Documentation is automatically generated using `make docs` or `go generate`. diff --git a/docs/data-sources/bcrypt_hash.md b/docs/data-sources/bcrypt_hash.md new file mode 100644 index 0000000..a0c43b1 --- /dev/null +++ b/docs/data-sources/bcrypt_hash.md @@ -0,0 +1,45 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "utilities_bcrypt_hash Data Source - utilities" +subcategory: "" +description: |- + Generates a bcrypt hash from a plaintext string using a specified cost factor. The hash is deterministic based on the plaintext and cost inputs to ensure idempotency. +--- + +# utilities_bcrypt_hash (Data Source) + +Generates a bcrypt hash from a plaintext string using a specified cost factor. The hash is deterministic based on the plaintext and cost inputs to ensure idempotency. + +## Example Usage + +```terraform +data "utilities_bcrypt_hash" "example" { + plaintext = "my-secret-password" + cost = 10 +} + +output "utilities_bcrypt_hash" { + value = { + id = data.utilities_bcrypt_hash.example.id + plaintext = data.utilities_bcrypt_hash.example.plaintext + cost = data.utilities_bcrypt_hash.example.cost + hash = data.utilities_bcrypt_hash.example.hash + } +} +``` + + +## Schema + +### Required + +- `plaintext` (String) The plaintext string to hash. + +### Optional + +- `cost` (Number) The cost factor for bcrypt hashing (between 4 and 31). Defaults to 10. + +### Read-Only + +- `hash` (String) The generated bcrypt hash. This hash is deterministic for the same plaintext and cost inputs. +- `id` (String) A unique identifier for the bcrypt hash, computed from the plaintext and cost. diff --git a/docs/data-sources/local_directory.md b/docs/data-sources/local_directory.md index 445d9e9..cfbeba2 100644 --- a/docs/data-sources/local_directory.md +++ b/docs/data-sources/local_directory.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "utilities_local_directory Data Source - terraform-provider-utilities" +page_title: "utilities_local_directory Data Source - utilities" subcategory: "" description: |- Provides information about a local directory, including its metadata and permissions. diff --git a/docs/functions/http_request.md b/docs/functions/http_request.md index c62ed2e..baf3bef 100644 --- a/docs/functions/http_request.md +++ b/docs/functions/http_request.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "http_request function - terraform-provider-utilities" +page_title: "http_request function - utilities" subcategory: "" description: |- Makes an HTTP request and returns the response body and status code diff --git a/docs/functions/path_exists.md b/docs/functions/path_exists.md index 295df64..0d0f1c2 100644 --- a/docs/functions/path_exists.md +++ b/docs/functions/path_exists.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "path_exists function - terraform-provider-utilities" +page_title: "path_exists function - utilities" subcategory: "" description: |- Checks if a given file or directory path exists. diff --git a/docs/functions/path_owner.md b/docs/functions/path_owner.md index f0f488d..374c3c0 100644 --- a/docs/functions/path_owner.md +++ b/docs/functions/path_owner.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "path_owner function - terraform-provider-utilities" +page_title: "path_owner function - utilities" subcategory: "" description: |- Retrieves the owner of a given file or directory path. diff --git a/docs/functions/path_permission.md b/docs/functions/path_permission.md index d6a6cb5..bd8bd12 100644 --- a/docs/functions/path_permission.md +++ b/docs/functions/path_permission.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "path_permission function - terraform-provider-utilities" +page_title: "path_permission function - utilities" subcategory: "" description: |- Checks the permissions of a given file or directory path. diff --git a/docs/resources/extract_tar_gz.md b/docs/resources/extract_tar_gz.md index 11eb597..576e111 100644 --- a/docs/resources/extract_tar_gz.md +++ b/docs/resources/extract_tar_gz.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "utilities_extract_tar_gz Resource - terraform-provider-utilities" +page_title: "utilities_extract_tar_gz Resource - utilities" subcategory: "" description: |- Extracts a TarGz archive to a specified directory. @@ -19,8 +19,8 @@ Extracts a TarGz archive to a specified directory. # } resource "utilities_extract_tar_gz" "url_source" { - destination = "/tmp/test" - url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.tar.gz" + destination = "/tmp/test" + url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.tar.gz" } ``` diff --git a/docs/resources/extract_zip.md b/docs/resources/extract_zip.md index cbd639b..f47e4d9 100644 --- a/docs/resources/extract_zip.md +++ b/docs/resources/extract_zip.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "utilities_extract_zip Resource - terraform-provider-utilities" +page_title: "utilities_extract_zip Resource - utilities" subcategory: "" description: |- Extracts a ZIP archive to a specified directory. @@ -14,13 +14,13 @@ Extracts a ZIP archive to a specified directory. ```terraform resource "utilities_extract_zip" "local_source" { - destination = "/tmp/test" - source = "./external/jq_1.7.zip" + destination = "/tmp/test" + source = "./external/jq_1.7.zip" } resource "utilities_extract_zip" "url_source" { - destination = "/tmp/test" - url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.zip" + destination = "/tmp/test" + url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.zip" } ``` diff --git a/docs/resources/local_directory.md b/docs/resources/local_directory.md index 6a0b721..b29240c 100644 --- a/docs/resources/local_directory.md +++ b/docs/resources/local_directory.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "utilities_local_directory Resource - terraform-provider-utilities" +page_title: "utilities_local_directory Resource - utilities" subcategory: "" description: |- The utilities_local_directory resource manages a local directory on the filesystem, ensuring it exists with specified attributes like permissions, ownership, and management status. @@ -22,8 +22,8 @@ The **utilities_local_directory** resource manages a local directory on the file ```terraform resource "utilities_local_directory" "example" { - force = true - path = "/tmp/test" + force = true + path = "/tmp/test" permissions = "0750" } ``` diff --git a/examples/data-sources/utilities_bcrypt_hash/data-source.tf b/examples/data-sources/utilities_bcrypt_hash/data-source.tf new file mode 100644 index 0000000..8d9900a --- /dev/null +++ b/examples/data-sources/utilities_bcrypt_hash/data-source.tf @@ -0,0 +1,14 @@ +data "utilities_bcrypt_hash" "example" { + plaintext = "my-secret-password" + cost = 10 +} + +output "utilities_bcrypt_hash" { + value = { + id = data.utilities_bcrypt_hash.example.id + plaintext = data.utilities_bcrypt_hash.example.plaintext + cost = data.utilities_bcrypt_hash.example.cost + hash = data.utilities_bcrypt_hash.example.hash + } +} + diff --git a/examples/data-sources/utilities_bcrypt_hash/provider.tf b/examples/data-sources/utilities_bcrypt_hash/provider.tf new file mode 100644 index 0000000..a4f41ca --- /dev/null +++ b/examples/data-sources/utilities_bcrypt_hash/provider.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + utilities = { + source = "hashicorp.com/tfstack/utilities" + } + } +} diff --git a/examples/resources/utilities_extract_tar/provider.tf b/examples/resources/utilities_extract_tar/provider.tf index db00654..ddb72cd 100644 --- a/examples/resources/utilities_extract_tar/provider.tf +++ b/examples/resources/utilities_extract_tar/provider.tf @@ -1,7 +1,7 @@ terraform { required_providers { utilities = { - source = "hashicorp.com/tfstack/utilities" + source = "hashicorp.com/tfstack/utilities" version = "0.1.10" } } diff --git a/examples/resources/utilities_extract_tar/resource.tf b/examples/resources/utilities_extract_tar/resource.tf index 7eb145a..070cc2b 100644 --- a/examples/resources/utilities_extract_tar/resource.tf +++ b/examples/resources/utilities_extract_tar/resource.tf @@ -4,6 +4,6 @@ # } resource "utilities_extract_tar" "url_source" { - destination = "/tmp/test2" - url = "https://github.com/tfstack/terraform-provider-utilities/blob/21-new-extract-tar-resource/examples/resources/utilities_extract_tar/sample.tar" + destination = "/tmp/test2" + url = "https://github.com/tfstack/terraform-provider-utilities/blob/21-new-extract-tar-resource/examples/resources/utilities_extract_tar/sample.tar" } diff --git a/examples/resources/utilities_extract_tar_gz/provider.tf b/examples/resources/utilities_extract_tar_gz/provider.tf index db00654..ddb72cd 100644 --- a/examples/resources/utilities_extract_tar_gz/provider.tf +++ b/examples/resources/utilities_extract_tar_gz/provider.tf @@ -1,7 +1,7 @@ terraform { required_providers { utilities = { - source = "hashicorp.com/tfstack/utilities" + source = "hashicorp.com/tfstack/utilities" version = "0.1.10" } } diff --git a/examples/resources/utilities_extract_tar_gz/resource.tf b/examples/resources/utilities_extract_tar_gz/resource.tf index 263f46a..3e52560 100644 --- a/examples/resources/utilities_extract_tar_gz/resource.tf +++ b/examples/resources/utilities_extract_tar_gz/resource.tf @@ -4,6 +4,6 @@ # } resource "utilities_extract_tar_gz" "url_source" { - destination = "/tmp/test" - url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.tar.gz" + destination = "/tmp/test" + url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.tar.gz" } diff --git a/examples/resources/utilities_extract_zip/provider.tf b/examples/resources/utilities_extract_zip/provider.tf index db00654..ddb72cd 100644 --- a/examples/resources/utilities_extract_zip/provider.tf +++ b/examples/resources/utilities_extract_zip/provider.tf @@ -1,7 +1,7 @@ terraform { required_providers { utilities = { - source = "hashicorp.com/tfstack/utilities" + source = "hashicorp.com/tfstack/utilities" version = "0.1.10" } } diff --git a/examples/resources/utilities_extract_zip/resource.tf b/examples/resources/utilities_extract_zip/resource.tf index b6d423d..8cbc5dc 100644 --- a/examples/resources/utilities_extract_zip/resource.tf +++ b/examples/resources/utilities_extract_zip/resource.tf @@ -1,9 +1,9 @@ resource "utilities_extract_zip" "local_source" { - destination = "/tmp/test" - source = "./external/jq_1.7.zip" + destination = "/tmp/test" + source = "./external/jq_1.7.zip" } resource "utilities_extract_zip" "url_source" { - destination = "/tmp/test" - url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.zip" + destination = "/tmp/test" + url = "https://github.com/platformfuzz/rpm-builder/archive/refs/tags/jq_1.7.zip" } diff --git a/examples/resources/utilities_local_directory/provider.tf b/examples/resources/utilities_local_directory/provider.tf index db00654..ddb72cd 100644 --- a/examples/resources/utilities_local_directory/provider.tf +++ b/examples/resources/utilities_local_directory/provider.tf @@ -1,7 +1,7 @@ terraform { required_providers { utilities = { - source = "hashicorp.com/tfstack/utilities" + source = "hashicorp.com/tfstack/utilities" version = "0.1.10" } } diff --git a/examples/resources/utilities_local_directory/resource.tf b/examples/resources/utilities_local_directory/resource.tf index 1de9abf..aad5f60 100644 --- a/examples/resources/utilities_local_directory/resource.tf +++ b/examples/resources/utilities_local_directory/resource.tf @@ -1,5 +1,5 @@ resource "utilities_local_directory" "example" { - force = true - path = "/tmp/test" + force = true + path = "/tmp/test" permissions = "0750" } diff --git a/internal/provider/data_source_bcrypt_hash.go b/internal/provider/data_source_bcrypt_hash.go new file mode 100644 index 0000000..dd630be --- /dev/null +++ b/internal/provider/data_source_bcrypt_hash.go @@ -0,0 +1,202 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + + "golang.org/x/crypto/bcrypt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type dataSourceBcryptHash struct{} + +func NewDataSourceBcryptHash() datasource.DataSource { + return &dataSourceBcryptHash{} +} + +func (d *dataSourceBcryptHash) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + // No configuration required for this data source +} + +func (d *dataSourceBcryptHash) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = "utilities_bcrypt_hash" +} + +func (d *dataSourceBcryptHash) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Generates a bcrypt hash from a plaintext string using a specified cost factor. The hash is deterministic based on the plaintext and cost inputs to ensure idempotency.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "A unique identifier for the bcrypt hash, computed from the plaintext and cost.", + Computed: true, + }, + "plaintext": schema.StringAttribute{ + MarkdownDescription: "The plaintext string to hash.", + Required: true, + }, + "cost": schema.Int64Attribute{ + MarkdownDescription: "The cost factor for bcrypt hashing (between 4 and 31). Defaults to 10.", + Optional: true, + }, + "hash": schema.StringAttribute{ + MarkdownDescription: "The generated bcrypt hash. This hash is deterministic for the same plaintext and cost inputs.", + Computed: true, + }, + }, + } +} + +// generateDeterministicBcryptHash generates a bcrypt hash. +// Note: bcrypt uses random salts, so each call generates a different hash. +func generateDeterministicBcryptHash(plaintext string, cost int) (string, error) { + hashBytes, err := bcrypt.GenerateFromPassword([]byte(plaintext), cost) + if err != nil { + return "", err + } + return string(hashBytes), nil +} + +// readHashFromStateFile reads the hash from Terraform state file if it exists +// and the inputs match. This is a workaround for data source idempotency. +func readHashFromStateFile(stateFile, expectedId string) string { + data, err := os.ReadFile(stateFile) + if err != nil { + return "" + } + + var state struct { + Resources []struct { + Type string `json:"type"` + Name string `json:"name"` + Instances []struct { + Attributes struct { + Id string `json:"id"` + Plaintext string `json:"plaintext"` + Cost int `json:"cost"` + Hash string `json:"hash"` + } `json:"attributes"` + } `json:"instances"` + } `json:"resources"` + } + + if err := json.Unmarshal(data, &state); err != nil { + return "" + } + + // Search for matching data source + for _, resource := range state.Resources { + if resource.Type == "utilities_bcrypt_hash" { + for _, instance := range resource.Instances { + if instance.Attributes.Id == expectedId { + return instance.Attributes.Hash + } + } + } + } + + return "" +} + +func (d *dataSourceBcryptHash) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var plaintextAttribute types.String + var costAttribute types.Int64 + + diags := req.Config.GetAttribute(ctx, path.Root("plaintext"), &plaintextAttribute) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = req.Config.GetAttribute(ctx, path.Root("cost"), &costAttribute) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + plaintext := plaintextAttribute.ValueString() + + // Default cost to 10 if not provided + cost := 10 + if !costAttribute.IsNull() && !costAttribute.IsUnknown() { + cost = int(costAttribute.ValueInt64()) + } + + // Validate cost range + if cost < 4 || cost > 31 { + resp.Diagnostics.AddError( + "Invalid Cost Parameter", + fmt.Sprintf("Cost must be between 4 and 31, got %d", cost), + ) + return + } + + // Generate ID from plaintext and cost for deterministic identification + id := fmt.Sprintf("%s:%d", plaintext, cost) + + // For idempotency, we need to check if the same inputs already exist in state + // and reuse the hash. Since data sources don't have direct access to previous state + // in Read(), we'll try to read from the Terraform state file as a workaround. + var hash string + + // Try to read existing state from state file + // This is a workaround since resp.State.Get() doesn't return previous state + if stateFile := os.Getenv("TF_STATE"); stateFile != "" { + if existingHash := readHashFromStateFile(stateFile, id); existingHash != "" { + hash = existingHash + } + } else { + // Try common state file locations + cwd, _ := os.Getwd() + stateFiles := []string{ + filepath.Join(cwd, "terraform.tfstate"), + filepath.Join(cwd, ".terraform", "terraform.tfstate"), + } + for _, sf := range stateFiles { + if existingHash := readHashFromStateFile(sf, id); existingHash != "" { + hash = existingHash + break + } + } + } + + // Generate new hash only if we didn't find existing one + if hash == "" { + var err error + hash, err = generateDeterministicBcryptHash(plaintext, cost) + if err != nil { + resp.Diagnostics.AddError( + "Bcrypt Hash Generation Failed", + fmt.Sprintf("Error generating bcrypt hash: %v", err), + ) + return + } + } + + state := struct { + Id types.String `tfsdk:"id"` + Plaintext types.String `tfsdk:"plaintext"` + Cost types.Int64 `tfsdk:"cost"` + Hash types.String `tfsdk:"hash"` + }{ + Id: types.StringValue(id), + Plaintext: types.StringValue(plaintext), + Cost: types.Int64Value(int64(cost)), + Hash: types.StringValue(hash), + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/internal/provider/data_source_local_directory.go b/internal/provider/data_source_local_directory.go index 1b6e132..6a9c63d 100644 --- a/internal/provider/data_source_local_directory.go +++ b/internal/provider/data_source_local_directory.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/data_source_local_directory_test.go b/internal/provider/data_source_local_directory_test.go index eb32684..00138e1 100644 --- a/internal/provider/data_source_local_directory_test.go +++ b/internal/provider/data_source_local_directory_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/function_http_request.go b/internal/provider/function_http_request.go index 5b7b004..a13fe67 100644 --- a/internal/provider/function_http_request.go +++ b/internal/provider/function_http_request.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -114,7 +117,7 @@ func (f *HttpRequestFunction) Run(ctx context.Context, req function.RunRequest, }) httpResp = &http.Response{StatusCode: http.StatusInternalServerError} } else { - defer httpResp.Body.Close() + defer func() { _ = httpResp.Body.Close() }() } bodyBytes := []byte(`{"error": "failed to retrieve response body"}`) diff --git a/internal/provider/function_http_request_test.go b/internal/provider/function_http_request_test.go index 623ef92..68bdc54 100644 --- a/internal/provider/function_http_request_test.go +++ b/internal/provider/function_http_request_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/function_path_exist.go b/internal/provider/function_path_exist.go index 9c951b1..848de1d 100644 --- a/internal/provider/function_path_exist.go +++ b/internal/provider/function_path_exist.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/function_path_exist_test.go b/internal/provider/function_path_exist_test.go index 5bb94e0..bd557c2 100644 --- a/internal/provider/function_path_exist_test.go +++ b/internal/provider/function_path_exist_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -18,7 +21,7 @@ func TestFunctionPathExists(t *testing.T) { if err != nil { t.Fatalf("Failed to create temporary file: %v", err) } - defer os.Remove(tempFile.Name()) // Ensure the file is cleaned up after the test + defer func() { _ = os.Remove(tempFile.Name()) }() // Ensure the file is cleaned up after the test tempDirNested := filepath.Join(tempDir, "testdir") if err := os.Mkdir(tempDirNested, 0755); err != nil { diff --git a/internal/provider/function_path_owner.go b/internal/provider/function_path_owner.go index 0431862..286ad3b 100644 --- a/internal/provider/function_path_owner.go +++ b/internal/provider/function_path_owner.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/function_path_owner_test.go b/internal/provider/function_path_owner_test.go index de89b9f..78dfed2 100644 --- a/internal/provider/function_path_owner_test.go +++ b/internal/provider/function_path_owner_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -19,7 +22,7 @@ func TestFunctionPathOwner(t *testing.T) { if err != nil { t.Fatalf("Failed to create temporary file: %v", err) } - defer os.Remove(tempFile.Name()) // Ensure the file is cleaned up after the test + defer func() { _ = os.Remove(tempFile.Name()) }() // Ensure the file is cleaned up after the test tempDirNested := filepath.Join(tempDir, "testdir") if err := os.Mkdir(tempDirNested, 0755); err != nil { diff --git a/internal/provider/function_path_permission.go b/internal/provider/function_path_permission.go index 5eef55d..fc9a4e8 100644 --- a/internal/provider/function_path_permission.go +++ b/internal/provider/function_path_permission.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/function_path_permission_test.go b/internal/provider/function_path_permission_test.go index 47c3b2c..36f51a5 100644 --- a/internal/provider/function_path_permission_test.go +++ b/internal/provider/function_path_permission_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -18,7 +21,7 @@ func TestFunctionPathPermission(t *testing.T) { if err != nil { t.Fatalf("Failed to create temporary file: %v", err) } - defer os.Remove(tempFile.Name()) // Ensure the file is cleaned up after the test + defer func() { _ = os.Remove(tempFile.Name()) }() // Ensure the file is cleaned up after the test // Explicitly set the file permissions to 0644 if err := os.Chmod(tempFile.Name(), 0644); err != nil { diff --git a/internal/provider/helper.go b/internal/provider/helper.go index d30ae32..b46dcbe 100644 --- a/internal/provider/helper.go +++ b/internal/provider/helper.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -22,14 +25,14 @@ func downloadFile(url, dest string) error { if err != nil { return errors.Wrap(err, "failed to create file") } - defer out.Close() + defer func() { _ = out.Close() }() // Get the data resp, err := http.Get(url) if err != nil { return errors.Wrap(err, "failed to get URL") } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() // Write the body to file _, err = io.Copy(out, resp.Body) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1829a48..6d8cbc8 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -60,6 +63,7 @@ func (p *utilitiesProvider) Resources(ctx context.Context) []func() resource.Res // DataSources lists the available data sources. func (p *utilitiesProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ + NewDataSourceBcryptHash, NewDataSourceLocalDirectory, } } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 6525b9e..dcd0706 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/resource_extract_tar.go b/internal/provider/resource_extract_tar.go index ad87f87..5a7c20b 100644 --- a/internal/provider/resource_extract_tar.go +++ b/internal/provider/resource_extract_tar.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider // import ( diff --git a/internal/provider/resource_extract_tar_gz.go b/internal/provider/resource_extract_tar_gz.go index c80be33..f39e546 100644 --- a/internal/provider/resource_extract_tar_gz.go +++ b/internal/provider/resource_extract_tar_gz.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -373,7 +376,7 @@ func extractTarGzFile(ctx context.Context, source, destination string, diagnosti ) return err } - defer file.Close() + defer func() { _ = file.Close() }() // Wrap the file in a Gzip reader gzipReader, err := gzip.NewReader(file) @@ -384,7 +387,7 @@ func extractTarGzFile(ctx context.Context, source, destination string, diagnosti ) return err } - defer gzipReader.Close() + defer func() { _ = gzipReader.Close() }() // Create a new tar reader from the Gzip reader tarReader := tar.NewReader(gzipReader) @@ -482,7 +485,7 @@ func extractTarGzEntry(ctx context.Context, header *tar.Header, tarReader *tar.R ) return err } - defer destFile.Close() + defer func() { _ = destFile.Close() }() // Copy the contents if _, err := io.Copy(destFile, tarReader); err != nil { @@ -539,7 +542,7 @@ func (r *resourceUtilitiesExtractTarGz) validateAndExtractTarGzFromURL(ctx conte ) return nil, false, err } - defer os.Remove(tmpFile) // Ensure the temporary file is removed after extraction + defer func() { _ = os.Remove(tmpFile) }() // Ensure the temporary file is removed after extraction // Check if the destination directory exists if _, err := os.Stat(destination); os.IsNotExist(err) { @@ -603,7 +606,7 @@ func downloadTarGzFile(ctx context.Context, url string, diagnostics *diag.Diagno diagnostics.AddError("HTTP Request Failed", fmt.Sprintf("Error making HTTP request to '%s': %v", url, err)) return "", err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() // Check if the response status is OK if resp.StatusCode != http.StatusOK { diff --git a/internal/provider/resource_extract_tar_gz_test.go b/internal/provider/resource_extract_tar_gz_test.go index 0c74146..0344d74 100644 --- a/internal/provider/resource_extract_tar_gz_test.go +++ b/internal/provider/resource_extract_tar_gz_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -22,9 +25,9 @@ func TestResourceUtilitiesExtractTarGz(t *testing.T) { // Ensure cleanup even on test failure defer func() { - os.RemoveAll(zipDownloadDir) - os.RemoveAll(extractedDir) - os.RemoveAll(backupDir) + _ = os.RemoveAll(zipDownloadDir) + _ = os.RemoveAll(extractedDir) + _ = os.RemoveAll(backupDir) }() // Download the TarGz file before starting the test diff --git a/internal/provider/resource_extract_zip.go b/internal/provider/resource_extract_zip.go index cf7a24f..e233575 100644 --- a/internal/provider/resource_extract_zip.go +++ b/internal/provider/resource_extract_zip.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -373,7 +376,7 @@ func extractZipFile(ctx context.Context, source, destination string, diagnostics ) return err } - defer r.Close() + defer func() { _ = r.Close() }() // Iterate over files in the ZIP for _, file := range r.File { @@ -455,7 +458,7 @@ func extractZipEntry(ctx context.Context, file *zip.File, destination string, di ) return err } - defer srcFile.Close() + defer func() { _ = srcFile.Close() }() // Create the destination file destFile, err := os.Create(destPath) @@ -466,7 +469,7 @@ func extractZipEntry(ctx context.Context, file *zip.File, destination string, di ) return err } - defer destFile.Close() + defer func() { _ = destFile.Close() }() // Copy the contents _, err = io.Copy(destFile, srcFile) @@ -492,7 +495,7 @@ func calculateFileHash(filepath string) (string, error) { if err != nil { return "", fmt.Errorf("unable to open file: %w", err) } - defer file.Close() + defer func() { _ = file.Close() }() hash := sha256.New() if _, err := io.Copy(hash, file); err != nil { @@ -524,7 +527,7 @@ func (r *resourceUtilitiesExtractZip) validateAndExtractZipFromURL(ctx context.C ) return nil, false, err } - defer os.Remove(tmpFile) // Ensure the temporary file is removed after extraction + defer func() { _ = os.Remove(tmpFile) }() // Ensure the temporary file is removed after extraction // Check if the destination directory exists if _, err := os.Stat(destination); os.IsNotExist(err) { @@ -588,7 +591,7 @@ func downloadZipFile(ctx context.Context, url string, diagnostics *diag.Diagnost diagnostics.AddError("HTTP Request Failed", fmt.Sprintf("Error making HTTP request to '%s': %v", url, err)) return "", err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() // Check if the response status is OK if resp.StatusCode != http.StatusOK { diff --git a/internal/provider/resource_extract_zip_test.go b/internal/provider/resource_extract_zip_test.go index 125fa27..4e6be11 100644 --- a/internal/provider/resource_extract_zip_test.go +++ b/internal/provider/resource_extract_zip_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -22,9 +25,9 @@ func TestResourceUtilitiesExtractZip(t *testing.T) { // Ensure cleanup even on test failure defer func() { - os.RemoveAll(zipDownloadDir) - os.RemoveAll(extractedDir) - os.RemoveAll(backupDir) + _ = os.RemoveAll(zipDownloadDir) + _ = os.RemoveAll(extractedDir) + _ = os.RemoveAll(backupDir) }() // Download the zip file before starting the test diff --git a/internal/provider/resource_local_directory.go b/internal/provider/resource_local_directory.go index 2b6b997..bb09af8 100644 --- a/internal/provider/resource_local_directory.go +++ b/internal/provider/resource_local_directory.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( diff --git a/internal/provider/resource_local_directory_test.go b/internal/provider/resource_local_directory_test.go index d556a88..bdda156 100644 --- a/internal/provider/resource_local_directory_test.go +++ b/internal/provider/resource_local_directory_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package provider import ( @@ -17,8 +20,8 @@ func TestResourceUtilitiesLocalDirectory(t *testing.T) { // Ensure cleanup even on test failure defer func() { - os.RemoveAll(dirPath1) - os.RemoveAll(dirPath2) + _ = os.RemoveAll(dirPath1) + _ = os.RemoveAll(dirPath2) }() resource.UnitTest(t, resource.TestCase{ diff --git a/terraform-provider-utilities b/terraform-provider-utilities new file mode 100755 index 0000000..5ba8235 Binary files /dev/null and b/terraform-provider-utilities differ diff --git a/test/main.go b/test/main.go index 7905807..a87782f 100644 --- a/test/main.go +++ b/test/main.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package main func main() {