From 04ffe843aaa284da0ffd5c896672644ad3ce890b Mon Sep 17 00:00:00 2001 From: Dimitar Ivanov Date: Tue, 9 Apr 2024 16:03:28 +0300 Subject: [PATCH] Reorganize `install_on_demand` scripts. Introduce `etcdctl` and `k9s` on demand scripts. Automatically generate `etcdctl` connectivity parameters when `wrapper` runs. --- dockerfile-configs/common-components.yaml | 31 +++++-- dotfiles/.bashrc | 1 + dotfiles/.install_on_demand/.wireguard | 19 ----- generator/lib/components.py | 18 +++-- hacks/ghelp | 12 ++- hacks/install_etcdctl | 43 +++++++--- hacks/install_k9s | 55 +++++++++---- hacks/ops-pod | 11 ++- install_on_demand/.etcdctl | 80 +++++++++++++++++++ install_on_demand/.k9s | 22 +++++ install_on_demand/.shrc | 10 +++ .../.table | 15 +--- install_on_demand/.wireguard | 15 ++++ 13 files changed, 249 insertions(+), 83 deletions(-) delete mode 100644 dotfiles/.install_on_demand/.wireguard create mode 100644 install_on_demand/.etcdctl create mode 100644 install_on_demand/.k9s create mode 100644 install_on_demand/.shrc rename {dotfiles/.install_on_demand => install_on_demand}/.table (55%) create mode 100644 install_on_demand/.wireguard diff --git a/dockerfile-configs/common-components.yaml b/dockerfile-configs/common-components.yaml index 12cfdc9..56ca657 100644 --- a/dockerfile-configs/common-components.yaml +++ b/dockerfile-configs/common-components.yaml @@ -89,19 +89,32 @@ from: ./hacks to: /hacks info: ~ + - name: install_on_demand + from: ./install_on_demand + to: /etc/bash/install_on_demand + info: ~ - bash: - name: /nonroot/hacks command: | mkdir -p /nonroot && mkdir -p /nonroot/hacks && chown 65532:65532 -R /nonroot info: A directory intended to be used by nonroot users. + - name: /opt/bin + command: | + mkdir -p /opt/bin && chmod -R 777 /opt/bin + info: A directory intended to allow common install point for ad-hoc install scripts - copy: + - name: install_k9s + from: ./hacks/install_k9s + to: /nonroot/hacks + command: --chown=65532:65532 + info: Bash script which installs the `k9s` tool in the container. If you are running this container as non-root, execute the `install_k9s` script in the `/nonroot/hacks` directory. - name: install_etcdctl from: ./hacks/install_etcdctl to: /nonroot/hacks command: --chown=65532:65532 - info: Bash script which installs the `etcdctl` tool in the container. If you are running this container as non-root, execute the `install-etcdctl` script in the `/nonroot/hacks` directory. + info: Bash script which installs the `etcdctl` tool in the container. If you are running this container as non-root, execute the `install_etcdctl` script in the `/nonroot/hacks` directory. - bash: - name: bash-completion @@ -109,14 +122,16 @@ echo "" >> /root/.bashrc;\ echo "source /etc/profile.d/bash_completion.sh" >> /root/.bashrc info: ~ - - name: table + - name: install_on_demand command: | - echo "source /root/dotfiles/.install_on_demand/.table" >> /root/dotfiles/.bashrc - info: Helpful tool that can be used to simplify the analysis of iptables entries. Pass a string argument to filter the output via grep. - - name: wg - command : | - echo "source /root/dotfiles/.install_on_demand/.wireguard" >> /root/dotfiles/.bashrc - info: Command line tool for the wireguard VPN. + echo "source /etc/bash/install_on_demand/.shrc" >> /etc/bash.bashrc + info: | + A set of "on demand" install scripts that are helpful for troubleshooting. + Currently supported are: + table: Simplifies iptables analysis. Pass string to filter output via grep. + wg: Command line tool for the wireguard VPN. + etcdctl: Command line tool for manual operations on etcdctl + k9s: TUI for administration of k8s clusters - name: dotfiles command: | echo "" >> /root/.bashrc;\ diff --git a/dotfiles/.bashrc b/dotfiles/.bashrc index 0e1d766..cdd863c 100644 --- a/dotfiles/.bashrc +++ b/dotfiles/.bashrc @@ -52,6 +52,7 @@ if [[ ! -f "$git_config_personal" ]]; then [[ -n "$dotfiles_user_email" ]] && echo -e "[user]\n email = $dotfiles_user_email" >> "$git_config_personal" fi +mkdir -p "${DOTFILES_HOME}/bin" "${DOTFILES_HOME}/scripts" # add bin dir to PATH [[ -d "$DOTFILES_HOME/bin" ]] && PATH="$DOTFILES_HOME/bin:$PATH" diff --git a/dotfiles/.install_on_demand/.wireguard b/dotfiles/.install_on_demand/.wireguard deleted file mode 100644 index b220ec2..0000000 --- a/dotfiles/.install_on_demand/.wireguard +++ /dev/null @@ -1,19 +0,0 @@ -alias wg='wireguard' -function wireguard() { - yes="Yy" - no="Nn" - - if ! hash wg &> /dev/null; then - local _confirmation="" - while [[ ! $_confirmation =~ ^[$yes$no]$ ]]; do - read -p "Wireguard not found. Do you want to install it now? (Y/n) " -r _confirmation - if [[ $_confirmation =~ ^[$no]$ ]]; then - echo -e "Error: wireguard not installed." - return 1 - fi - done - read -p "Version [latest]: " -r version - apt-get --yes update && apt-get install --yes wireguard"${version:+=$version}" --yes - fi - command wg "$@" -} diff --git a/generator/lib/components.py b/generator/lib/components.py index 1b49a26..031a5e1 100644 --- a/generator/lib/components.py +++ b/generator/lib/components.py @@ -9,7 +9,7 @@ class BaseComponentConfig: def __init__(self, name, info): self.name = name - self.info = info + self.info = info.replace("\n", "\\\\n") if info is not None else None def get_info(self): return self.info @@ -27,11 +27,11 @@ def __init__(self, config): class DictComponentConfig(BaseComponentConfig): required_keys = [ - {"key": "name", "types":(str)}, + {"key": "name", "types": (str)}, ] optional_keys = [ - {"key": "info", "types":(str, type(None))}, - {"key": "provides", "types":(str, list, type(None))} + {"key": "info", "types": (str, type(None))}, + {"key": "provides", "types": (str, list, type(None))} ] def __init__(self, config): @@ -114,6 +114,7 @@ class AptRepoConfig(DictComponentConfig): {"key": "repo", "types": (str)}, {"key": "keyring", "types": (str)} ] + def __init__(self, config): DictComponentConfig.__init__(self, config) self.release_prefix = config.get("release-prefix", "") @@ -139,7 +140,13 @@ def get_key_url(self): class ComponentConfigParser: - registered_classes = [StringComponentConfig, DictComponentConfig, BashCommandConfig, ToolConfig, AptRepoConfig] + registered_classes = [ + StringComponentConfig, + DictComponentConfig, + BashCommandConfig, + ToolConfig, + AptRepoConfig, + ] def __init__(self, *argv): for component_class in argv: @@ -162,4 +169,3 @@ def parse_components(self, component_configs): raise lastErr components.append(component) return components - diff --git a/hacks/ghelp b/hacks/ghelp index 006fcc3..aea3af3 100755 --- a/hacks/ghelp +++ b/hacks/ghelp @@ -63,7 +63,7 @@ def retrieve_hacks_info(): return tools -ghelp_info_path="/var/lib/ghelp_info" +ghelp_info_path = "/var/lib/ghelp_info" ghelp_info = None with open(ghelp_info_path, "r") as f: ghelp_info = json.load(f) @@ -72,7 +72,7 @@ if ghelp_info is None: print("Failed to load ghelp info") exit(1) -apttools = retrieve_packages_info(ghelp_info["apt"], "apt", "show", "=", "\n\n" , "Package", "Version", "Description") +apttools = retrieve_packages_info(ghelp_info["apt"], "apt", "show", "=", "\n\n", "Package", "Version", "Description") piptools = retrieve_packages_info(ghelp_info["pip"], "pip", "show", "==", "---", "Name", "Version", "Summary") downloaded_tools = retrieve_downloaded_tools_info(ghelp_info["downloaded"]) hack_tools = retrieve_hacks_info() @@ -90,8 +90,12 @@ max_info_width = (terminal_width-10)//2 for i in range(len(table)): table[i][0] = textwrap.fill(table[i][0], width=max_name_width) if table[i][1] is not None: - table[i] [1] = textwrap.fill(table[i][1], width=max_name_width) - table[i][2] = textwrap.fill(table[i][2], width=max_info_width) + table[i][1] = textwrap.fill(table[i][1], width=max_name_width) + table[i][2] = textwrap.fill( + table[i][2].replace("\\n", "\n"), + width=max_info_width, + replace_whitespace=False + ) table_headers = ["TOOL/PACKAGE", "VERSION", "NOTES"] table_headers[0] = textwrap.fill(table_headers[0], width=max_name_width) diff --git a/hacks/install_etcdctl b/hacks/install_etcdctl index 290558b..974aed0 100755 --- a/hacks/install_etcdctl +++ b/hacks/install_etcdctl @@ -12,30 +12,47 @@ function show_help () { } function install () { - local version=$1 + etcd_version="v3.4.26" + local version="${1:-${etcd_version}}" local download_url + local arch + local platform + local pkg + local dest + local yellow="\033[0;33m" + local nc="\033[0m" + local tmp_dir - if [ -z "$version" ] - then # fetch v3.4.26 - version="v3.4.26" - else - if [[ ! $version == v* ]] - then - version="v${version}" + tmp_dir="$(mktemp -d)" + mkdir -p "${tmp_dir}/dest" + + dest="/opt/bin/etcdctl" + if uname="$(whoami 2> /dev/null)"; then + if [[ "${uname}" == "root" ]]; then + if [ -w "/usr/local/bin" ]; then + dest="/usr/local/bin/etcdctl" + fi fi fi - arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') + arch=$(uname -m | sed 's/^x86_64$/amd64/;s/^aarch64$/arm64/') - echo "installing etcdctl version ${version}" - download_url="https://github.com/coreos/etcd/releases/download/${version}/etcd-${version}-linux-${arch}.tar.gz" + platform="$(uname -s | tr '[:upper:]' '[:lower:]')" + pkg="etcd-${version}-${platform}-${arch}" - curl -sL ${download_url} -o etcd-${version}-linux-${arch}.tar.gz && tar -zxvf etcd-${version}-linux-${arch}.tar.gz && mv etcd-${version}-linux-${arch}/etcdctl . && rm etcd-${version}-linux-${arch}.tar.gz && rm -r etcd-${version}-linux-${arch} + download_url="https://github.com/etcd-io/etcd/releases/download/${version}/${pkg}.tar.gz" + curl -sL "${download_url}" \ + -o "${tmp_dir}/${pkg}.tar.gz" && \ + tar -zxf "${tmp_dir}/${pkg}.tar.gz" -C "${tmp_dir}/dest" && \ + mv "${tmp_dir}/dest/${pkg}/etcdctl" "${dest}" && \ + rm -rf "${tmp_dir}" + echo -e "${yellow}" echo "You can now start using etcdctl. Just execute \"etcdctl\" to use it. See https://etcd.io/docs/v3.4/dev-guide/interacting_v3/ for more details." - echo "This tool assumes that it is being run in an ephemeral container in a pod. If this is not the case then please ensure that you provide the correct ecrtificates, the correct endpoint and have all necessary accesses." + echo "This tool assumes that it is being run in an ephemeral container in a pod. If this is not the case, please ensure that you provide the correct certificates, the correct endpoint, and have all necessary accesses." echo "Certificates to be passed to the command should be mounted onto any container in the pod having a shared process namespace. Please run \"ps -A\" to list all processes and then access the certificates using \"/proc//root/\". Pass these certificate file paths to the etcdctl command :)" + echo -e "${nc}" } case "$1" in diff --git a/hacks/install_k9s b/hacks/install_k9s index e373471..90365b8 100755 --- a/hacks/install_k9s +++ b/hacks/install_k9s @@ -14,26 +14,51 @@ function show_help () { function install () { local version=$1 local download_url - local arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') - - if [ -z "$version" ] - then # fetch latest - local latest_release=$(curl -sL https://api.github.com/repos/derailed/k9s/releases/latest) - version=$(echo "${latest_release}" | jq -r '.tag_name') - echo "installing latest version ${version}" - download_url=$(echo "${latest_release}" | jq -r --arg filename "k9s_Linux_${arch}.tar.gz" '.assets[] | select(.name == $filename) | .browser_download_url') - else - if [[ ! $version == v* ]] - then - version="v${version}" + local yellow="\033[0;33m" + local nc="\033[0m" + local arch + local platform + local pkg + local dest + local version_url="https://api.github.com/repos/derailed/k9s/releases/latest" + + tmp_dir="$(mktemp -d)" + mkdir -p "${tmp_dir}/dest" + + dest="/opt/bin/k9s" + if uname="$(whoami 2> /dev/null)"; then + if [[ "${uname}" == "root" ]]; then + if [ -w "/usr/local/bin" ]; then + dest="/usr/local/bin/k9s" + fi fi - echo "installing k9s version ${version}" - download_url="https://github.com/derailed/k9s/releases/download/${version}/k9s_Linux_${arch}.tar.gz" fi - curl -sL ${download_url} -o k9s.tar.gz && tar -zxvf k9s.tar.gz k9s && mv k9s /usr/local/bin/k9s && chmod 755 /usr/local/bin/k9s && rm k9s.tar.gz + arch=$(uname -m | sed 's/^x86_64$/amd64/;s/^aarch64$/arm64/') + + platform="$(uname -s)" + pkg="k9s_${platform}_${arch}" + + if [ -z "$version" ]; then # fetch latest + version=$(curl -sL "${version_url}" | jq -r '.tag_name') + fi + + if [[ ! $version == v* ]]; then + version="v${version}" + fi + + download_url="https://github.com/derailed/k9s/releases/download/${version}/${pkg}.tar.gz" + + curl -sL "${download_url}" \ + -o "${tmp_dir}/${pkg}.tar.gz" && \ + tar -zxf "${tmp_dir}/${pkg}.tar.gz" -C "${tmp_dir}/dest" && \ + mv "${tmp_dir}/dest/k9s" "${dest}" && \ + chmod 755 "${dest}" && \ + rm -rf "${tmp_dir}" + echo -e "${yellow}" echo "You can now start using k9s. Just execute \"k9s\" to use it or \"k9s -n mynamespace\" to target a namespace. See https://github.com/derailed/k9s for more details." + echo -e "${nc}" } case "$1" in diff --git a/hacks/ops-pod b/hacks/ops-pod index d6bddb4..c826e24 100755 --- a/hacks/ops-pod +++ b/hacks/ops-pod @@ -51,7 +51,7 @@ sanitize_hostname() { } name="$(sanitize_hostname "ops-pod-$(whoami)")" -default_image="europe-docker.pkg.dev/sap-se-gcp-k8s-delivery/releases-public/eu_gcr_io/gardener-project/gardener/ops-toolbelt:latest" +default_image="europe-docker.pkg.dev/gardener-project/releases/gardener/ops-toolbelt:latest" function get_default_namespace() { _namespace=$(kubectl config view -o jsonpath="{.contexts[?(@.name == \"$(kubectl config current-context)\")].context.namespace}") echo "${_namespace:-default}" @@ -149,7 +149,6 @@ $tolerations_array containers: - name: ops-pod image: ${image} - imagePullPolicy: Always command: - sleep - "43200" @@ -183,12 +182,12 @@ while [[ $(kubectl -n $namespace get pods | sed -n -r "s/^$name.*Running.*$/Runn # exec into pod (and chroot into node if a node was selected) if [[ ${node_chroot} -eq ${TRUE} ]]; then - kubectl -n $namespace exec -ti $name -- bash -c "rm -rf /host/root/dotfiles 1> /dev/null; \ + kubectl -n $namespace exec -ti $name -- bash -c 'rm -rf /host/root/dotfiles 1> /dev/null; \ cp -r /root/dotfiles /host/root 1> /dev/null; \ cp -r /hacks /host 1> /dev/null; rm -f /host/root/.bashrc; \ - ln -s /root/dotfiles/.bashrc /host/root/.bashrc 1> /dev/null; export PATH=\"/hacks:$PATH\"; \ - echo -e '\nBE CAREFUL!!! Node root directory mounted under / \n'; \ - chroot /host /bin/bash" + ln -s /root/dotfiles/.bashrc /host/root/.bashrc 1> /dev/null; export PATH="/hacks:$PATH"; \ + echo -e "\nBE CAREFUL!!! Node root directory mounted under / \n"; \ + chroot /host /bin/bash' else kubectl -n $namespace exec -ti $name -- bash -c "echo -e '\nNode root dir is mounted under /host' >> /etc/motd; /bin/bash" fi diff --git a/install_on_demand/.etcdctl b/install_on_demand/.etcdctl new file mode 100644 index 0000000..a255f24 --- /dev/null +++ b/install_on_demand/.etcdctl @@ -0,0 +1,80 @@ +#! /usr/bin/env bash + +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +# +# SPDX-License-Identifier: Apache-2.0 + +alias etcdctl="etcdctl_install" +function etcdctl_install() { + app="etcdctl" + install_func='/nonroot/hacks/install_etcdctl' + + if ! hash "${app}" &> /dev/null; then + "${install_func}" 1>&2 + fi + command "${app}" $(_etcdctl_prepend_args "$@") +} + +# checks passed arguments and fills in args for certificates +function _etcdctl_prepend_args(){ + local cert_args=() + local args=() + local pid + + pid="$(get_etcd_pid)" + if [ "${pid}" == "" ]; then + echo "etcd is not running" 1>&2 + return + fi + if [ "${#}" -ne 0 ]; then + args=("$@") + fi + + if ! _check_arg_present "--help" "${args[@]}"; then + echo "${args[*]}" + return + fi + + if _check_arg_present "--cert" "${args[@]}"; then + cert_args+=("--cert=/proc/$(get_etcd_pid)/root/var/etcd/ssl/client/client/tls.crt") + fi + if _check_arg_present "--key" "${args[@]}"; then + cert_args+=("--key=/proc/$(get_etcd_pid)/root/var/etcd/ssl/client/client/tls.key") + fi + if _check_arg_present "--cacert" "${args[@]}"; then + cert_args+=("--cacert=/proc/$(get_etcd_pid)/root/var/etcd/ssl/client/ca/bundle.crt") + fi + if _check_arg_present "--endpoints" "${args[@]}"; then + cert_args+=("--endpoints=https://etcd-main-local:2379") + fi + if _check_arg_present "--keepalive-timeout" "${args[@]}"; then + cert_args+=("--keepalive-timeout=2m") + fi + if _check_arg_present "--keepalive-time" "${args[@]}"; then + cert_args+=("--keepalive-time=2m") + fi + if _check_arg_present "--dial-timeout" "${args[@]}"; then + cert_args+=("--dial-timeout=2m") + fi + if _check_arg_present "--dial-timeout" "${args[@]}"; then + cert_args+=("--dial-timeout=2m") + fi + args=("${args[@]}" "${cert_args[@]}") + echo "${args[*]}" +} + +function _check_arg_present(){ + local starts_with="${1}" + shift + local args=("$@") + for arg in "${args[@]}"; do + if [[ "${arg}" == "${starts_with}[= ]"* ]]; then + return 1 + fi + done + return 0 +} + +function get_etcd_pid() { + pgrep wrapper +} diff --git a/install_on_demand/.k9s b/install_on_demand/.k9s new file mode 100644 index 0000000..8fc184f --- /dev/null +++ b/install_on_demand/.k9s @@ -0,0 +1,22 @@ +#! /usr/bin/env bash + +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +# +# SPDX-License-Identifier: Apache-2.0 + +alias k9s="k9s_install" +function k9s_install() { + app="k9s" + install_func='/nonroot/hacks/install_k9s' + + if ! hash "${app}" &> /dev/null; then + "${install_func}" 1>&2 + fi + # k9s does not start if HOME is not writeable + if [ ! -w "${HOME}" ]; then + echo "HOME is not writeable. Reconfiguring to /tmp/home" 1>&2 + mkdir -p "/tmp/home" + export HOME="/tmp/home" + fi + command "${app}" "$@" +} diff --git a/install_on_demand/.shrc b/install_on_demand/.shrc new file mode 100644 index 0000000..0309a29 --- /dev/null +++ b/install_on_demand/.shrc @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +# +# SPDX-License-Identifier: Apache-2.0 + +source /etc/bash/install_on_demand/.table +source /etc/bash/install_on_demand/.wireguard +source /etc/bash/install_on_demand/.etcdctl +source /etc/bash/install_on_demand/.k9s + +export PATH="/opt/bin:${PATH}" diff --git a/dotfiles/.install_on_demand/.table b/install_on_demand/.table similarity index 55% rename from dotfiles/.install_on_demand/.table rename to install_on_demand/.table index ed533f0..43dd7ca 100644 --- a/dotfiles/.install_on_demand/.table +++ b/install_on_demand/.table @@ -1,19 +1,10 @@ +#! /usr/bin/env bash + function table () { if ! hash iptables-save &> /dev/null; then - local yes="Yy" - local no="Nn" - local _confirmation="" - while [[ ! $_confirmation =~ ^[$yes$no]$ ]]; do - read -p "Required iptables-save not found. Do you want to install it now? (Y/n) " -r _confirmation - if [[ $_confirmation =~ ^[$no]$ ]]; then - echo -e "Error: iptables-save not installed." - return 1 - fi - done echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections ;\ echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections ;\ - read -p "Version [latest]: " -r version - apt-get --yes update && apt-get install --yes iptables-persistent"${version:+=$version}" + apt-get --yes update && apt-get install --yes iptables-persistent 1>&2 fi iptables-save | while IFS= read -r line; do diff --git a/install_on_demand/.wireguard b/install_on_demand/.wireguard new file mode 100644 index 0000000..00c06a9 --- /dev/null +++ b/install_on_demand/.wireguard @@ -0,0 +1,15 @@ +#! /usr/bin/env bash + +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +# +# SPDX-License-Identifier: Apache-2.0 + +alias wg='wireguard' +function wireguard() { + if ! hash wg &> /dev/null; then + echo "Installing wireguard via apt" 1>&2 + apt-get --yes update 1>/dev/null 2>/dev/null && \ + apt-get install --yes wireguard --yes 1>/dev/null 2>/dev/null + fi + command wg "$@" +}