...
1#!/usr/bin/env bash
2
3# Copyright The Helm Authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# The install script is based off of the MIT-licensed script from glide,
18# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get
19
20: ${BINARY_NAME:="helm"}
21: ${USE_SUDO:="true"}
22: ${DEBUG:="false"}
23: ${VERIFY_CHECKSUM:="true"}
24: ${VERIFY_SIGNATURES:="false"}
25: ${HELM_INSTALL_DIR:="/usr/local/bin"}
26: ${GPG_PUBRING:="pubring.kbx"}
27
28HAS_CURL="$(type "curl" &> /dev/null && echo true || echo false)"
29HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)"
30HAS_OPENSSL="$(type "openssl" &> /dev/null && echo true || echo false)"
31HAS_GPG="$(type "gpg" &> /dev/null && echo true || echo false)"
32HAS_GIT="$(type "git" &> /dev/null && echo true || echo false)"
33
34# initArch discovers the architecture for this system.
35initArch() {
36 ARCH=$(uname -m)
37 case $ARCH in
38 armv5*) ARCH="armv5";;
39 armv6*) ARCH="armv6";;
40 armv7*) ARCH="arm";;
41 aarch64) ARCH="arm64";;
42 x86) ARCH="386";;
43 x86_64) ARCH="amd64";;
44 i686) ARCH="386";;
45 i386) ARCH="386";;
46 esac
47}
48
49# initOS discovers the operating system for this system.
50initOS() {
51 OS=$(echo `uname`|tr '[:upper:]' '[:lower:]')
52
53 case "$OS" in
54 # Minimalist GNU for Windows
55 mingw*|cygwin*) OS='windows';;
56 esac
57}
58
59# runs the given command as root (detects if we are root already)
60runAsRoot() {
61 if [ $EUID -ne 0 -a "$USE_SUDO" = "true" ]; then
62 sudo "${@}"
63 else
64 "${@}"
65 fi
66}
67
68# verifySupported checks that the os/arch combination is supported for
69# binary builds, as well whether or not necessary tools are present.
70verifySupported() {
71 local supported="darwin-amd64\ndarwin-arm64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nlinux-s390x\nlinux-riscv64\nwindows-amd64"
72 if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then
73 echo "No prebuilt binary for ${OS}-${ARCH}."
74 echo "To build from source, go to https://github.com/helm/helm"
75 exit 1
76 fi
77
78 if [ "${HAS_CURL}" != "true" ] && [ "${HAS_WGET}" != "true" ]; then
79 echo "Either curl or wget is required"
80 exit 1
81 fi
82
83 if [ "${VERIFY_CHECKSUM}" == "true" ] && [ "${HAS_OPENSSL}" != "true" ]; then
84 echo "In order to verify checksum, openssl must first be installed."
85 echo "Please install openssl or set VERIFY_CHECKSUM=false in your environment."
86 exit 1
87 fi
88
89 if [ "${VERIFY_SIGNATURES}" == "true" ]; then
90 if [ "${HAS_GPG}" != "true" ]; then
91 echo "In order to verify signatures, gpg must first be installed."
92 echo "Please install gpg or set VERIFY_SIGNATURES=false in your environment."
93 exit 1
94 fi
95 if [ "${OS}" != "linux" ]; then
96 echo "Signature verification is currently only supported on Linux."
97 echo "Please set VERIFY_SIGNATURES=false or verify the signatures manually."
98 exit 1
99 fi
100 fi
101
102 if [ "${HAS_GIT}" != "true" ]; then
103 echo "[WARNING] Could not find git. It is required for plugin installation."
104 fi
105}
106
107# checkDesiredVersion checks if the desired version is available.
108checkDesiredVersion() {
109 if [ "x$DESIRED_VERSION" == "x" ]; then
110 # Get tag from release URL
111 local latest_release_url="https://get.helm.sh/helm-latest-version"
112 local latest_release_response=""
113 if [ "${HAS_CURL}" == "true" ]; then
114 latest_release_response=$( curl -L --silent --show-error --fail "$latest_release_url" 2>&1 || true )
115 elif [ "${HAS_WGET}" == "true" ]; then
116 latest_release_response=$( wget "$latest_release_url" -q -O - 2>&1 || true )
117 fi
118 TAG=$( echo "$latest_release_response" | grep '^v[0-9]' )
119 if [ "x$TAG" == "x" ]; then
120 printf "Could not retrieve the latest release tag information from %s: %s\n" "${latest_release_url}" "${latest_release_response}"
121 exit 1
122 fi
123 else
124 TAG=$DESIRED_VERSION
125 fi
126}
127
128# checkHelmInstalledVersion checks which version of helm is installed and
129# if it needs to be changed.
130checkHelmInstalledVersion() {
131 if [[ -f "${HELM_INSTALL_DIR}/${BINARY_NAME}" ]]; then
132 local version=$("${HELM_INSTALL_DIR}/${BINARY_NAME}" version --template="{{ .Version }}")
133 if [[ "$version" == "$TAG" ]]; then
134 echo "Helm ${version} is already ${DESIRED_VERSION:-latest}"
135 return 0
136 else
137 echo "Helm ${TAG} is available. Changing from version ${version}."
138 return 1
139 fi
140 else
141 return 1
142 fi
143}
144
145# downloadFile downloads the latest binary package and also the checksum
146# for that binary.
147downloadFile() {
148 HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz"
149 DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST"
150 CHECKSUM_URL="$DOWNLOAD_URL.sha256"
151 HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)"
152 HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST"
153 HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256"
154 echo "Downloading $DOWNLOAD_URL"
155 if [ "${HAS_CURL}" == "true" ]; then
156 curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE"
157 curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE"
158 elif [ "${HAS_WGET}" == "true" ]; then
159 wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL"
160 wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL"
161 fi
162}
163
164# verifyFile verifies the SHA256 checksum of the binary package
165# and the GPG signatures for both the package and checksum file
166# (depending on settings in environment).
167verifyFile() {
168 if [ "${VERIFY_CHECKSUM}" == "true" ]; then
169 verifyChecksum
170 fi
171 if [ "${VERIFY_SIGNATURES}" == "true" ]; then
172 verifySignatures
173 fi
174}
175
176# installFile installs the Helm binary.
177installFile() {
178 HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME"
179 mkdir -p "$HELM_TMP"
180 tar xf "$HELM_TMP_FILE" -C "$HELM_TMP"
181 HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm"
182 echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}"
183 runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME"
184 echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME"
185}
186
187# verifyChecksum verifies the SHA256 checksum of the binary package.
188verifyChecksum() {
189 printf "Verifying checksum... "
190 local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}')
191 local expected_sum=$(cat ${HELM_SUM_FILE})
192 if [ "$sum" != "$expected_sum" ]; then
193 echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting."
194 exit 1
195 fi
196 echo "Done."
197}
198
199# verifySignatures obtains the latest KEYS file from GitHub main branch
200# as well as the signature .asc files from the specific GitHub release,
201# then verifies that the release artifacts were signed by a maintainer's key.
202verifySignatures() {
203 printf "Verifying signatures... "
204 local keys_filename="KEYS"
205 local github_keys_url="https://raw.githubusercontent.com/helm/helm/main/${keys_filename}"
206 if [ "${HAS_CURL}" == "true" ]; then
207 curl -SsL "${github_keys_url}" -o "${HELM_TMP_ROOT}/${keys_filename}"
208 elif [ "${HAS_WGET}" == "true" ]; then
209 wget -q -O "${HELM_TMP_ROOT}/${keys_filename}" "${github_keys_url}"
210 fi
211 local gpg_keyring="${HELM_TMP_ROOT}/keyring.gpg"
212 local gpg_homedir="${HELM_TMP_ROOT}/gnupg"
213 mkdir -p -m 0700 "${gpg_homedir}"
214 local gpg_stderr_device="/dev/null"
215 if [ "${DEBUG}" == "true" ]; then
216 gpg_stderr_device="/dev/stderr"
217 fi
218 gpg --batch --quiet --homedir="${gpg_homedir}" --import "${HELM_TMP_ROOT}/${keys_filename}" 2> "${gpg_stderr_device}"
219 gpg --batch --no-default-keyring --keyring "${gpg_homedir}/${GPG_PUBRING}" --export > "${gpg_keyring}"
220 local github_release_url="https://github.com/helm/helm/releases/download/${TAG}"
221 if [ "${HAS_CURL}" == "true" ]; then
222 curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc"
223 curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc"
224 elif [ "${HAS_WGET}" == "true" ]; then
225 wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc"
226 wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc"
227 fi
228 local error_text="If you think this might be a potential security issue,"
229 error_text="${error_text}\nplease see here: https://github.com/helm/community/blob/master/SECURITY.md"
230 local num_goodlines_sha=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)')
231 if [[ ${num_goodlines_sha} -lt 2 ]]; then
232 echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256!"
233 echo -e "${error_text}"
234 exit 1
235 fi
236 local num_goodlines_tar=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)')
237 if [[ ${num_goodlines_tar} -lt 2 ]]; then
238 echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz!"
239 echo -e "${error_text}"
240 exit 1
241 fi
242 echo "Done."
243}
244
245# fail_trap is executed if an error occurs.
246fail_trap() {
247 result=$?
248 if [ "$result" != "0" ]; then
249 if [[ -n "$INPUT_ARGUMENTS" ]]; then
250 echo "Failed to install $BINARY_NAME with the arguments provided: $INPUT_ARGUMENTS"
251 help
252 else
253 echo "Failed to install $BINARY_NAME"
254 fi
255 echo -e "\tFor support, go to https://github.com/helm/helm."
256 fi
257 cleanup
258 exit $result
259}
260
261# testVersion tests the installed client to make sure it is working.
262testVersion() {
263 set +e
264 HELM="$(command -v $BINARY_NAME)"
265 if [ "$?" = "1" ]; then
266 echo "$BINARY_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?'
267 exit 1
268 fi
269 set -e
270}
271
272# help provides possible cli installation arguments
273help () {
274 echo "Accepted cli arguments are:"
275 echo -e "\t[--help|-h ] ->> prints this help"
276 echo -e "\t[--version|-v <desired_version>] . When not defined it fetches the latest release from GitHub"
277 echo -e "\te.g. --version v3.0.0 or -v canary"
278 echo -e "\t[--no-sudo] ->> install without sudo"
279}
280
281# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977
282cleanup() {
283 if [[ -d "${HELM_TMP_ROOT:-}" ]]; then
284 rm -rf "$HELM_TMP_ROOT"
285 fi
286}
287
288# Execution
289
290#Stop execution on any error
291trap "fail_trap" EXIT
292set -e
293
294# Set debug if desired
295if [ "${DEBUG}" == "true" ]; then
296 set -x
297fi
298
299# Parsing input arguments (if any)
300export INPUT_ARGUMENTS="${@}"
301set -u
302while [[ $# -gt 0 ]]; do
303 case $1 in
304 '--version'|-v)
305 shift
306 if [[ $# -ne 0 ]]; then
307 export DESIRED_VERSION="${1}"
308 if [[ "$1" != "v"* ]]; then
309 echo "Expected version arg ('${DESIRED_VERSION}') to begin with 'v', fixing..."
310 export DESIRED_VERSION="v${1}"
311 fi
312 else
313 echo -e "Please provide the desired version. e.g. --version v3.0.0 or -v canary"
314 exit 0
315 fi
316 ;;
317 '--no-sudo')
318 USE_SUDO="false"
319 ;;
320 '--help'|-h)
321 help
322 exit 0
323 ;;
324 *) exit 1
325 ;;
326 esac
327 shift
328done
329set +u
330
331initArch
332initOS
333verifySupported
334checkDesiredVersion
335if ! checkHelmInstalledVersion; then
336 downloadFile
337 verifyFile
338 installFile
339fi
340testVersion
341cleanup
View as plain text