#!/usr/bin/env zsh # local debug=${DEBUG} local playbook_filename="main.yml" local playbook_dir= local inventory= local template_dir= local template_repo= local template_repo_version= local quiet= # Bold Colors local BLUE='\x1b[34;1m' local GREEN='\x1b[32;1m' local RED='\x1b[31;1m' local NOCOLOR='\033[0m' ########################## UTILS ######################### function print_color { local color=$1 local string=$2 shift;shift; printf "%s$color$string$NOCOLOR$@" } function debug_print() { local label=$1 shift local rest="$@" [ ! "$debug" = "" ] && print_color $BLUE "$label " && print_color $NOCOLOR "$rest\n" } function fail() { print_color $RED "ERROR: " && print_color $NOCOLOR "$1\n" && exit 1 } function set_verbose() { if [ ! "$1" = "" ]; then debug="1" fi } function set_quiet() { if [ ! "$1" = "" ]; then debug= quiet="1" fi } function load_config() { local configs=( "$HOME/.hparc" $(find "$XDG_CONFIG_HOME/hpa-playbook/" -type f -maxdepth 1 -mindepth 1 -print0) "${HPA_CONFIG_DIR}" "$PWD/.hparc" ) for f in ${configs[@]}; do [ -f "$f" ] && source "$f" && debug_print "Load Config:" "sourced config: $f" done } # NOTE: Assumes playbook_dir and inventory have been setup. function run_playbook() { if [ "$quiet" = "" ]; then ansible-playbook "$playbook_dir/$playbook_filename" \ --inventory "$inventory" \ "${HPA_DEFAULT_PLAYBOOK_ARGS}" \ "$@" else ansible-playbook "$playbook_dir/$playbook_filename" \ --inventory "$inventory" \ "${HPA_DEFAULT_PLAYBOOK_ARGS}" \ "$@" 2>&1 1>/dev/null fi } function parse_opt_or_env_var { local opt=$1 local env=$2 if [ ! "$opt" = "" ]; then echo $opt elif [ ! "$env" = "" ]; then echo $env fi } function parse_playbook_dir_or_fail() { local label="Parse Playbook:" local parsed=$(parse_opt_or_env_var $1 ${HPA_PLAYBOOK_DIR}) [ "$parsed" = "" ] && fail "$label playbook directory not found." if [ -f "$parsed" ]; then playbook_dir=$(dirname "$parsed") elif [ -d "$parsed" ]; then playbook_dir=$parsed else fail "$label playbook directory not found." fi } # NOTE: assumes playbook_dir has been set. function parse_inventory_or_fail() { local label="Parse Inventory:" debug_print $label "opt: '$1'" local parsed=$(parse_opt_or_env_var $1 ${HPA_DEFAULT_INVENTORY}) debug_print $label "parsed opt or env: '$parsed'" if [ ! "$parsed" = "" ]; then [ ! -f "$parsed" ] && fail "$label inventory is not a file." debug_print $label "using parsed inventory." inventory=$parsed elif [ -f "$playbook_dir/inventory.ini" ]; then debug_print $label "using default playbook option." inventory="$playbook_dir/inventory.ini" else fail "$label failed to find inventory file." fi } # NOTE: assumes load_config has been called. function parse_template_dir() { template_dir=$(parse_opt_or_env_var $1 ${HPA_TEMPLATE_DIR}) } # NOTE: assumes load_config has been called. function parse_repo_or_fail() { local label="Parse Repo:" template_repo=$(parse_opt_or_env_var $1 ${HPA_TEMPLATE_REPO}) if [ "$template_repo" = "" ]; then fail "$label failed to find template repo." fi } # NOTE: assumes load_config has been called. function parse_repo_version_or_fail() { local label="Parse Repo Version:" template_repo_version=$(parse_opt_or_env_var $1 ${HPA_TEMPLATE_VERSION}) if [ "$template_repo_version" = "" ]; then fail "$label failed to find template repo version." fi } function setup_command() { local label=$1 local verbose=$2 local quietOpt=$3 local playbookOpt=$4 local inventoryOpt=$5 set_verbose $verbose set_quiet $quietOpt parse_playbook_dir_or_fail "$playbookOpt" parse_inventory_or_fail "$inventoryOpt" debug_print "$label" "playbook dir: '$playbook_dir'" debug_print "$label" "inventory: '$inventory'" } ########################## SUBCOMMANDS ######################### function create_project() { function usage() { cat < [playbook-args...] Where: project-dir : The directory to generate the project in (required). playbook-args : Extra arguments passed after the project directory get passed directly to the ansible-playbook command. Options: -b | --branch : The repo branch to clone (ignored if the -t option is used). -r | --repo : A template repo to use and clone into the project (ignored if the -t option is used). -t | --template : A local template / repo to use without cloning. -v | --version : The version of the repo to clone (ignored if the -t option is used). Flags: -l | --local-template-dir : Force using local template dir, generally used when config holds the information for where to locate the template directory on the system. Global Options: -i | --inventory : A custom ansible inventory file to use (optional if calling this script from the playbook directory). -p | --playbook : Path to the ansible-hpa-playbook (optional if calling this script from the playbook directory). -q | --quiet : Surpress ansible output to only errors and warnings. Global Flags: -h | --help : Show this help page. --verbose : Increase log output. Examples: # Setup a project for super-customer using a local template directory. $ hpa setup-project --template ~/projects/my-template ~/consults/super-customer --ask-vault-pass # Setup a project for super-customer using a template repo. $ hpa setup-project --repo "https://git.example.com/my-template.git" --branch "main" ~/consults/super-customer --ask-vault-pass EOF } local label="Setup Project:" local setup_args=("--tags" "setup-project") zparseopts -D -E - \ p:=playbookOpt -playbook:=playbookOpt \ i:=inventoryOpt -inventory:=inventoryOpt \ t:=template -template-dir:=template \ r:=repo -repo:=repo \ v:=version -version:=version \ b:=version -branch:=version \ h=help -help=help \ l=localTemplateDir -local-template-dir=localTemplateDir \ -verbose=verbose \ q=quietOpt -quiet=quietOpt # exit early and show help [ ! "$help" = "" ] && usage && exit 0 setup_command $label $verbose $quietOpt \ ${playbookOpt[-1]} ${inventoryOpt[-1]} local project_dir="$1" [ "$project_dir" = "" ] && fail "Setup Project: project directory not supplied" setup_args+=("--extra-vars" "project_dir=$project_dir") shift; parse_template_dir "${template[-1]}" local json= if [ ! "$localTemplateDir" = "" ]; then debug_print $label "parsed template dir: '$template_dir'" if [ "$template_dir" = "" ]; then fail "$label failed to find template directory." fi json="{'template': {'path': '"$template_dir"'}}" else parse_repo_or_fail ${repo[-1]} parse_repo_version_or_fail ${version[-1]} debug_print $label "parsed repo: '$template_repo'" debug_print $label "parsed repo version: '$template_repo_version'" json="{'template': {'repo': {'url': '"$template_repo"', 'version': '"$template_repo_version"' }}}" fi debug_print "$label" "json: $json" setup_args+=("--extra-vars" "$json") run_playbook "${setup_args[@]}" "$@" } function build_project() { function usage() { cat < [playbook-args...] Where: project-dir : The path to the project to build. playbook-args : Extra arguments passed after the project directory get passed directly to the ansible-playbook command. Options: -v | --vars-dir : Specifiy where project variables are loaded from, useful if they are not in the root of the project directory. Global Options: -i | --inventory : A custom ansible inventory file to use (optional if calling this script from the playbook directory). -p | --playbook : Path to the ansible-hpa-playbook (optional if calling this script from the playbook directory). -q | --quiet : Surpress ansible output to only errors and warnings. Global Flags: --verbose : Increase log output. Examples: # Setup a project for super-customer using a local template directory. $ hpa setup-project --template ~/projects/my-template ~/consults/super-customer --ask-vault-pass # Setup a project for super-customer using a template repo. $ hpa setup-project --repo "https://git.example.com/my-template.git" --branch "main" ~/consults/super-customer --ask-vault-pass EOF } zparseopts -D -E - \ p:=playbookOpt -playbook:=playbookOpt \ i:=inventoryOpt -inventory:=inventoryOpt \ v:=varsDir -vars-dir:=varsDir \ -verbose=verbose \ q=quietOpt -quiet=quietOpt local label="Build Project:" local build_args=("--tags" "build-project") setup_command $label $verbose $quietOpt \ ${playbookOpt[-1]} ${inventoryOpt[-1]} local project_dir=$1 [ "$project_dir" = "" ] && fail "$label did not specify project directory" shift build_args+=("--extra-vars" "project_dir=$project_dir") debug_print $label "args: $build_args" if [ ${#varsDir} -gt 1 ]; then build_args+=("--extra-vars" "project_vars_dir=${varsDir[-1]}") fi run_playbook "${build_args[@]}" "$@" } function create_project_template() { zparseopts -D -E - \ p:=playbookOpt -playbook:=playbookOpt \ i:=inventoryOpt -inventory:=inventoryOpt \ v:=varsDir -vars-dir:=varsDir w:=vaultOpt -with-vault:=vaultOpt \ -verbose=verbose \ q=quietOpt -quiet=quietOpt local label="Create Project Template:" local template_args=("--tags" "repo-template") setup_command $label $verbose $quietOpt \ ${playbookOpt[-1]} ${inventoryOpt[-1]} local output_dir=$1 [ "$output_dir" = "" ] && fail "$label did not specify output directory" shift template_args+=("--extra-vars" "output_dir=$output_dir") if [ ${#varsDir} -gt 1 ];then template_args+=("--extra-vars" "repo_vars_dir=${varsDir[-1]}") fi run_playbook "${template_args[@]}" "$@" } ########################## MAIN ######################### function main() { function usage() { cat <