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

Enhancement of the Packer FMT + Packer Validate Hooks #4

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
22 changes: 10 additions & 12 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
---
- id: packer_fmt
name: Packer fmt
description: Rewrites all Packer configuration files to a canonical format.
entry: hooks/packer_fmt.sh
language: script
files: (packer\.json|\.pkr\.hcl|\.pkrvars\.hcl)$

- id: packer_validate
name: Packer Validate
description: This hook runs `packer validate` on appropriate files.
name: Packer validate
description: Validates all Packer configuration files.
require_serial: true
entry: hooks/packer_validate.sh
language: script
files: (packer\.json|\.pkr\.hcl)$
pass_filenames: true
always_run: true

- id: packer_fmt
name: Packer Format
description: This hook runs `packer fmt` on appropriate files.
entry: hooks/packer_fmt.sh
language: script
files: (\.pkr\.hcl)$
pass_filenames: true
71 changes: 63 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ projects using [Packer](https://www.packer.io/).

## Available Hooks ##

| Hook name | Description |
| ----------------- | ------------------------------------------------------- |
| `packer_validate` | Validate all Packer templates. |
| `packer_fmt` | Check that Packer HCL templates are properly formatted. |
| Hook name | Description |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `packer_fmt` | Rewrites all Packer configuration files to a canonical format. |
| `packer_validate` | Validates all Packer configuration files. |

## Usage ##

Expand All @@ -19,16 +19,71 @@ repos:
- repo: https://github.com/cisagov/pre-commit-packer
rev: <version> # Version from https://github.com/cisagov/pre-commit-packer/releases
hooks:
- id: packer_fmt
- id: packer_validate
args:
- manual_file_entry
- id: packer_fmt
- '--args=--var-file=inputs/dev.pkrvars.hcl'
```

## Notes about the `packer_fmt` hook ##

This hook scans any files in the packer configuration ending in `packer.json`,`.pkr.hcl`
and `.pkrvars.hcl` and applies packer formatting.

## Notes about the `packer_validate` hook ##

This hook matches any paths ending in `packer.json` and `.pkr.hcl` by default.
File paths can be added for checking manually as additional arguments.
1. `packer_validate` supports custom arguments so you can pass supported
no-color or json flags.

1. Example:

```yaml
hooks:
- id: packer_validate
args: ['--args=-json']
```

In order to pass multiple args, try the following:

```yaml
- id: packer_validate
args:
- '--args=-json'
- '--args=-no-color'
```

1. `packer_validate` also supports custom environment variables passed to
the pre-commit runtime

1. Example:

```yaml
hooks:
- id: packer_validate
args: ['--envs=AWS_DEFAULT_REGION="us-west-2"']
```

In order to pass multiple args, try the following:

```yaml
- id: packer_validate
args:
- '--envs=AWS_DEFAULT_REGION="us-west-2"'
- '--envs=AWS_ACCESS_KEY_ID="anaccesskey"'
- '--envs=AWS_SECRET_ACCESS_KEY="asecretkey"'
```

1. `packer_validate` also supports custom arguments allowing to choose
the input pkrvars.hcl passed to the pre-commit runtime to validate your packer configuration

1. Example:

```yaml
hooks:
- id: packer_validate
args:
- '--args=--var-file=inputs/dev.pkrvars.hcl'
```

## Contributing ##

Expand Down
43 changes: 21 additions & 22 deletions hooks/packer_fmt.sh
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
#!/usr/bin/env bash
set -e

set -o nounset
set -o errexit
set -o pipefail
declare -a paths

if [ -z "$(command -v packer)" ]
then
echo "packer is required"
exit 1
fi
index=0

error=0
for file_with_path in "$@"; do
file_with_path="${file_with_path// /__REPLACED__SPACE__}"

for file in "$@"
do
if ! packer fmt -check "$file"
then
error=1
echo
echo "Failed path: $file"
echo "================================"
fi
paths[index]=$(dirname "$file_with_path")
(("index+=1"))
done

if [[ $error -ne 0 ]]
then
exit 1
fi
for path_uniq in $(echo "${paths[*]}" | tr ' ' '\n' | sort -u); do
path_uniq="${path_uniq//__REPLACED__SPACE__/ }"

pushd "$path_uniq" > /dev/null
packer fmt "$path_uniq"
popd > /dev/null
done

# *.pkrvars.hcl not located in the main directory are excluded by `packer fmt`
IFS=
pkrvars_dir=$(dirname "$(find . -path ./git -prune -false -o -name '*.pkrvars.hcl' -print -quit)")
echo "$pkrvars_dir"
cd "$pkrvars_dir"
packer fmt .
132 changes: 107 additions & 25 deletions hooks/packer_validate.sh
Original file line number Diff line number Diff line change
@@ -1,29 +1,111 @@
#!/usr/bin/env bash
set -eo pipefail

set -o nounset
set -o errexit
set -o pipefail

if [ -z "$(command -v packer)" ]
then
echo "packer is required"
exit 1
fi

error=0

for file in "$@"
do
if ! packer validate "$file"
then
error=1
echo
echo "Failed path: $file"
echo "================================"
main() {
initialize_
parse_cmdline_ "$@"
packer_validate_
}

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
# shellcheck disable=SC1091
. "$_SCRIPT_DIR/lib_getopt"
}

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

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

packer_validate_() {

# Setup environment variables
local var var_name var_value
for var in "${ENVS[@]}"; do
var_name="${var%%=*}"
var_value="${var#*=}"
# shellcheck disable=SC2086
export $var_name="$var_value"
done

declare -a paths
local index=0
local error=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 [[ -n "$(find "$path_uniq" -maxdepth 1 -name '*.pkr.hcl' -print)" ]]; then

pushd "$(realpath "$path_uniq")" > /dev/null
set +e
validate_output=$(packer validate "${ARGS[@]}" . 2>&1)
validate_code=$?
set -e

if [[ $validate_code != 0 ]]; then
error=1
echo "Validation failed: $path_uniq"
echo "$validate_output"
echo
fi

popd > /dev/null
fi
done

if [[ $error -ne 0 ]]; then
exit 1
fi
done
}

# global arrays
declare -a ARGS
declare -a ENVS
declare -a FILES

if [[ $error -ne 0 ]]
then
exit 1
fi
[[ ${BASH_SOURCE[0]} != "$0" ]] || main "$@"
Loading