From bdc2e9f4577d1f17f282f9de2177a08998bbbd3c Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Sat, 17 Oct 2020 09:30:11 +1100 Subject: [PATCH] Enable Codespaces. (#14564) --- .devcontainer/Dockerfile | 21 +++ .devcontainer/devcontainer.json | 52 ++++++ .../library-scripts/docker-debian.sh | 172 ++++++++++++++++++ .../library-scripts/git-lfs-debian.sh | 34 ++++ 4 files changed, 279 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/library-scripts/docker-debian.sh create mode 100644 .devcontainer/library-scripts/git-lfs-debian.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000000..9628a309e345 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,21 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.145.0/containers/codespaces-linux/.devcontainer/base.Dockerfile + +FROM mcr.microsoft.com/vscode/devcontainers/universal:0-linux + +USER root + +# [Option] Install Docker CLI +ARG INSTALL_DOCKER="false" +COPY library-scripts/docker-debian.sh /tmp/library-scripts/ +RUN if [ "${INSTALL_DOCKER}" = "true" ]; then \ + rm -f /usr/local/share/docker-init.sh \ + && bash /tmp/library-scripts/docker-debian.sh "true" "/var/run/docker-host.sock" "/var/run/docker.sock" "codespace"; \ + fi \ + && rm -rf /var/lib/apt/lists/* /tmp/library-scripts/ + +USER codespace + +# ** [Optional] Uncomment this section to install additional packages. ** +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000000..80712a781c15 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,52 @@ +{ + "name": "GitHub Codespaces (Default)", + "build": { + "dockerfile": "Dockerfile", + "args": { + "INSTALL_DOCKER": "true" + } + }, + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "go.useGoProxyToCheckForToolUpdates": false, + "go.useLanguageServer": true, + "go.gopath": "/go", + "go.goroot": "/usr/local/go", + "go.toolsGopath": "/go/bin", + "python.pythonPath": "/opt/python/latest/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", + "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", + "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", + "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint", + "lldb.executable": "/usr/bin/lldb", + "files.watcherExclude": { + "**/target/**": true + } + }, + "remoteUser": "codespace", + "overrideCommand": false, + "workspaceMount": "source=${localWorkspaceFolder},target=/home/codespace/workspace,type=bind,consistency=cached", + "workspaceFolder": "/home/codespace/workspace", + "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind" ], + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "GitHub.vscode-pull-request-github", + "MS-vsliveshare.vsliveshare", + "VisualStudioExptTeam.vscodeintellicode" + ] + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "uname -a" +} diff --git a/.devcontainer/library-scripts/docker-debian.sh b/.devcontainer/library-scripts/docker-debian.sh new file mode 100644 index 000000000000..42ee7d6cd382 --- /dev/null +++ b/.devcontainer/library-scripts/docker-debian.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/master/script-library/docs/docker.md +# +# Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] + +ENABLE_NONROOT_DOCKER=${1:-"true"} +SOURCE_SOCKET=${2:-"/var/run/docker-host.sock"} +TARGET_SOCKET=${3:-"/var/run/docker.sock"} +USERNAME=${4:-"automatic"} + +set -e + +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 + +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi + +# Function to run apt-get if needed +apt-get-update-if-needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Install apt-transport-https, curl, lsb-release, gpg if missing +if ! dpkg -s apt-transport-https curl ca-certificates lsb-release > /dev/null 2>&1 || ! type gpg > /dev/null 2>&1; then + apt-get-update-if-needed + apt-get -y install --no-install-recommends apt-transport-https curl ca-certificates lsb-release gnupg2 +fi + +# Install Docker CLI if not already installed +if type docker > /dev/null 2>&1; then + echo "Docker CLI already installed." +else + curl -fsSL https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/gpg | (OUT=$(apt-key add - 2>&1) || echo $OUT) + echo "deb [arch=amd64] https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]') $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list + apt-get update + apt-get -y install --no-install-recommends docker-ce-cli +fi + +# Install Docker Compose if not already installed +if type docker-compose > /dev/null 2>&1; then + echo "Docker Compose already installed." +else + + LATEST_COMPOSE_VERSION=$(curl -sSL "https://api.github.com/repos/docker/compose/releases/latest" | grep -o -P '(?<="tag_name": ").+(?=")') + curl -sSL "https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose +fi + +# If init file already exists, exit +if [ -f "/usr/local/share/docker-init.sh" ]; then + exit 0 +fi + +# By default, make the source and target sockets the same +if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then + touch "${SOURCE_SOCKET}" + ln -s "${SOURCE_SOCKET}" "${TARGET_SOCKET}" +fi + +# Add a stub if not adding non-root user access, user is root +if [ "${ENABLE_NONROOT_DOCKER}" = "false" ] || [ "${USERNAME}" = "root" ]; then + echo '/usr/bin/env bash -c "\$@"' > /usr/local/share/docker-init.sh + chmod +x /usr/local/share/docker-init.sh + exit 0 +fi + +# If enabling non-root access and specified user is found, setup socat and add script +chown -h "${USERNAME}":root "${TARGET_SOCKET}" +if ! dpkg -s socat > /dev/null 2>&1; then + apt-get-update-if-needed + apt-get -y install socat +fi +tee /usr/local/share/docker-init.sh > /dev/null \ +<< EOF +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +set -e + +SOCAT_PATH_BASE=/tmp/vscr-dind-socat +SOCAT_LOG=\${SOCAT_PATH_BASE}.log +SOCAT_PID=\${SOCAT_PATH_BASE}.pid + +# Wrapper function to only use sudo if not already root +sudoIf() +{ + if [ "\$(id -u)" -ne 0 ]; then + sudo "\$@" + else + "\$@" + fi +} + +# Log messages +log() +{ + echo -e "[\$(date)] \$@" | sudoIf tee -a \${SOCAT_LOG} > /dev/null +} + +echo -e "\n** \$(date) **" | sudoIf tee -a \${SOCAT_LOG} > /dev/null +log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET}" + +# If enabled, try to add a docker group with the right GID. If the group is root, +# fall back on using socat to forward the docker socket to another unix socket so +# that we can set permissions on it without affecting the host. +if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ] && [ "${USERNAME}" != "root" ] && [ "${USERNAME}" != "0" ]; then + SOCKET_GID=\$(stat -c '%g' ${SOURCE_SOCKET}) + if [ "\${SOCKET_GID}" != "0" ]; then + log "Adding user to group with GID \${SOCKET_GID}." + if [ "\$(cat /etc/group | grep :\${SOCKET_GID}:)" = "" ]; then + sudoIf groupadd --gid \${SOCKET_GID} docker-host + fi + # Add user to group if not already in it + if [ "\$(id ${USERNAME} | grep -E 'groups=.+\${SOCKET_GID}\(')" = "" ]; then + sudoIf usermod -aG \${SOCKET_GID} ${USERNAME} + fi + else + # Enable proxy if not already running + if [ ! -f "\${SOCAT_PID}" ] || ! ps -p \$(cat \${SOCAT_PID}) > /dev/null; then + log "Enabling socket proxy." + log "Proxying ${SOURCE_SOCKET} to ${TARGET_SOCKET} for vscode" + sudoIf rm -rf ${TARGET_SOCKET} + (sudoIf socat UNIX-LISTEN:${TARGET_SOCKET},fork,mode=660,user=${USERNAME} UNIX-CONNECT:${SOURCE_SOCKET} 2>&1 | sudoIf tee -a \${SOCAT_LOG} > /dev/null & echo "\$!" | sudoIf tee \${SOCAT_PID} > /dev/null) + else + log "Socket proxy already running." + fi + fi + log "Success" +fi + +# Execute whatever commands were passed in (if any). This allows us +# to set this script to ENTRYPOINT while still executing the default CMD. +set +e +exec "\$@" +EOF +chmod +x /usr/local/share/docker-init.sh +chown ${USERNAME}:root /usr/local/share/docker-init.sh +echo "Done!" \ No newline at end of file diff --git a/.devcontainer/library-scripts/git-lfs-debian.sh b/.devcontainer/library-scripts/git-lfs-debian.sh new file mode 100644 index 000000000000..1f5647475aaa --- /dev/null +++ b/.devcontainer/library-scripts/git-lfs-debian.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/master/script-library/docs/git-lfs.md +# +# Syntax: ./git-lfs-debian.sh + +set -e + +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 + +export DEBIAN_FRONTEND=noninteractive + +# Install git and curl if missing +if ! dpkg -s git curl ca-certificates > /dev/null 2>&1; then + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + apt-get update + fi + apt-get -y install --no-install-recommends git curl ca-certificates +fi + +# Install Git LFS +echo "Downloading Git LFS..." +curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash +echo "Installing Git LFS..." +apt-get install -yq git-lfs +git lfs install +echo "Done!" \ No newline at end of file