-
Notifications
You must be signed in to change notification settings - Fork 456
/
tfenv-install
executable file
·259 lines (222 loc) · 9.1 KB
/
tfenv-install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#!/usr/bin/env bash
set -uo pipefail;
####################################
# Ensure we can execute standalone #
####################################
function early_death() {
echo "[FATAL] ${0}: ${1}" >&2;
exit 1;
};
if [ -z "${TFENV_ROOT:-""}" ]; then
# http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
readlink_f() {
local target_file="${1}";
local file_name;
while [ "${target_file}" != "" ]; do
cd "$(dirname ${target_file})" || early_death "Failed to 'cd \$(dirname ${target_file})' while trying to determine TFENV_ROOT";
file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TFENV_ROOT";
target_file="$(readlink "${file_name}")";
done;
echo "$(pwd -P)/${file_name}";
};
TFENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
[ -n "${TFENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TFENV_ROOT";
else
TFENV_ROOT="${TFENV_ROOT%/}";
fi;
export TFENV_ROOT;
if [ -n "${TFENV_HELPERS:-""}" ]; then
log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again';
else
[ "${TFENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh";
if source "${TFENV_ROOT}/lib/helpers.sh"; then
log 'debug' 'Helpers sourced successfully';
else
early_death "Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh";
fi;
fi;
# Ensure libexec and bin are in $PATH
for dir in libexec bin; do
case ":${PATH}:" in
*:${TFENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TFENV_ROOT}/${dir}', not adding it again";;
*)
log 'debug' "\$PATH does not contain '${TFENV_ROOT}/${dir}', prepending and exporting it now";
export PATH="${TFENV_ROOT}/${dir}:${PATH}";
;;
esac;
done;
#####################
# Begin Script Body #
#####################
[ "${#}" -gt 1 ] && log 'error' 'usage: tfenv install [<version>]';
declare requested="${1:-""}";
log debug "Resolving version with: tfenv-resolve-version ${requested}";
declare resolved="$(tfenv-resolve-version ${requested})";
declare version="${resolved%%\:*}";
declare regex="${resolved##*\:}";
[ -n "${version}" ] || log 'error' 'Version is not specified. This should not be possible as we default to latest';
log 'debug' "Processing install for version ${version}, using regex ${regex}";
version="$(tfenv-list-remote | grep -e "${regex}" | head -n 1)";
[ -n "${version}" ] || log 'error' "No versions matching '${requested}' found in remote";
dst_path="${TFENV_ROOT}/versions/${version}";
if [ -f "${dst_path}/terraform" ]; then
echo "Terraform v${version} is already installed";
exit 0;
fi;
TFENV_ARCH="${TFENV_ARCH:-amd64}";
case "$(uname -s)" in
Darwin*)
os="darwin_${TFENV_ARCH}";
;;
MINGW64*)
os="windows_${TFENV_ARCH}";
;;
MSYS_NT*)
os="windows_${TFENV_ARCH}";
;;
CYGWIN_NT*)
os="windows_${TFENV_ARCH}";
;;
FreeBSD*)
os="freebsd_${TFENV_ARCH}"
;;
*)
os="linux_${TFENV_ARCH}";
;;
esac;
keybase_bin="$(command -v keybase 2>/dev/null)";
shasum_bin="$(command -v shasum 2>/dev/null)";
sha256sum_bin="$(command -v sha256sum 2>/dev/null)";
TFENV_REMOTE="${TFENV_REMOTE:-https://releases.hashicorp.com}";
version_url="${TFENV_REMOTE}/terraform/${version}";
# Thanks for the inconsistency in 0.12-alpha, Hashicorp(!)
if [[ "${version}" =~ 0.12.0-alpha[3-9] ]]; then
tarball_name="terraform_${version}_terraform_${version}_${os}.zip";
else
tarball_name="terraform_${version}_${os}.zip";
fi;
shasums_name="terraform_${version}_SHA256SUMS";
log 'info' "Installing Terraform v${version}";
# Create a local temporary directory for downloads
download_tmp="$(mktemp -d tfenv_download.XXXXXX)" || log 'error' "Unable to create temporary download directory in $(pwd)";
# Clean it up in case of error
trap "rm -rf ${download_tmp}" EXIT;
declare curl_progress="";
case "${TFENV_CURL_OUTPUT:-2}" in
'2')
log 'debug' 'Setting curl progress bar with "-#"';
curl_progress="-#";
;;
'1')
log 'debug' 'Using default curl output';
curl_progress="";
;;
'0')
log 'debug' 'Running curl silently with "-s"';
curl_progress="-s";
;;
*)
log 'error' 'TFENV_CURL_OUTPUT specified, but not with a supported value ([0,1,2])';
;;
esac;
log 'info' "Downloading release tarball from ${version_url}/${tarball_name}";
curlw ${curl_progress} -f -o "${download_tmp}/${tarball_name}" "${version_url}/${tarball_name}" || log 'error' 'Tarball download failed';
log 'info' "Downloading SHA hash file from ${version_url}/${shasums_name}";
curlw -s -f -o "${download_tmp}/${shasums_name}" "${version_url}/${shasums_name}" || log 'error' 'SHA256 hashes download failed';
download_signature() {
log 'info' "Downloading SHA hash signature file from ${version_url}/${shasums_name}.sig";
curlw -s -f \
-o "${download_tmp}/${shasums_name}.sig" \
"${version_url}/${shasums_name}.sig" \
&& log 'debug' "SHA256SUMS signature file downloaded successfully to ${download_tmp}/${shasums_name}.sig" \
|| log 'error' 'SHA256SUMS signature download failed';
};
# If on MacOS with Homebrew, use GNU grep
# This allows keybase login detection to work on Mac
if [[ $(uname) == 'Darwin' ]] && [ $(which brew) ]; then
if ! [ $(which ggrep) ]; then
brew install grep;
fi
alias grep=ggrep;
fi
# Verify signature if verification mechanism (keybase, gpg, etc) is present
if [[ -f "${TFENV_ROOT}/use-gnupg" ]]; then
# GnuPG uses the user's keyring, and any web-of-trust or local signatures or
# anything else they have setup. This is the crazy-powerful mode which is
# overly confusing to newcomers. We don't support it without the user creating
# the file use-gnupg, optionally with directives in it.
gnupg_command="$(sed -E -n -e 's/^binary: *//p' <"${TFENV_ROOT}/use-gnupg")";
[[ -n "${gnupg_command}" ]] || gnupg_command=gpg;
download_signature;
# Deliberately unquoted command, in case caller has something fancier in "use-gnupg".
# Also, don't use batch mode. If someone specifies GnuPG, let them deal with any prompting.
${gnupg_command} \
--verify "${download_tmp}/${shasums_name}.sig" \
"${download_tmp}/${shasums_name}" \
|| log 'error' 'PGP signature rejected by GnuPG';
elif [[ -f "${TFENV_ROOT}/use-gpgv" ]]; then
# gpgv is a much simpler interface to verification, but does require that the
# key have been downloaded and marked trusted.
# We don't force the caller to trust the tfenv repo's copy of their key, they
# have to choose to make that trust decision.
gpgv_command="$(sed -E -n -e 's/^binary: *//p' <"${TFENV_ROOT}/use-gpgv")";
trust_tfenv="$(sed -E -n -e 's/^trust.?tfenv: *//p' <"${TFENV_ROOT}/use-gpgv")";
[[ -n "${gpgv_command}" ]] || gpgv_command=gpgv;
download_signature;
if [[ "${trust_tfenv}" == 'yes' ]]; then
${gpgv_command} \
--keyring "${TFENV_ROOT}/share/hashicorp-keys.pgp" \
"${download_tmp}/${shasums_name}.sig" \
"${download_tmp}/${shasums_name}" \
|| log 'error' 'PGP signature rejected';
else
${gpgv_command} \
"${download_tmp}/${shasums_name}.sig" \
"${download_tmp}/${shasums_name}" \
|| log 'error' 'PGP signature rejected';
fi;
elif [[ -n "${keybase_bin}" && -x "${keybase_bin}" ]]; then
grep -Eq '^Logged in:[[:space:]]*yes' <("${keybase_bin}" status);
keybase_logged_in="${?}";
grep -Fq hashicorp <("${keybase_bin}" list-following);
keybase_following_hc="${?}";
if [[ "${keybase_logged_in}" -ne 0 || "${keybase_following_hc}" -ne 0 ]]; then
log 'warn' 'Unable to verify OpenPGP signature unless logged into keybase and following hashicorp';
else
download_signature;
"${keybase_bin}" pgp verify \
-S hashicorp \
-d "${download_tmp}/${shasums_name}.sig" \
-i "${download_tmp}/${shasums_name}" \
&& log 'debug' 'SHA256SUMS signature matched' \
|| log 'error' 'SHA256SUMS signature does not match!';
fi;
else
# Warning about this avoids an unwarranted sense of confidence in the SHA check
log 'warn' 'No keybase install found, skipping OpenPGP signature verification';
fi;
if [[ -n "${shasum_bin}" && -x "${shasum_bin}" ]]; then
(
cd "${download_tmp}";
"${shasum_bin}" \
-a 256 \
-s \
-c <(grep -F "${tarball_name}" "${shasums_name}")
) || log 'error' 'SHA256 hash does not match!';
elif [[ -n "${sha256sum_bin}" && -x "${sha256sum_bin}" ]]; then
(
cd "${download_tmp}";
"${sha256sum_bin}" \
-c <(grep -F "${tarball_name}" "${shasums_name}")
) || log 'error' 'SHA256 hash does not match!';
else
# Lack of shasum deserves a proper warning
log 'warn' 'No shasum tool available. Skipping SHA256 hash validation';
fi;
mkdir -p "${dst_path}" || log 'error' "Failed to make directory ${dst_path}";
declare unzip_output;
unzip_output="$(unzip -o "${download_tmp}/${tarball_name}" -d "${dst_path}")" || log 'error' 'Tarball unzip failed';
while IFS= read -r unzip_line; do
log 'info' "${unzip_line}";
done < <(printf '%s\n' "${unzip_output}");
log 'info' "Installation of terraform v${version} successful. To make this your default version, run 'tfenv use ${version}'";