diff --git a/Dockerfile.custom b/Dockerfile.custom index 7bb7282d3..7ae868b6b 100644 --- a/Dockerfile.custom +++ b/Dockerfile.custom @@ -3,6 +3,12 @@ # for a customer using the Cloud Posse Reference Architecture. # Use it as a basis for your own customizations. # +# Note that Geodesic supports runtime customizations that +# do not require a custome Dockerfile. See: +# https://github.com/cloudposse/geodesic/blob/master/docs/customization.md +# +# See Dockerfile.options for some common options you might want. +# # Note that the version numbers in this file are not maintained, # you will want to update them to current versions when you start # and then have a plan for regularly updating them as you go along. diff --git a/Makefile b/Makefile index bfc0e5758..99460904e 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ deps: init docker tag $(DOCKER_IMAGE_NAME) $(DOCKER_IMAGE_NAME_BASE) %.install: - @docker run --rm --env DOCKER_IMAGE --env DOCKER_TAG $(DOCKER_IMAGE_NAME) | bash -s $(DOCKER_TAG) || (echo "Try: sudo make install"; exit 1) + @docker run --rm --env DOCKER_IMAGE --env DOCKER_TAG $(DOCKER_IMAGE_NAME) | bash -s $(DOCKER_TAG) build: $(DOCKER_BASE_OS).build diff --git a/Makefile.custom b/Makefile.custom index a67925735..e09b2e7b5 100644 --- a/Makefile.custom +++ b/Makefile.custom @@ -3,6 +3,13 @@ # Modify the variable settings to create your own version of Geodesic # with your own Docker image name and app name. # +# See Dockerfile.custom and Dockerfile.options for +# how to customize your Dockerfile. +# Note that Geodesic supports runtime customizations that +# do not require a custom Dockerfile: See +# https://github.com/cloudposse/geodesic/blob/master/docs/customization.md +# +# # The `make` variables build up to $(DOCKER_IMAGE):$(DOCKER_TAG) being # what you would use for `docker run` and `docker push`. # You probably want to use either `latest` or `dev` for DOCKER_TAG @@ -47,7 +54,7 @@ push: ## Install wrapper script from geodesic container install: - @docker run --rm $(DOCKER_IMAGE_NAME) | bash -s $(DOCKER_TAG) || (echo "Try: sudo make install"; exit 1) + @docker run --rm $(DOCKER_IMAGE_NAME) | bash -s $(DOCKER_TAG) ## Start the geodesic shell by calling wrapper script run: diff --git a/docs/customization.md b/docs/customization.md index a9aed2d34..a27597f12 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -18,14 +18,54 @@ how to configure the customization. ## DESCRIPTION -Users can place bash shell scripts on their host computer, to be read either at the start of `bash` profile +A great deal of customization can be implemented by placing shell scripts +on the host computer, to be read either at the start of `bash` profile script processing or at the end of it. These shell scripts can set up environment variables, command aliases, shell functions, etc. and through setting environment variables, can cause Geodesic to enable or disable certain features. +Due to technical limitations, there are a small number of options +that can only be controlled via command line options or corresponding (exported) +shell environment variables set in the user's shell before launching +Geodesic via the wrapper script (which is the default and recommended way to launch Geodesic). + Users can also choose whether to have a single `bash` history file for all containers or to have separate history files. +### Command line options (also environment variables) + +Geodesic accepts a small number of command line options, which can also be +set by corresponding (exported) shell environment variables. In all cases, +the command line options must be introduced with double dashes and be +set via `=`. Boolean options can be set `=true` with no argument on the command line. +Command line options are lower case with words separated by dashes, +while environment variables are upper case with words separated by underscored. + +Valid: +```bash +geodesic --env-file=~/.geodesic/extra-envs +ENV_FILE=~/.geodesic/extra_envs geodesic +geodesic --geodesic-host-bindfs-enabled +``` + +Not valid: +```bash +geodesic --env-file ~/.geodesic/extra-envs +env-file=~/.geodesic/extra-envs +ENV-FILE=~/.geodesic/extra-envs +GEODESIC_HOST_BINDFS_ENABLED= geodesic +``` + +At the moment, underscores are accepted instead of dashes in command line arguments, +but this behavior should not be relied on: + +Works, but not officially supported: +```bash +geodesic --env-file=~/.geodesic/extra_envs +``` + +TODO: Document all the command line options in [geodesic(1)](geodesic.md) + ### Root directory for configuration All configuration files are stored under `$GEODESIC_CONFIG_HOME`, which defaults to `/localhost/.geodesic`. @@ -36,26 +76,42 @@ then files would go in `~/work/config/geodesic/` and below on your Docker host m ### Resources -There are currently 3 Resources used for configuration: +There are currently 5 Resources used for configuration: - Preferences, which are shell scripts loaded very early in the launch of the Geodesic shell. - Overrides, which are shell scripts loaded very late in the launch of the Geodesic shell. - `bash` history files, which store `bash` command line history. +- A file of environment variable settings passed to `docker --env-file` +- Command-line options (which can also be set by correspondingly named shell environment variables) Additionally, when Geodesic exits normally, it will run the host command `geodesic_on_exit` if it is available. This is intended to be a script that you write and install anywhere on your PATH to do whatever cleanup you want. For example, change the window title. +#### Preferences and Overrides + Both preferences and overrides can be either a single file, named `preferences` and `overrides` respectively, or can be a collection of files in directories named `preferences.d` and `overrides.d`. If they are directories, all the visible files in the directories will be sourced, except for hidden files and files with names matching the `GEODESIC_AUTO_LOAD_EXCLUSIONS` regex, which defaults to `(~|.bak|.log|.old|.orig|.disabled)$`. +#### History files + `bash` history is always stored in a single file named `history`, never a directory of files nor files with any other name. If you want to use a separate history file for one Geodesic-based Docker image not shared by other Geodesic-based Docker images, you must create an empty `history` file in the image-specific configuration directory (see below). +#### Environment file + +Geodesic passes a single argument to Docker's `--env-file` option when launching +via the installed wrapper script. By default, that file is `~/.geodesic/env` +but that file name can be set via the `--geodesic-default-env-file` command line +option or the `GEODESIC_DEFAULT_ENV_FILE` environment variable. + +A second environment file can be specified in addition to the default via the +`--env-file` command line option or `ENV_FILE` + ### Configuration by file placement Resources can be in several places, and will be loaded from most general to most specific, according to the name of the docker container image. diff --git a/os/alpine/Dockerfile.alpine b/os/alpine/Dockerfile.alpine index d28cc0305..d09e7a486 100644 --- a/os/alpine/Dockerfile.alpine +++ b/os/alpine/Dockerfile.alpine @@ -33,6 +33,14 @@ RUN python3 -m pip install --upgrade pip setuptools wheel && \ pip install $(grep cryptography /requirements.txt) && \ pip install -r /requirements.txt --ignore-installed --prefix=/dist --no-build-isolation --no-warn-script-location +### While we have gcc installed, we take advantage of that and build bindfs +### Use fuse (FUSE 2) rather than fuse3 for consistency with Debian +RUN apk add curl fuse fuse-dev +RUN curl -qOsSL https://bindfs.org/downloads/bindfs-1.15.1.tar.gz +RUN tar zxf bindfs-1.15.1.tar.gz && cd bindfs-1.15.1/ && \ + ./configure && make && make install + + # # Google Cloud SDK # @@ -111,6 +119,9 @@ WORKDIR /tmp # Copy python dependencies COPY --from=python /dist/ /usr/ +# Install bindfs +COPY --from=python /usr/local/bin/bindfs /usr/local/bin/bindfs + # # Install Google Cloud SDK # @@ -151,6 +162,7 @@ ADD https://raw.githubusercontent.com/ahmetb/kubectx/v${KUBECTX_COMPLETION_VERSI ARG KUBE_PS1_VERSION ADD https://raw.githubusercontent.com/jonmosco/kube-ps1/v${KUBE_PS1_VERSION}/kube-ps1.sh /etc/profile.d/prompt:kube-ps1.sh +RUN chmod 755 /etc/bash_completion.d/kubens.sh /etc/bash_completion.d/kubectx.sh /etc/profile.d/prompt:kube-ps1.sh # # Install helm diff --git a/os/alpine/packages-alpine.txt b/os/alpine/packages-alpine.txt index d60e51a1a..990cd5169 100644 --- a/os/alpine/packages-alpine.txt +++ b/os/alpine/packages-alpine.txt @@ -2,6 +2,7 @@ busybox-extras diffutils drill +fuse fzf-bash-completion iputils keybase-client@testing diff --git a/os/debian/Dockerfile.debian b/os/debian/Dockerfile.debian index 8a46f0e45..52a05bbdd 100644 --- a/os/debian/Dockerfile.debian +++ b/os/debian/Dockerfile.debian @@ -107,7 +107,7 @@ COPY packages.txt os/debian/packages-debian.txt /etc/apt/ ## Here is where we would copy in the repo checksum in an attempt to ensure updates bust the Docker build cache # Add CloudPosse package repo -RUN apt-get update && apt-get install -y apt-utils && apt-get install -y curl +RUN apt-get update && apt-get install -y apt-utils curl RUN curl -1sLf 'https://dl.cloudsmith.io/public/cloudposse/packages/cfg/setup/bash.deb.sh' | bash # Install Google package repo @@ -178,6 +178,7 @@ ADD https://raw.githubusercontent.com/ahmetb/kubectx/v${KUBECTX_COMPLETION_VERSI ARG KUBE_PS1_VERSION ADD https://raw.githubusercontent.com/jonmosco/kube-ps1/v${KUBE_PS1_VERSION}/kube-ps1.sh /etc/profile.d/prompt:kube-ps1.sh +RUN chmod 755 /etc/bash_completion.d/kubens.sh /etc/bash_completion.d/kubectx.sh /etc/profile.d/prompt:kube-ps1.sh # # Install helm diff --git a/os/debian/packages-debian.txt b/os/debian/packages-debian.txt index 307094220..a721491ed 100644 --- a/os/debian/packages-debian.txt +++ b/os/debian/packages-debian.txt @@ -1,4 +1,5 @@ # Essential debian-only packages +bindfs default-mysql-client dnsutils inetutils-ftp diff --git a/rootfs/etc/motd b/rootfs/etc/motd index fcb618bcf..8b0a55488 100644 --- a/rootfs/etc/motd +++ b/rootfs/etc/motd @@ -1,11 +1,11 @@ IMPORTANT: -* Unless there were errors reported above, -* your host $HOME directory should be mounted to `/localhost`, and -* your host AWS configuration and credentials should be available. -* Use Leapp on your host computer to manage your credentials. -* Leapp is free, open source, and available from https://leapp.cloud -* Use AWS_PROFILE environment variable to manage your AWS IAM role. -* You can interactively select AWS profiles via the `assume-role` command. +# Unless there were errors reported above, +# * Your host $HOME directory should be available under `/localhost` +# * Your host AWS configuration and credentials should be available +# * Use Leapp on your host computer to manage your credentials +# * Leapp is free, open source, and available from https://leapp.cloud +# * Use AWS_PROFILE environment variable to manage your AWS IAM role +# * You can interactively select AWS profiles via the `assume-role` command diff --git a/rootfs/etc/profile.d/_colors.sh b/rootfs/etc/profile.d/_10-colors.sh similarity index 75% rename from rootfs/etc/profile.d/_colors.sh rename to rootfs/etc/profile.d/_10-colors.sh index c5a48bf4e..14f1cf214 100755 --- a/rootfs/etc/profile.d/_colors.sh +++ b/rootfs/etc/profile.d/_10-colors.sh @@ -1,6 +1,8 @@ # Files in the profile.d directory are executed by the lexicographical order of their file names. -# This file is named _colors.sh. The leading underscore is needed to ensure this file executes before -# other files that depend on the functions defined here. This file has no dependencies and should come first. +# This file is named _10-colors.sh. The leading underscore is needed to ensure this file executes before +# other files that depend on the functions defined here. The number portion is to ensure proper ordering among +# the high-priority scripts +# This file has no dependencies and should come first. function red() { echo "$(tput setaf 1)$*$(tput setaf 0)" } @@ -32,4 +34,4 @@ function bold() { # The terminal does not support color printf "%s\n" "$*" fi -} \ No newline at end of file +} diff --git a/rootfs/etc/profile.d/_20-localhost.sh b/rootfs/etc/profile.d/_20-localhost.sh new file mode 100644 index 000000000..2a72f4905 --- /dev/null +++ b/rootfs/etc/profile.d/_20-localhost.sh @@ -0,0 +1,29 @@ +# Files in the profile.d directory are executed by the lexicographical order of their file names. +# This file is named _20-localhost.sh. The leading underscore is needed to ensure this file executes before +# other files that depend on the file system mapping defined here. +# The number portion is to ensure proper ordering among the high-priority scripts. +# This file has only depends on colors.sh and should come before any scripts that +# attempt to access files on the host via `/localhost`. + +if [[ $SHLVL == 1 ]] && [[ -n $GEODESIC_HOST_UID ]] && [[ -n $GEODESIC_HOST_GID ]] && + [[ -n $GEODESIC_LOCALHOST ]] && df -a | grep -q " ${GEODESIC_LOCALHOST}\$"; then + if [[ $(df -a | grep ' /localhost$' | cut -f1 -d' ') == ${GEODESIC_LOCALHOST} ]]; then + echo "# Host file ownership mapping already configured" + export GEODESIC_LOCALHOST_MAPPED_DEVICE="${GEODESIC_LOCALHOST}" + elif df -a | grep -q ' /localhost$'; then + red "# Host filesystems found mounted at both /localhost and /localhost.bindfs." + red "# * Verify that content under /localhost is what you expect." + red "# * Report the issue at https://github.com/cloudposse/geodesic/issues" + red "# * Include the output of \`env | grep GEODESIC\` and \`df -a\` in your issue description." + elif bindfs -o nonempty ${GEODESIC_BINDFS_OPTIONS} --create-for-user="$GEODESIC_HOST_UID" --create-for-group="$GEODESIC_HOST_GID" "${GEODESIC_LOCALHOST}" /localhost; then + green "# BindFS mapping of ${GEODESIC_LOCALHOST} to /localhost enabled." + green "# Files created under /localhost will have UID:GID ${GEODESIC_HOST_UID}:${GEODESIC_HOST_GID} on host." + export GEODESIC_LOCALHOST_MAPPED_DEVICE="${GEODESIC_LOCALHOST}" + else + red "# ERROR: Unable to mirror /localhost.bindfs to /localhost" + red "# * Report the issue at https://github.com/cloudposse/geodesic/issues" + red "# * Work around the issue by unsetting shell environment variable GEODESIC_HOST_BINDFS_ENABLED." + red "# * Exiting." + exec false + fi +fi diff --git a/rootfs/etc/profile.d/_geodesic-config.sh b/rootfs/etc/profile.d/_30-geodesic-config.sh similarity index 91% rename from rootfs/etc/profile.d/_geodesic-config.sh rename to rootfs/etc/profile.d/_30-geodesic-config.sh index ca0f64003..4471d9494 100755 --- a/rootfs/etc/profile.d/_geodesic-config.sh +++ b/rootfs/etc/profile.d/_30-geodesic-config.sh @@ -1,7 +1,9 @@ # Files in the profile.d directory are executed by the lexicographical order of their file names. -# This file is named _geodesic-config.sh. The leading underscore is needed to ensure this file executes before -# other files that depend on the functions defined here. -# This file has depends on _colors.sh and should come second. +# This file is named _30-geodesic-config.sh. The leading underscore is needed to ensure this file +# executes before other files that depend on the functions defined here. +# The number portion is to ensure proper ordering among the high-priority scripts. +# This file defines functions but does not execute them, so it can come anywhere +# before the first script to use one of its functions, such as preferences.sh. # bash functions that support the user customization framework # diff --git a/rootfs/etc/profile.d/_preferences.sh b/rootfs/etc/profile.d/_40-preferences.sh similarity index 90% rename from rootfs/etc/profile.d/_preferences.sh rename to rootfs/etc/profile.d/_40-preferences.sh index 415c516a9..f3dd8fc0b 100755 --- a/rootfs/etc/profile.d/_preferences.sh +++ b/rootfs/etc/profile.d/_40-preferences.sh @@ -1,7 +1,8 @@ # Files in the profile.d directory are executed by the lexicographical order of their file names. -# This file is named _preferences.sh. The leading underscore is needed to ensure this file executes before -# other files that depend on the functions defined here. -# This file has depends on _geodesic-config.sh and should come third. +# This file is named _40-preferences.sh. The leading underscore is needed to ensure this file +# executes before other files that depend on the functions defined here. +# The number portion is to ensure proper ordering among the high-priority scripts. +# This file has depends on colors.sh, geodesic-config.sh, and localhost.sh and should come after them. # This file loads user preferences/customizations and must load before any user-visible configuration takes place. # In case this output is being piped into a shell, print a warning message @@ -58,13 +59,13 @@ elif [[ ! -d $GEODESIC_CONFIG_HOME ]]; then fi if [[ ! -d $GEODESIC_CONFIG_HOME ]]; then - if ! df | grep -q /localhost; then - if [[ -z $KUBERNETES_PORT ]]; then + if ! df -a | grep -q " ${GEODESIC_LOCALHOST:-/localhost}\$"; then + if [[ -n $KUBERNETES_PORT ]]; then + echo $(green Kubernetes host detected, Geodesic customization disabled.) + else red "########################################################################################" >&2 - red "# No filesystem is mounted at $(bold /localhost) which limits Geodesic functionality." >&2 + red "# No filesystem is mounted at $(bold ${GEODESIC_LOCALHOST:-/localhost}) which limits Geodesic functionality." >&2 boot install - else - echo $(green Kubernetes host detected, Geodesic customization disabled.) fi export GEODESIC_CUSTOMIZATION_DISABLED="/localhost not a volume" elif mkdir -p $GEODESIC_CONFIG_HOME; then diff --git a/rootfs/etc/profile.d/_workdir.sh b/rootfs/etc/profile.d/_50-workdir.sh similarity index 67% rename from rootfs/etc/profile.d/_workdir.sh rename to rootfs/etc/profile.d/_50-workdir.sh index 9d33197f1..64f9c773e 100644 --- a/rootfs/etc/profile.d/_workdir.sh +++ b/rootfs/etc/profile.d/_50-workdir.sh @@ -1,13 +1,13 @@ # Files in the profile.d directory are executed by the lexicographical order of their file names. -# This file sets the working directory inside Geodesic to match the host directory Geodesic -# was launched from, if possible. If the host directory is not accessible, it sets the working directory to `/`. -# -# This file is named _workdir.sh. The leading underscore is needed to ensure this file executes before -# other files that may depend on it. The "w" is needed to ensure it is loaded *after* _preferences.sh +# This file is named _50-workdir.sh. The leading underscore is needed to ensure this file +# executes before other files that may depend on it. +# The number portion is to ensure proper ordering among the high-priority scripts. +# This file depends on colors.sh, localhost.sh, and preferences,sh and must come after them # +# Outputs the device the file resides on, or /dev/null if the file does not exist function _file_device() { - df --output=source "$1" | tail -1 + { [[ -e $1 ]] && df --output=source "$1" | tail -1; } || echo '/dev/null' } # file_on_host is true when the argument is a file or directory that appears to be on the Host file system. @@ -16,8 +16,8 @@ function _file_device() { # Therefore we cache some info in the environment. if [[ $GEODESIC_LOCALHOST_DEVICE == "disabled" ]]; then red "# Host filesystem device detection disabled." -elif df -a | grep -q /localhost; then - export GEODESIC_LOCALHOST_DEVICE=$(_file_device /localhost) +elif df -a | grep -q " ${GEODESIC_LOCALHOST:-/localhost}\$"; then + export GEODESIC_LOCALHOST_DEVICE=$(_file_device "${GEODESIC_LOCALHOST:-/localhost}") if [[ $GEODESIC_LOCALHOST_DEVICE == $(_file_device /) ]]; then red "# Host filesystem device detection failed. Falling back to \"path starts with /localhost\"." GEODESIC_LOCALHOST_DEVICE="same-as-root" @@ -27,12 +27,13 @@ else fi function file_on_host() { - if [[ $GEODESIC_LOCALHOST_DEVICE =~ ^(disabled|missing)$ ]]; then + if [[ $GEODESIC_LOCALHOST_DEVICE =~ ^(disabled|missing)$ ]]; then return 1 elif [[ $GEODESIC_LOCALHOST_DEVICE == "same-as-root" ]]; then - [[ $(readlink -e "$1") =~ ^/localhost(/.*)?$ ]] + [[ $(readlink -e "$1") =~ ^/localhost ]] else - [[ $(_file_device "$1") == ${GEODESIC_LOCALHOST_DEVICE} ]] + local dev="$(_file_device "$1")" + [[ $dev == $GEODESIC_LOCALHOST_DEVICE ]] || [[ $dev == $GEODESIC_LOCALHOST_MAPPED_DEVICE ]] fi } @@ -52,7 +53,7 @@ if [[ -d $GEODESIC_WORKDIR ]]; then [[ $SHLVL == 1 ]] && green "# Initial working directory configured as ${GEODESIC_WORKDIR}" else if [[ -d $GEODESIC_HOST_CWD ]]; then - if [[ -n $LOCAL_HOME ]] && { [[ $GEODESIC_LOCALHOST_DEVICE == "disabled" ]] || $(file_on_host "$GEODESIC_HOST_CWD"); }; then + if [[ -n $LOCAL_HOME ]] && { [[ $GEODESIC_LOCALHOST_DEVICE == "disabled" ]] || file_on_host "$GEODESIC_HOST_CWD"; }; then export GEODESIC_WORKDIR=$(readlink -e "${GEODESIC_HOST_CWD}") green "# Initial working directory set from host CWD to ${GEODESIC_WORKDIR}" else diff --git a/rootfs/etc/profile.d/atmos.sh b/rootfs/etc/profile.d/atmos.sh index d0f10bdba..9e924fd6e 100644 --- a/rootfs/etc/profile.d/atmos.sh +++ b/rootfs/etc/profile.d/atmos.sh @@ -4,7 +4,7 @@ function atmos_configure_base_path() { # Leave $ATMOS_BASE_PATH alone if it is already set if [[ -n $ATMOS_BASE_PATH ]]; then if [[ $SHLVL == 1 ]]; then - green "# Using configured $ATMOS_BASE_PATH of \"$ATMOS_BASE_PATH\"" + green "# Using configured ATMOS_BASE_PATH of \"$ATMOS_BASE_PATH\"" fi return fi diff --git a/rootfs/etc/profile.d/aws-okta.sh b/rootfs/etc/profile.d/aws-okta.sh index 66ccf0207..dd1217467 100755 --- a/rootfs/etc/profile.d/aws-okta.sh +++ b/rootfs/etc/profile.d/aws-okta.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash if [ "${AWS_OKTA_ENABLED}" == "true" ]; then - echo + echo echo red '* You have AWS_OKTA_ENABLED set to "true".' red '* Cloud Posse no longer recommends using aws-okta and is' @@ -35,7 +35,7 @@ if [ "${AWS_OKTA_ENABLED}" == "true" ]; then PROMPT_HOOKS+=("aws_okta_prompt") function aws_okta_prompt() { - if [[ -z "${AWS_OKTA_PROFILE}" && -z "${ASSUME_ROLE}" ]]; then + if [[ -z "${AWS_OKTA_PROFILE}" && -z "${ASSUME_ROLE}" ]]; then echo -e "-> Run '$(green assume-role)' to login to AWS with aws-okta" fi } diff --git a/rootfs/etc/profile.d/aws.sh b/rootfs/etc/profile.d/aws.sh index 6a4795d26..ebda6fc3a 100755 --- a/rootfs/etc/profile.d/aws.sh +++ b/rootfs/etc/profile.d/aws.sh @@ -35,8 +35,8 @@ fi function aws_choose_role() { _preview="${FZF_PREVIEW:-crudini --format=ini --get "$AWS_CONFIG_FILE" 'profile {}'}" - cat "${AWS_SHARED_CREDENTIALS_FILE:-~/.aws/credentials}" "${AWS_CONFIG_FILE:-~/.aws/config}" 2>/dev/null | \ - crudini --get - | sed 's/^ *profile *//' | \ + cat "${AWS_SHARED_CREDENTIALS_FILE:-~/.aws/credentials}" "${AWS_CONFIG_FILE:-~/.aws/config}" 2>/dev/null | + crudini --get - | sed 's/^ *profile *//' | fzf \ --height 30% \ --preview-window right:70% \ @@ -67,7 +67,7 @@ function aws_sdk_assume_role() { else AWS_PROFILE="$role" $* fi - ASSUME_ROLE="$assume_role"; + ASSUME_ROLE="$assume_role" } # Asks AWS what the currently active identity is and diff --git a/rootfs/etc/profile.d/banner.sh b/rootfs/etc/profile.d/banner.sh index b85589040..2bc416a57 100755 --- a/rootfs/etc/profile.d/banner.sh +++ b/rootfs/etc/profile.d/banner.sh @@ -5,22 +5,11 @@ BANNER_INDENT="${BANNER_INDENT:- }" BANNER_FONT="${BANNER_FONT:-Nancyj.flf}" # " IDE parser fix if [ "${SHLVL}" == "1" ]; then - function _check_support() { + function _check_support() { [[ $(arch) != "x86_64" ]] || grep -qsE 'GenuineIntel|AuthenticAMD' /proc/cpuinfo && return - echo - echo - red '**********************************************************************' - red '**********************************************************************' - red '** **' - red '** You appear to be running Geodesic on an Apple M1 CPU **' - red '** Geodesic is not supported on the Apple M1 and has known issues **' - red '** See https://github.com/cloudposse/geodesic/issues/719 **' - red '** **' - red '**********************************************************************' - red '**********************************************************************' - echo - echo - } + yellow '# Detected Apple M1 emulating Intel CPU. Support for this configuration is evolving.' + yellow '# Report issues and read about solutions at https://github.com/cloudposse/geodesic/issues/719' + } function _header() { local vstring @@ -44,15 +33,12 @@ if [ "${SHLVL}" == "1" ]; then elif [ "$BANNER_COMMAND" == "figurine" ]; then ${BANNER_COMMAND} -f "${BANNER_FONT}" "${BANNER}" | sed "s/^/${BANNER_INDENT}/" else - ${BANNER_COMMAND} + ${BANNER_COMMAND} fi fi } - # We call _check_support twice so that the warning appears - # both above and below the banner _check_support _header - _check_support unset _check_support unset _header fi diff --git a/rootfs/etc/profile.d/iterm.sh b/rootfs/etc/profile.d/iterm.sh index caf2a432b..8dc0882ec 100755 --- a/rootfs/etc/profile.d/iterm.sh +++ b/rootfs/etc/profile.d/iterm.sh @@ -17,6 +17,3 @@ if [ "${TERM_PROGRAM}" == "iTerm.app" ]; then trap _geodesic_iterm_exit EXIT fi fi - - - diff --git a/rootfs/etc/profile.d/prompt.sh b/rootfs/etc/profile.d/prompt.sh index 862abc352..8ba827d9e 100755 --- a/rootfs/etc/profile.d/prompt.sh +++ b/rootfs/etc/profile.d/prompt.sh @@ -16,7 +16,10 @@ export SCREEN_SIZE="${LINES}x${COLUMNS}" # So we cannot just unthinkingly set PROMPT_COMMAND=prompter or PROMPT_COMMAND="${PROMPT_COMMAND};prompter" # Instead, we examine the PROMPT_COMMAND variable, initialize it to "prompter;" if it is empty, # or otherwise add "prompter;" to the end of the command string (inserting a ; before it if needed). -export PROMPT_COMMAND +# We do not want subshells to try to run prompt commands if they are not defined, so we do not export PROMPT_COMMAND +export -n PROMPT_COMMAND +# We do not want our dynamic prompt to be copied and not updated in a subshell, so we do not export PS1, either +export -n PS1 function _install_prompter() { if ! [[ $PROMPT_COMMAND =~ prompter ]]; then local final_colon=';$' @@ -82,8 +85,8 @@ function geodesic_prompt() { # See https://github.com/cloudposse/geodesic/issues/417 [[ -z $ASSUME_ROLE_ACTIVE_MARK ]] && ASSUME_ROLE_ACTIVE_MARK=$'\x01'$(tput bold)$(tput setaf 2)$'\x02\u221a\x01'$(tput sgr0)$'\x02' # green bold '√' [[ -z $ASSUME_ROLE_INACTIVE_MARK ]] && ASSUME_ROLE_INACTIVE_MARK=$'\x01'$(tput bold)$(tput setaf 1)$'\x02\u2717\x01'$(tput sgr0)$'\x02' # red bold '✗' - [[ -z $BLACK_RIGHTWARDS_ARROWHEAD ]] && BLACK_RIGHTWARDS_ARROWHEAD=$'\u27A4' # '➤' - [[ -z $BANNER_MARK ]] && BANNER_MARK='⧉' # \u29c9 TWO JOINED SQUARES + [[ -z $BLACK_RIGHTWARDS_ARROWHEAD ]] && BLACK_RIGHTWARDS_ARROWHEAD=$'\u27A4' # '➤' + [[ -z $BANNER_MARK ]] && BANNER_MARK='⧉' # \u29c9 TWO JOINED SQUARES ;; *) @@ -99,7 +102,7 @@ function geodesic_prompt() { # '▶︎' ($'\u25b6\ufe0e') BLACK RIGHT-POINTING TRIANGLE which is sometimes presented as an emoji (as GitHub likes to) '▶️' # '⏩︎' ($'\u23e9\ufe0e') BLACK RIGHT-POINTING DOUBLE TRIANGLE [[ -z $BLACK_RIGHTWARDS_ARROWHEAD ]] && BLACK_RIGHTWARDS_ARROWHEAD=$'\u2a20' # '⨠' Z NOTATION SCHEMA PIPING - [[ -z $BANNER_MARK ]] && BANNER_MARK='⧉' # \u29c9 TWO JOINED SQUARES + [[ -z $BANNER_MARK ]] && BANNER_MARK='⧉' # \u29c9 TWO JOINED SQUARES ;; esac diff --git a/rootfs/templates/bootstrap b/rootfs/templates/bootstrap index 245da5134..0011e2013 100755 --- a/rootfs/templates/bootstrap +++ b/rootfs/templates/bootstrap @@ -3,8 +3,8 @@ export DOCKER_IMAGE="{{getenv "DOCKER_IMAGE" "cloudposse/geodesic"}}" export DOCKER_TAG="{{- getenv "DOCKER_TAG" (printf "${1:-%s-%s}" ((index (split (getenv "GEODESIC_VERSION") " ") 0) | default "dev") (getenv "GEODESIC_OS" "alpine")) -}}" export APP_NAME=${APP_NAME:-$(basename $DOCKER_IMAGE)} export INSTALL_PATH=${INSTALL_PATH:-/usr/local/bin} +export SAFE_INSTALL_PATH="$HOME/.local/bin" # per XDG recommendations export INSTALLER_NAME="${APP_NAME}-installer" -export OUTPUT=${OUTPUT:-/dev/null} # Replace with /dev/stdout to audit output export REQUIRE_PULL=${REQUIRE_PULL:-false} if [ -z "${DOCKER_IMAGE}" ]; then @@ -43,9 +43,23 @@ if [ $? -ne 0 ]; then fi # Check that we can write to install path -if [ ! -w "${INSTALL_PATH}" ]; then - echo "Cannot write to ${INSTALL_PATH}. Please retry using sudo." 2>&1 - exit 1 +if ! $([ -d "${INSTALL_PATH}" ] && [ -r "${INSTALL_PATH}" ] && + [ -w "${INSTALL_PATH}" ] && [ -x "${INSTALL_PATH}" ]); then + if [ ! -d "${SAFE_INSTALL_PATH}" ]; then + mkdir -p "${SAFE_INSTALL_PATH}" 2>/dev/null && + echo "Creating ${SAFE_INSTALL_PATH}" >&2 + fi + + if [ -d "${SAFE_INSTALL_PATH}" ] && [ -r "${SAFE_INSTALL_PATH}" ] && + [ -w "${SAFE_INSTALL_PATH}" ] && [ -x "${SAFE_INSTALL_PATH}" ]; then + echo "Inadequate permissions to install to ${INSTALL_PATH}. Installing to ${SAFE_INSTALL_PATH}." >&2 + INSTALL_PATH="${SAFE_INSTALL_PATH}" + else + echo "Inadequate permissions to install to ${INSTALL_PATH} or ${SAFE_INSTALL_PATH}." >&2 + echo "Please \`chmod u+rwx \"$INSTALL_PATH\"\` (you may need to use \`sudo\`)" + echo "or set INSTALL_PATH to a writable directory where you want to install ${APP_NAME}." >&2 + exit 1 + fi fi # Proceed with installation @@ -61,14 +75,35 @@ fi # Sometimes docker might not exit cleanly docker rm -f "${INSTALLER_NAME}" >/dev/null 2>&1 -(docker run --name "${INSTALLER_NAME}" --rm -e DOCKER_IMAGE -e DOCKER_TAG -e APP_NAME "${DOCKER_IMAGE}:${DOCKER_TAG}" -c wrapper | tee "${INSTALL_PATH}/${APP_NAME}" >${OUTPUT}) && +docker run --name "${INSTALLER_NAME}" --rm -e DOCKER_IMAGE -e DOCKER_TAG -e APP_NAME "${DOCKER_IMAGE}:${DOCKER_TAG}" -c wrapper >"${INSTALL_PATH}/${APP_NAME}" && chmod 755 "${INSTALL_PATH}/${APP_NAME}" if [ $? -eq 0 ]; then echo "# Installed ${APP_NAME} to ${INSTALL_PATH}/${APP_NAME}" - exit 0 else + echo echo "# Failed to install ${APP_NAME}" - echo "# Please let us know! Send an email to < hello@cloudposse.com > with what went wrong." + echo "# If this is not due to file system permissions, please" + echo "# check for known issues and consider reporting the failure at" + echo "# https://github.com/cloudposse/geodesic/issues" + echo + exit 1 +fi + +hash -r || true +if [ "$(realpath "${INSTALL_PATH}/${APP_NAME}")" != "$(command -v "${APP_NAME}" 2>/dev/null)" ]; then + if [ -x "${INSTALL_PATH}/${APP_NAME}" ]; then + if [ -n "$(command -v "${APP_NAME}" 2>/dev/null)" ]; then + echo "# WARNING: \`${APP_NAME}\` installed in ${INSTALL_PATH} but" + echo "# also found in $(dirname "$(command -v "${APP_NAME}")")" + echo "# which appears earlier in \$PATH. " + echo "# Fix your PATH or \`rm \"$(command -v "${APP_NAME}")\"" + echo + else + echo "# WARNING: It appears ${INSTALL_PATH} is not on your PATH. Please add it." + fi + else + echo "# WARNING: Install appeared to succeed but ${INSTALL_PATH}/${APP_NAME} is not executable." + fi exit 1 fi diff --git a/rootfs/templates/scaffolding/Makefile b/rootfs/templates/scaffolding/Makefile index 87754cbcd..d965860c0 100644 --- a/rootfs/templates/scaffolding/Makefile +++ b/rootfs/templates/scaffolding/Makefile @@ -19,7 +19,7 @@ push: docker push $(DOCKER_IMAGE) install: - @docker run --rm -e DOCKER_IMAGE -e DOCKER_TAG $(DOCKER_IMAGE_NAME) | sudo bash -s $(DOCKER_TAG) + @docker run --rm -e DOCKER_IMAGE -e DOCKER_TAG $(DOCKER_IMAGE_NAME) | bash -s $(DOCKER_TAG) run: $(KOPS_CLUSTER_NAME) diff --git a/rootfs/templates/wrapper b/rootfs/templates/wrapper index 2814b863e..daf5342ee 100755 --- a/rootfs/templates/wrapper +++ b/rootfs/templates/wrapper @@ -83,6 +83,23 @@ function use() { DOCKER_ARGS=() fi + if [ "${GEODESIC_HOST_BINDFS_ENABLED}" = "true" ]; then + if [ "${USER_ID}" = 0 ]; then + echo "# WARNING: Host user is root. This is DANGEROUS." + echo " * Geodesic should not be launched by the host root user." + echo " * Use \"rootless\" mode instead. See https://docs.docker.com/engine/security/rootless/" + echo "# Not enabling BindFS host filesystem mapping because host user is root." + else + echo "# Enabling BindFS mapping of file system owner and group ID." + DOCKER_ARGS+=( + --env GEODESIC_HOST_UID="${USER_ID}" + --env GEODESIC_HOST_GID="${GROUP_ID}" + --env GEODESIC_LOCALHOST="${GEODESIC_LOCALHOST:=/localhost.bindfs}" + --env GEODESIC_BINDFS_OPTIONS + ) + fi + fi + if [ "${WITH_DOCKER}" == "true" ]; then # Bind-mount docker socket into container # Should work on Linux and Mac. @@ -155,9 +172,13 @@ function use() { if [ "${local_home}" == "/localhost" ]; then echo "WARNING: not mounting ${local_home} because it conflicts with geodesic" else - echo "# Mounting ${local_home} into container with workdir ${GEODESIC_HOST_CWD}" + if [ "${GEODESIC_LOCALHOST:-/localhost}" != "/localhost" ]; then + echo "# Mounting ${local_home} into container at ${GEODESIC_LOCALHOST} with workdir ${GEODESIC_HOST_CWD}" + else + echo "# Mounting ${local_home} into container with workdir ${GEODESIC_HOST_CWD}" + fi DOCKER_ARGS+=( - --volume="${local_home}:/localhost" + --volume="${local_home}:${GEODESIC_LOCALHOST:-/localhost}" --env LOCAL_HOME="${local_home}" ) fi diff --git a/rootfs/usr/local/bin/boot b/rootfs/usr/local/bin/boot index d713536c7..d2541b815 100755 --- a/rootfs/usr/local/bin/boot +++ b/rootfs/usr/local/bin/boot @@ -27,8 +27,6 @@ color "# Geodesic with all its features (the recommended way to use Geodesic):" color "#" color "# docker run --rm ${DOCKER_IMAGE:-cloudposse/geodesic}:${DOCKER_TAG:-latest${ID:+-$ID}} init | bash" color "#" -color "# (On a Linux workstation, you might need to use \"sudo bash\" instead of just \"bash\")" -color "#" color "# After that, you should be able to launch Geodesic just by typing" color "#" color "# geodesic"