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
20PROJECT_NAME="helm"
21TILLER_NAME="tiller"
22
23: ${USE_SUDO:="true"}
24: ${HELM_INSTALL_DIR:="/usr/local/bin"}
25
26# initArch discovers the architecture for this system.
27initArch() {
28  ARCH=$(uname -m)
29  case $ARCH in
30    armv5*) ARCH="armv5";;
31    armv6*) ARCH="armv6";;
32    armv7*) ARCH="arm";;
33    aarch64) ARCH="arm64";;
34    x86) ARCH="386";;
35    x86_64) ARCH="amd64";;
36    i686) ARCH="386";;
37    i386) ARCH="386";;
38  esac
39}
40
41# initOS discovers the operating system for this system.
42initOS() {
43  OS=$(echo `uname`|tr '[:upper:]' '[:lower:]')
44
45  case "$OS" in
46    # Minimalist GNU for Windows
47    mingw*) OS='windows';;
48  esac
49}
50
51# runs the given command as root (detects if we are root already)
52runAsRoot() {
53  local CMD="$*"
54
55  if [ $EUID -ne 0 -a $USE_SUDO = "true" ]; then
56    CMD="sudo $CMD"
57  fi
58
59  $CMD
60}
61
62# verifySupported checks that the os/arch combination is supported for
63# binary builds.
64verifySupported() {
65  local supported="darwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nwindows-amd64"
66  if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then
67    echo "No prebuilt binary for ${OS}-${ARCH}."
68    echo "To build from source, go to https://github.com/helm/helm"
69    exit 1
70  fi
71
72  if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then
73    echo "Either curl or wget is required"
74    exit 1
75  fi
76}
77
78# checkDesiredVersion checks if the desired version is available.
79checkDesiredVersion() {
80  if [ "x$DESIRED_VERSION" == "x" ]; then
81    # Get tag from release URL
82    local release_url="https://github.com/helm/helm/releases"
83    if type "curl" > /dev/null; then
84
85      TAG=$(curl -Ls $release_url | grep 'href="/helm/helm/releases/tag/v2.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}')
86    elif type "wget" > /dev/null; then
87      TAG=$(wget $release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v2.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}')
88    fi
89  else
90    TAG=$DESIRED_VERSION
91  fi
92}
93
94# checkHelmInstalledVersion checks which version of helm is installed and
95# if it needs to be changed.
96checkHelmInstalledVersion() {
97  if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then
98    local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version -c | grep '^Client' | cut -d'"' -f2)
99    if [[ "$version" == "$TAG" ]]; then
100      echo "Helm ${version} is already ${DESIRED_VERSION:-latest}"
101      return 0
102    else
103      echo "Helm ${TAG} is available. Changing from version ${version}."
104      return 1
105    fi
106  else
107    return 1
108  fi
109}
110
111# downloadFile downloads the latest binary package and also the checksum
112# for that binary.
113downloadFile() {
114  HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz"
115  DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST"
116  CHECKSUM_URL="$DOWNLOAD_URL.sha256"
117  HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)"
118  HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST"
119  HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256"
120  echo "Downloading $DOWNLOAD_URL"
121  if type "curl" > /dev/null; then
122    curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE"
123  elif type "wget" > /dev/null; then
124    wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL"
125  fi
126  if type "curl" > /dev/null; then
127    curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE"
128  elif type "wget" > /dev/null; then
129    wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL"
130  fi
131}
132
133# installFile verifies the SHA256 for the file, then unpacks and
134# installs it.
135installFile() {
136  HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME"
137  local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}')
138  local expected_sum=$(cat ${HELM_SUM_FILE})
139  if [ "$sum" != "$expected_sum" ]; then
140    echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting."
141    exit 1
142  fi
143
144  mkdir -p "$HELM_TMP"
145  tar xf "$HELM_TMP_FILE" -C "$HELM_TMP"
146  HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME"
147  TILLER_TMP_BIN="$HELM_TMP/$OS-$ARCH/$TILLER_NAME"
148  echo "Preparing to install $PROJECT_NAME and $TILLER_NAME into ${HELM_INSTALL_DIR}"
149  runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR"
150  echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME"
151  if [ -x "$TILLER_TMP_BIN" ]; then
152    runAsRoot cp "$TILLER_TMP_BIN" "$HELM_INSTALL_DIR"
153    echo "$TILLER_NAME installed into $HELM_INSTALL_DIR/$TILLER_NAME"
154  else
155    echo "info: $TILLER_NAME binary was not found in this release; skipping $TILLER_NAME installation"
156  fi
157}
158
159# fail_trap is executed if an error occurs.
160fail_trap() {
161  result=$?
162  if [ "$result" != "0" ]; then
163    if [[ -n "$INPUT_ARGUMENTS" ]]; then
164      echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS"
165      help
166    else
167      echo "Failed to install $PROJECT_NAME"
168    fi
169    echo -e "\tFor support, go to https://github.com/helm/helm."
170  fi
171  cleanup
172  exit $result
173}
174
175# testVersion tests the installed client to make sure it is working.
176testVersion() {
177  set +e
178  HELM="$(command -v $PROJECT_NAME)"
179  if [ "$?" = "1" ]; then
180    echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?'
181    exit 1
182  fi
183  set -e
184  echo "Run '$PROJECT_NAME init' to configure $PROJECT_NAME."
185}
186
187# help provides possible cli installation arguments
188help () {
189  echo "Accepted cli arguments are:"
190  echo -e "\t[--help|-h ] ->> prints this help"
191  echo -e "\t[--version|-v <desired_version>]"
192  echo -e "\te.g. --version v2.4.0  or -v latest"
193  echo -e "\t[--no-sudo]  ->> install without sudo"
194}
195
196# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977
197cleanup() {
198  if [[ -d "${HELM_TMP_ROOT:-}" ]]; then
199    rm -rf "$HELM_TMP_ROOT"
200  fi
201}
202
203# Execution
204
205#Stop execution on any error
206trap "fail_trap" EXIT
207set -e
208
209# Parsing input arguments (if any)
210export INPUT_ARGUMENTS="${@}"
211set -u
212while [[ $# -gt 0 ]]; do
213  case $1 in
214    '--version'|-v)
215       shift
216       if [[ $# -ne 0 ]]; then
217           export DESIRED_VERSION="${1}"
218       else
219           echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest"
220           exit 0
221       fi
222       ;;
223    '--no-sudo')
224       USE_SUDO="false"
225       ;;
226    '--help'|-h)
227       help
228       exit 0
229       ;;
230    *) exit 1
231       ;;
232  esac
233  shift
234done
235set +u
236
237initArch
238initOS
239verifySupported
240checkDesiredVersion
241if ! checkHelmInstalledVersion; then
242  downloadFile
243  installFile
244fi
245testVersion
246cleanup
247