Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add new hook for terraform providers lock operation #173

Merged
merged 4 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@
files: (\.tf|\.tfvars)$
exclude: \.terraform\/.*$

- id: terraform_providers_lock
name: Lock terraform provider versions
description: Updates provider signatures in dependency lock files.
require_serial: true
entry: terraform_providers_lock.sh
language: script
files: (\.terraform\.lock\.hcl)$
exclude: \.terraform\/.*$

- id: terraform_tflint
name: Terraform validate with tflint
description: Validates all Terraform configuration files with TFLint.
Expand Down
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* [checkov](#checkov)
* [terraform_docs](#terraform_docs)
* [terraform_docs_replace](#terraform_docs_replace)
* [terraform_providers_lock](#terraform_providers_lock)
* [terraform_tflint](#terraform_tflint)
* [terraform_tfsec](#terraform_tfsec)
* [terraform_validate](#terraform_validate)
Expand Down Expand Up @@ -183,6 +184,7 @@ There are several [pre-commit](https://pre-commit.com/) hooks to keep Terraform
| `terraform_docs_without_aggregate_type_defaults` | Inserts input and output documentation into `README.md` without aggregate type defaults. Hook notes same as for [terraform_docs](#terraform_docs) |
| `terraform_docs` | Inserts input and output documentation into `README.md`. Recommended. [Hook notes](#terraform_docs) |
| `terraform_fmt` | Rewrites all Terraform configuration files to a canonical format. [Hook notes](#terraform_docs) |
| `terraform_providers_lock` | Updates provider signatures in [dependency lock files](https://www.terraform.io/docs/cli/commands/providers/lock.html). [Hook notes](#terraform_providers_lock)
| `terraform_tflint` | Validates all Terraform configuration files with [TFLint](https://github.com/terraform-linters/tflint). [Available TFLint rules](https://github.com/terraform-linters/tflint/tree/master/docs/rules#rules). [Hook notes](#terraform_tflint). |
| `terraform_tfsec` | [TFSec](https://github.com/liamg/tfsec) static analysis of terraform templates to spot potential security issues. [Hook notes](#terraform_tfsec) |
| `terraform_validate` | Validates all Terraform configuration files. [Hook notes](#terraform_validate) |
Expand Down Expand Up @@ -328,7 +330,48 @@ Example:

`terraform_validate` hook will try to reinitialize them before running `terraform validate` command.

**Warning:** If you use Terraform workspaces, DO NOT use this workaround ([details](https://github.com/antonbabenko/pre-commit-terraform/issues/203#issuecomment-918791847)). Wait to [`force-init`](https://github.com/antonbabenko/pre-commit-terraform/issues/224) option implementation
**Warning:** If you use Terraform workspaces, DO NOT use this workaround ([details](https://github.com/antonbabenko/pre-commit-terraform/issues/203#issuecomment-918791847)). Wait to [`force-init`](https://github.com/antonbabenko/pre-commit-terraform/issues/224) option implementation

### terraform_providers_lock

1. The hook requires Terraform 0.14 or later.

1. The hook invokes two operations that can be really slow:
`terraform init` (in case `.terraform` directory is not initialised)
and `terraform providers lock`. Both operations require downloading
data from remote Terraform registries, and not all of that
downloaded data or meta-data is currently being cached by Terraform.

1. `terraform_providers_lock` supports custom arguments.

Example:

```yaml
hooks:
- id: terraform_providers_lock
args: ['--args=-platform=windows_amd64']
```

In order to pass multiple args, try the following:

```yaml
- id: terraform_providers_lock
args:
- '--args=-platform=windows_amd64'
- '--args=-platform=darwin_amd64'
```

1. It may happen that Terraform working directory (`.terraform`) already exists but is outdated
(e.g. not initialized modules, wrong version of Terraform, etc).
To solve this problem you can find and delete all `.terraform` directories in your repository using this command:

```shell
find . -type d -name .terraform -prune -print -exec rm -rf {} \;
```

`terraform_providers_lock` hook will try to reinitialize them before running `terraform providers lock` command.

## Notes for developers

## Notes for contributors

Expand Down
88 changes: 88 additions & 0 deletions terraform_providers_lock.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env bash

set -eo pipefail

main() {
initialize_
parse_cmdline_ "$@"
terraform_providers_lock_
}

initialize_() {
# get directory containing this script
local dir
local source
source="${BASH_SOURCE[0]}"
while [[ -L $source ]]; do # resolve $source until the file is no longer a symlink
dir="$(cd -P "$(dirname "$source")" > /dev/null && pwd)"
source="$(readlink "$source")"
# if $source was a relative symlink, we need to resolve it relative to the path where the symlink file was located
[[ $source != /* ]] && source="$dir/$source"
done
_SCRIPT_DIR="$(dirname "$source")"

# source getopt function
# shellcheck source=lib_getopt
. "$_SCRIPT_DIR/lib_getopt"
}

parse_cmdline_() {
declare argv
argv=$(getopt -o a: --long args: -- "$@") || return
eval "set -- $argv"

for argv; do
case $argv in
-a | --args)
shift
ARGS+=("$1")
shift
;;
--)
shift
FILES=("$@")
break
;;
esac
done
}

terraform_providers_lock_() {
local -a paths
local index=0
local file_with_path

for file_with_path in "${FILES[@]}"; do
file_with_path="${file_with_path// /__REPLACED__SPACE__}"

paths[index]=$(dirname "$file_with_path")

((index += 1))
done

local path_uniq
for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do
path_uniq="${path_uniq//__REPLACED__SPACE__/ }"

if [[ ! -d "${path_uniq}/.terraform" ]]; then
set +e
init_output=$(terraform -chdir="${path_uniq}" init -backend=false 2>&1)
init_code=$?
set -e

if [[ $init_code != 0 ]]; then
echo "Init before validation failed: $path_uniq"
echo "$init_output"
exit 1
fi
fi

terraform -chdir="${path_uniq}" providers lock "${ARGS[@]}"
done
}

# global arrays
declare -a ARGS
declare -a FILES

[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@"