Skip to content

Commit

Permalink
Update kubectx-kubens to install Go versions (fixes #459) (#499)
Browse files Browse the repository at this point in the history
* Update kubectx-kubens to install Go versions (fixes #459)

* Fix typo
  • Loading branch information
natescherer authored Aug 20, 2023
1 parent 3f9164f commit 4d15b67
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 63 deletions.
2 changes: 1 addition & 1 deletion src/kubectx-kubens/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Kubectx and Kubens (via Github Releases)",
"id": "kubectx-kubens",
"version": "1.0.2",
"version": "1.0.3",
"description": "kubectx is a tool to switch between contexts (clusters) on kubectl faster. kubens is a tool to switch between Kubernetes namespaces (and configure them for kubectl) easily.",
"documentationURL": "https://github.com/devcontainers-contrib/features/tree/main/src/kubectx-kubens",
"installsAfter": [
Expand Down
79 changes: 20 additions & 59 deletions src/kubectx-kubens/install.sh
Original file line number Diff line number Diff line change
@@ -1,72 +1,33 @@
#!/usr/bin/env bash

KUBCTX_KUBENS_VERSION="${VERSION:-"latest"}"
KUBECTX_KUBENS_VERSION="${VERSION:-"latest"}"

set -e

# Clean up
rm -rf /var/lib/apt/lists/*
source ./library_scripts.sh

# nanolayer is a cli utility which keeps container layers as small as possible
# source code: https://github.com/devcontainers-contrib/nanolayer
# `ensure_nanolayer` is a bash function that will find any existing nanolayer installations,
# and if missing - will download a temporary copy that automatically get deleted at the end
# of the script
ensure_nanolayer nanolayer_location "v0.4.45"

if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi

# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" >/dev/null 2>&1; then
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update -y
fi
apt-get -y install --no-install-recommends "$@"
fi
}

# Figure out correct version of a three part version number is not passed
find_version_from_git_tags() {
local variable_name=$1
local requested_version=${!variable_name}
if [ "${requested_version}" = "none" ]; then return; fi
local repository=$2
local prefix=${3:-"tags/v"}
local separator=${4:-"."}
local last_part_optional=${5:-"false"}
if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
local escaped_separator=${separator//./\\.}
local last_part
if [ "${last_part_optional}" = "true" ]; then
last_part="(${escaped_separator}[0-9]+)?"
else
last_part="${escaped_separator}[0-9]+"
fi
local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$"
local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)"
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then
declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)"
else
set +e
declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
set -e
fi
fi
if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" >/dev/null 2>&1; then
echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2
exit 1
fi
echo "${variable_name}=${!variable_name}"
}

find_version_from_git_tags KUBCTX_KUBENS_VERSION 'https://github.com/ahmetb/kubectx'

check_packages ca-certificates curl

curl -L -o /usr/local/bin/kubectx https://raw.githubusercontent.com/ahmetb/kubectx/v${KUBCTX_KUBENS_VERSION}/kubectx &&
curl -L -o /usr/local/bin/kubens https://raw.githubusercontent.com/ahmetb/kubectx/v${KUBCTX_KUBENS_VERSION}/kubens &&
chmod +x /usr/local/bin/kubectx &&
chmod +x /usr/local/bin/kubens

# Clean up
rm -rf /var/lib/apt/lists/*
$nanolayer_location \
install \
devcontainer-feature \
"ghcr.io/devcontainers-contrib/features/gh-release:1" \
--option repo='ahmetb/kubectx' --option binaryNames='kubectx' --option version="$KUBECTX_KUBENS_VERSION" --option assetRegex="kubectx.*"

$nanolayer_location \
install \
devcontainer-feature \
"ghcr.io/devcontainers-contrib/features/gh-release:1" \
--option repo='ahmetb/kubectx' --option binaryNames='kubens' --option version="$KUBECTX_KUBENS_VERSION" --option assetRegex="kubens.*"

echo "Done!"
179 changes: 179 additions & 0 deletions src/kubectx-kubens/library_scripts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#!/bin/bash -i


clean_download() {
# The purpose of this function is to download a file with minimal impact on contaier layer size
# this means if no valid downloader is found (curl or wget) then we install a downloader (currently wget) in a
# temporary manner, and making sure to
# 1. uninstall the downloader at the return of the function
# 2. revert back any changes to the package installer database/cache (for example apt-get lists)
# The above steps will minimize the leftovers being created while installing the downloader
# Supported distros:
# debian/ubuntu/alpine

url=$1
output_location=$2
tempdir=$(mktemp -d)
downloader_installed=""

function _apt_get_install() {
tempdir=$1

# copy current state of apt list - in order to revert back later (minimize contianer layer size)
cp -p -R /var/lib/apt/lists $tempdir
apt-get update -y
apt-get -y install --no-install-recommends wget ca-certificates
}

function _apt_get_cleanup() {
tempdir=$1

echo "removing wget"
apt-get -y purge wget --auto-remove

echo "revert back apt lists"
rm -rf /var/lib/apt/lists/*
rm -r /var/lib/apt/lists && mv $tempdir/lists /var/lib/apt/lists
}

function _apk_install() {
tempdir=$1
# copy current state of apk cache - in order to revert back later (minimize contianer layer size)
cp -p -R /var/cache/apk $tempdir

apk add --no-cache wget
}

function _apk_cleanup() {
tempdir=$1

echo "removing wget"
apk del wget
}
# try to use either wget or curl if one of them already installer
if type curl >/dev/null 2>&1; then
downloader=curl
elif type wget >/dev/null 2>&1; then
downloader=wget
else
downloader=""
fi

# in case none of them is installed, install wget temporarly
if [ -z $downloader ] ; then
if [ -x "/usr/bin/apt-get" ] ; then
_apt_get_install $tempdir
elif [ -x "/sbin/apk" ] ; then
_apk_install $tempdir
else
echo "distro not supported"
exit 1
fi
downloader="wget"
downloader_installed="true"
fi

if [ $downloader = "wget" ] ; then
wget -q $url -O $output_location
else
curl -sfL $url -o $output_location
fi

# NOTE: the cleanup procedure was not implemented using `trap X RETURN` only because
# alpine lack bash, and RETURN is not a valid signal under sh shell
if ! [ -z $downloader_installed ] ; then
if [ -x "/usr/bin/apt-get" ] ; then
_apt_get_cleanup $tempdir
elif [ -x "/sbin/apk" ] ; then
_apk_cleanup $tempdir
else
echo "distro not supported"
exit 1
fi
fi

}


ensure_nanolayer() {
# Ensure existance of the nanolayer cli program
local variable_name=$1

local required_version=$2
# normalize version
if ! [[ $required_version == v* ]]; then
required_version=v$required_version
fi

local nanolayer_location=""

# If possible - try to use an already installed nanolayer
if [[ -z "${NANOLAYER_FORCE_CLI_INSTALLATION}" ]]; then
if [[ -z "${NANOLAYER_CLI_LOCATION}" ]]; then
if type nanolayer >/dev/null 2>&1; then
echo "Found a pre-existing nanolayer in PATH"
nanolayer_location=nanolayer
fi
elif [ -f "${NANOLAYER_CLI_LOCATION}" ] && [ -x "${NANOLAYER_CLI_LOCATION}" ] ; then
nanolayer_location=${NANOLAYER_CLI_LOCATION}
echo "Found a pre-existing nanolayer which were given in env variable: $nanolayer_location"
fi

# make sure its of the required version
if ! [[ -z "${nanolayer_location}" ]]; then
local current_version
current_version=$($nanolayer_location --version)
if ! [[ $current_version == v* ]]; then
current_version=v$current_version
fi

if ! [ $current_version == $required_version ]; then
echo "skipping usage of pre-existing nanolayer. (required version $required_version does not match existing version $current_version)"
nanolayer_location=""
fi
fi

fi

# If not previuse installation found, download it temporarly and delete at the end of the script
if [[ -z "${nanolayer_location}" ]]; then

if [ "$(uname -sm)" == "Linux x86_64" ] || [ "$(uname -sm)" == "Linux aarch64" ]; then
tmp_dir=$(mktemp -d -t nanolayer-XXXXXXXXXX)

clean_up () {
ARG=$?
rm -rf $tmp_dir
exit $ARG
}
trap clean_up EXIT


if [ -x "/sbin/apk" ] ; then
clib_type=musl
else
clib_type=gnu
fi

tar_filename=nanolayer-"$(uname -m)"-unknown-linux-$clib_type.tgz

# clean download will minimize leftover in case a downloaderlike wget or curl need to be installed
clean_download https://github.com/devcontainers-contrib/cli/releases/download/$required_version/$tar_filename $tmp_dir/$tar_filename

tar xfzv $tmp_dir/$tar_filename -C "$tmp_dir"
chmod a+x $tmp_dir/nanolayer
nanolayer_location=$tmp_dir/nanolayer


else
echo "No binaries compiled for non-x86-linux architectures yet: $(uname -m)"
exit 1
fi
fi

# Expose outside the resolved location
declare -g ${variable_name}=$nanolayer_location

}


6 changes: 3 additions & 3 deletions test/kubectx-kubens/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ set -e

source dev-container-features-test-lib

# we are simply checking existance for now.
# we are simply checking version for now.
# full operability depends on the existance of kubectl.
check "kubectx existance" which kubectx
check "kubens existance" which kubens
check "kubectx version" kubectx --version
check "kubens version" kubens --version

reportResults

0 comments on commit 4d15b67

Please sign in to comment.