diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 29fe43ce..0af38550 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -106,8 +106,8 @@ jobs: FETCH_GH_TOKEN=${{ secrets.GITHUB_TOKEN }} publish-template: - runs-on: ubuntu-latest needs: [create-release, publish-image] + runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: Checkout @@ -178,6 +178,7 @@ jobs: coder template push \ --directory ${TEMPLATE_DIR} \ --var workspace_image=${WORKSPACE_IMAGE} \ + --var test_mode=false \ --name ${TEMPLATE_VERSION} \ --message "${RELEASE_MSG}" \ --yes \ diff --git a/.github/workflows/test-image.yaml b/.github/workflows/test-image.yaml index 719aa621..e332723f 100644 --- a/.github/workflows/test-image.yaml +++ b/.github/workflows/test-image.yaml @@ -6,7 +6,7 @@ name: "test image" on: pull_request: paths: - - 'images/**' + - 'images/homelab-workspace/**' - '.github/workflows/test-image.yaml' workflow_dispatch: @@ -81,8 +81,8 @@ jobs: echo "Done" echo - echo "Running entrypoint preparation script..." - docker exec $container_name sudo -u root /opt/coder/bin/entrypoint-prepare.sh --username $test_user + echo "Create test user within container..." + docker exec $container_name sudo -u root useradd --groups sudo --shell /bin/bash $test_user echo "Done" echo diff --git a/.github/workflows/test-template.yaml b/.github/workflows/test-template.yaml index d3de78fa..49972dbe 100644 --- a/.github/workflows/test-template.yaml +++ b/.github/workflows/test-template.yaml @@ -58,21 +58,6 @@ jobs: # renovate: datasource=github-releases depName=nestybox/sysbox SYSBOX_VERSION: "0.6.4" - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3 - with: - driver-opts: network=host - - - name: Build image for testing template - uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6 - with: - context: ./images/simple-workspace - load: true - push: false - tags: simple-workspace:latest - allow: network.host - network: host - - name: Start local coder instance # yamllint disable-line rule:indentation run: | @@ -123,7 +108,14 @@ jobs: export TEMPLATE_NAME="test-$(date --utc +%Y%m%d-%H%M%SZ)" echo "TEMPLATE_NAME=${TEMPLATE_NAME}" >> $GITHUB_ENV echo "Publishing template ${TEMPLATE_DIR} as ${TEMPLATE_NAME}..." - coder template push --directory ${TEMPLATE_DIR} --var workspace_image=simple-workspace:latest --yes ${TEMPLATE_NAME} + set -x + coder template push \ + --directory ${TEMPLATE_DIR} \ + --var workspace_image=ubuntu:noble \ + --var test_mode=true \ + --yes \ + ${TEMPLATE_NAME} + set +x echo echo "Confirming template has been published..." @@ -192,7 +184,7 @@ jobs: docker logs -n 100 coder-coder-1 echo echo Container logs: - docker logs -n 100 ${WORKSPACE_NAME} + docker logs 100 ${WORKSPACE_NAME} echo echo Workspace status coder show ${WORKSPACE_NAME} diff --git a/images/homelab-workspace/coder/entrypoint-prepare.sh b/images/homelab-workspace/coder/entrypoint-prepare.sh deleted file mode 100755 index 9579d6cc..00000000 --- a/images/homelab-workspace/coder/entrypoint-prepare.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash -set -eo pipefail - -if [[ $EUID -ne 0 ]]; then - echo "Should be run as root" - exit 1 -fi - -USERNAME="" -while [[ "$#" -gt 0 ]]; do - case $1 in - --username) - USERNAME="$2"; shift; - if [[ -z "${USERNAME}" ]]; then - echo "--username is required" - exit 1 - fi - ;; - *) echo "Unknown parameter passed: $1"; echo; exit 1 ;; - esac - shift -done -if [[ -z "${USERNAME}" ]]; then - echo "--username is required" - exit 1 -fi - -update_user() { - local username="$1" - local home_dir="/home/${username}" - - echo "- Modifying user..." - usermod --home $home_dir --shell /bin/bash --login $username coder - - # allow coder user to sudo to so that they can run any system actions (such as using apt-get) within their workspace container. - echo "- Enabling sudo" - echo "${username} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd - echo -} - -maintain_directories() { - local username="$1" - local home_dir="/home/${username}" - - echo "- Creating directories..." - mkdir -p $home_dir - ln -sf $home_dir /home/coder - mkdir -p $home_dir/.log/ - echo "- Updating directory permissions..." - chown $username:coder $home_dir - chown $username:coder $home_dir/.log/ - echo -} - -main() { - echo "Updating user: coder -> ${username}" - update_user $USERNAME - echo "Maintain required directories..." - maintain_directories $USERNAME - echo "Complete." -} - -main diff --git a/images/simple-workspace/Dockerfile b/images/simple-workspace/Dockerfile deleted file mode 100644 index 31f2d36c..00000000 --- a/images/simple-workspace/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM ubuntu:noble@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 - -# shell options to ensure RUN statements fail on error -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# minimum requirements for running coder agent -RUN apt-get update && \ - DEBIAN_FRONTEND="noninteractive" apt-get install -yq --no-install-recommends \ - bash \ - curl \ - git \ - sudo \ - unzip \ - wget && \ - apt-get autoremove -y && \ - apt-get clean -y && \ - apt-get autoremove -y && \ - apt-get clean -y && \ - rm -rf /tmp/* /var/cache/debconf/* /var/cache/python/* /var/lib/apt/lists/* /var/log/* /var/tmp/* - -ARG USER=coder -RUN useradd --groups sudo --no-create-home --shell /bin/bash ${USER} \ - && echo "${USER} ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/${USER} \ - && chmod 0440 /etc/sudoers.d/${USER} -USER ${USER} -WORKDIR /home/${USER} diff --git a/templates/docker/homelab-workspace/coder-agent.tf b/templates/docker/homelab-workspace/coder-agent.tf new file mode 100644 index 00000000..4c1fa8a2 --- /dev/null +++ b/templates/docker/homelab-workspace/coder-agent.tf @@ -0,0 +1,56 @@ +resource "coder_agent" "main" { + arch = "amd64" + os = "linux" + startup_script = var.test_mode ? "/bin/bash --noprofile --norc" : "/bin/bash --noprofile --norc /opt/coder/bin/agent-startup.sh" + startup_script_behavior = "blocking" + + metadata { + display_name = "CPU Usage" + key = "0_cpu_usage" + script = "coder stat cpu" + interval = 60 + timeout = 1 + } + + metadata { + display_name = "Memory Usage" + key = "1_mem_usage" + script = "coder stat mem --prefix Gi" + interval = 60 + timeout = 1 + } + + metadata { + display_name = "Home Disk" + key = "3_home_disk" + script = "coder stat disk --path $${HOME}" + interval = 60 + timeout = 1 + } + + metadata { + display_name = "CPU Usage (Host)" + key = "4_cpu_usage_host" + script = "coder stat cpu --host" + interval = 60 + timeout = 1 + } + + metadata { + display_name = "Memory Usage (Host)" + key = "5_mem_usage_host" + script = "coder stat mem --host" + interval = 60 + timeout = 1 + } + + metadata { + display_name = "Load Average" + key = "load" + script = <&1 +echo "---------------------------------------------------------------------------------" +echo "Writing coder agent init script to file..." cat > /tmp/coder-agent-init-script.sh <<'EOT' ${local.standard_init_script} EOT chmod 755 /tmp/coder-agent-init-script.sh +echo "---------------------------------------------------------------------------------" +echo "Checking minimum requirements for coder agent..." +if (command -v curl && command -v sudo && command -v useradd) > /dev/null; then + echo "Minimum requirements for running coder agent is met." +else + echo "Installing minimum required software for running coder agent..." + apt-get update + DEBIAN_FRONTEND="noninteractive" apt-get install -yq --no-install-recommends curl sudo adduser +fi +echo "---------------------------------------------------------------------------------" +if grep coder /etc/passwd > /dev/null; then + echo "Modifying user: coder -> ${local.username}..." + usermod --home /home/${local.username} --shell /bin/bash --login $username coder +else + echo "Creating user - ${local.username}..." + useradd --groups sudo --home-dir /home/${local.username} --shell /bin/bash ${local.username} +fi +# allow coder user to sudo to so that they can run any system actions (such as using apt-get) within their workspace container. +echo "Enabling ${local.username} to sudo" +echo "${local.username} ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/${local.username} +chmod 0440 /etc/sudoers.d/${local.username} +echo "---------------------------------------------------------------------------------" +echo "Creating directories and updating directory permissions..." +mkdir -p /home/${local.username}/.log/ +chown ${local.username} /home/${local.username} +chown ${local.username} /home/${local.username}/.log/ +echo "---------------------------------------------------------------------------------" +echo "Preparation complete. Starting coder agent..." if [[ "$ENTRYPOINT_MODE" == "SUPERVISED" ]]; then - echo "Running in supervised mode..." - /opt/coder/bin/entrypoint-prepare.sh --username ${local.username} + echo "Running agent in supervised mode..." echo -e "#!/bin/bash\nsudo -u ${local.username} --preserve-env=CODER_AGENT_TOKEN /bin/bash /tmp/coder-agent-init-script.sh" > /tmp/coder-agent-wrapper.sh chmod 755 /tmp/coder-agent-wrapper.sh exec /usr/bin/supervisord -c /etc/supervisord.conf else - echo "Running in unsupervised mode..." + echo "Running agent in unsupervised mode..." sudo -u ${local.username} --preserve-env=CODER_AGENT_TOKEN /bin/bash /tmp/coder-agent-init-script.sh fi EOF @@ -144,7 +69,7 @@ resource "docker_container" "workspace" { count = data.coder_workspace.me.start_count image = docker_image.workspace_image.image_id - name = local.test_mode ? data.coder_workspace.me.name : "${lower(local.username)}-${lower(data.coder_workspace.me.name)}" + name = var.test_mode ? data.coder_workspace.me.name : "${lower(local.username)}-${lower(data.coder_workspace.me.name)}" hostname = data.coder_workspace.me.name runtime = "sysbox-runc" user = "0:0" @@ -153,7 +78,7 @@ resource "docker_container" "workspace" { env = [ "CODER_AGENT_TOKEN=${coder_agent.main.token}", - "ENTRYPOINT_MODE=${local.test_mode ? "UNSUPERVISED" : "SUPERVISED"}" + "ENTRYPOINT_MODE=${var.test_mode ? "UNSUPERVISED" : "SUPERVISED"}" ] host { host = "host.docker.internal" diff --git a/templates/docker/homelab-workspace/terraform.tf b/templates/docker/homelab-workspace/terraform.tf new file mode 100644 index 00000000..a1894b29 --- /dev/null +++ b/templates/docker/homelab-workspace/terraform.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.8" + + required_providers { + coder = { + source = "coder/coder" + version = "~> 1.0.0" + } + docker = { + source = "kreuzwerker/docker" + version = "~> 3.0.2" + } + } +} + +provider "docker" { + host = "unix:///var/run/docker.sock" +} diff --git a/templates/docker/homelab-workspace/variables.tf b/templates/docker/homelab-workspace/variables.tf new file mode 100644 index 00000000..91980dda --- /dev/null +++ b/templates/docker/homelab-workspace/variables.tf @@ -0,0 +1,7 @@ +variable "workspace_image" { + type = string +} + +variable "test_mode" { + type = bool +} diff --git a/templates/docker/homelab-workspace/volumes.tf b/templates/docker/homelab-workspace/volumes.tf new file mode 100644 index 00000000..8304f634 --- /dev/null +++ b/templates/docker/homelab-workspace/volumes.tf @@ -0,0 +1,25 @@ +locals { + container_volume_mounts = { + "home" = "/home/${local.username}", + "docker" = "/var/lib/docker" + } + bind_mount_host_paths = { + "home" = "/srv/workspaces/${local.username}", + "docker" = "/srv/workspaces/${local.username}-${data.coder_workspace.me.name}-docker" + } + + # create docker volumes in test_mode + docker_volumes = var.test_mode ? ["home", "docker"] : [] + # create bind mounts otherwise + bind_mounts = var.test_mode ? [] : ["home", "docker"] +} + + +resource "docker_volume" "volume" { + for_each = toset(local.docker_volumes) + + name = "coder-${data.coder_workspace.me.id}-${each.key}" + lifecycle { + ignore_changes = all + } +}