Sign inSign up

dockersamples/labspace-content-dev:v0.25.0-sdlc

Manifest digest

sha256:fe5c9fbdee1704c4dbc408672ff8d76ffbc4a8b2b4f82fb66a69845855b2e649

Last pushed

6 months by dockersamplesbuildbot

Type

Compose

Manifest digest

sha256:fe5c9fbdee1704c4dbc408672ff8d76ffbc4a8b2b4f82fb66a69845855b2e649

Compose file content

##################################################################################################
# This Compose file is intended to run a Labspace. It can be run with the following:
#
# CONTENT_REPO_URL=https://github.com/username/labspace-content-repo docker compose -f compose.run.yaml up
##################################################################################################

services:
  configurator:
    image: dockersamples/labspace-configurator:dev
    use_api_socket: true
    volumes:
      - labspace-content:/project
      - labspace-instructions:/instructions
      - docker-creds:/docker-creds
      - labspace-support:/etc/labspace-support

  interface:
    image: dockersamples/labspace-interface:dev
    ports:
      - "3030:3030"
    volumes:
      - labspace-instructions:/labspace/instructions:ro
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/private-key
        volume:
          subpath: private-key
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/socket
        volume:
          subpath: socket
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/metadata
        volume:
          subpath: metadata
    depends_on:
      workspace:
        condition: service_started
      configurator:
        condition: service_completed_successfully
    restart: unless-stopped

  workspace:
    image: dockersamples/labspace-workspace-node:dev
    depends_on:
      configurator:
        condition: service_completed_successfully
      socket-proxy:
        condition: service_started
    ports:
      - 8085:8085 # For the IDE itself
      - 3000:3000 # For the Node application running in the IDE
    volumes:
      - socket-proxy:/var/run
      - labspace-content:/home/coder/project
      - docker-creds:/docker-creds
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/public-key
        volume:
          subpath: public-key
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/socket
        volume:
          subpath: socket


  host-republisher:
    image: dockersamples/labspace-host-port-republisher:dev
    pull_policy: always
    restart: always
    volumes:
      - socket-proxy:/var/run
    network_mode: service:workspace
    environment:
      LABEL_FILTER: labspace-resource=true
    depends_on:
      - workspace
      - socket-proxy

  workspace-cleaner:
    image: dockersamples/labspace-cleaner:dev
    pull_policy: always
    volumes:
      - socket-proxy:/var/run
    environment:
      LABEL_FILTER: labspace-resource=true
    depends_on:
      - socket-proxy

  socket-proxy:
    image: mikesir87/docker-socket-proxy:v1.3.1
    volumes:
      - socket-proxy:/tmp/proxy
      - /var/run/docker.sock:/var/run/docker.sock
    configs:
      - source: docker-proxy-config-labels
        target: /etc/docker-socket-proxy/config.d/add-and-require-labels.yaml
      - source: docker-proxy-mount-remap
        target: /etc/docker-socket-proxy/config.d/mount-path-remap.yaml
      - source: docker-proxy-mount-allowlist
        target: /etc/docker-socket-proxy/config.d/mount-path-allowlist.yaml
      - source: docker-proxy-forward-proxied-socket
        target: /etc/docker-socket-proxy/config.d/forward-proxied-socket.yaml
      - source: docker-proxy-add-to-labspace-network
        target: /etc/docker-socket-proxy/config.d/add-to-labspace-network.yaml
    environment:
      DEBUG_LOGS: "true"
      LISTEN_SOCKET_PATH: /tmp/proxy/docker.sock

volumes:
  socket-proxy:
    name: labspace-socket-proxy
  labspace-content:
    name: labspace-content
  labspace-instructions:
  docker-creds:
  labspace-support:

networks:
  default:
    name: labspace

configs:
  docker-proxy-config-labels:
    content: |
      mutators:
        # Add labels to all newly created objects
        - type: addLabels
          labels:
            labspace-resource: "true"
      responseFilters:
        # Only return objects with the labels we mutated on
        - type: labelFilter
          objectsToFilter:
            - containers
            - volumes
            - networks
          requiredLabels:
            labspace-resource: "true"
  docker-proxy-mount-remap:
    content: |
      # Remap the project directory to the labspace content volume, since the project
      # is running out of a volume.
      mutators:
        - type: mountPath
          from: /home/coder/project
          to: labspace-content
  docker-proxy-mount-allowlist:
    content: |
      gates:
        - type: mountSource
          allowedSources:
            - labspace-content
            - labspace-socket-proxy
            - buildx_buildkit_default_state
            - label:labspace-resource=true
  docker-proxy-forward-proxied-socket:
    content: |
      # If requests to use the Docker Socket are used (such as Testcontainers),
      # use the proxied one to ensure permissions, remappings, etc. are applied
      mutators:
        - type: mountPath
          from: /var/run/docker.sock
          to: labspace-socket-proxy/docker.sock
  docker-proxy-add-to-labspace-network:
    content: |
      mutators:
        - type: addToNetwork
          networks:
            - labspace
##################################################################################################
# This Compose file provides the overrides needed to develop Labspace content.
#
# This variant will mount in the source content into the configurator, where it will then be
# copied into the volume that is shared with the rest of the labspace. This allows you to run through
# the lab, making changes to files, running startup scripts, and more without polluting the actual
# content. Compose Watch will monitor the content directory for changes and sync them into the
# workspace, which shares the volume with the interface.
#
# The CONTENT_PATH environment variable is being used here because we are unable to simply use
# a relative path (such as "./"). When running the Compose stack via a published OCI artifact,
# the working directory is not the same as the location of the Compose files, which is its cached
# location on the host filesystem. Therefore, the paths would not resolve correctly.
##################################################################################################

services:
  configurator:
    volumes:
      - $CONTENT_PATH:/dev-content:ro
    environment:
      DEV_MODE: true

  interface:
    environment:
      CONTENT_DEV_MODE: true
    volumes:
      # Remove the read-only flag since watch will be syncing changes
      - labspace-instructions:/labspace/instructions
    develop:
      watch:
        - path: $CONTENT_PATH/labspace
          action: sync
          target: /labspace/instructions

  workspace:
    develop:
      watch:
        - path: $CONTENT_PATH/project
          action: sync
          target: /home/coder/project
#########################################################################
# This Compose file extends the default Labspace setup by adding the following elements:
#
# - A Traefik reverse proxy to route traffic to Gitea and k3s
# - A Gitea server for Git hosting and CI/CD
# - A Gitea self-hosted runner for executing CI jobs
# - A k3s Kubernetes cluster
#
# This provides a "SDLC-in-a-box" environment for end-to-end labs.
#
# Additional documentation is found in the docs/sdlc-labspace.md file.
#########################################################################

services:
  ############################################################
  # Overrides to Labspace default services
  ############################################################
  workspace:
    environment:
      SKIP_CI_SECRET_SETUP: ${SKIP_CI_SECRET_SETUP:-false}
    volumes:
      - gitea-outputs:/gitea-outputs:ro
      - k3s-output:/kubeconfig:ro
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/k8s-setup
        volume:
          subpath: k8s-setup
    configs:
      - source: workspace-entrypoint-script
        target: /entrypoint.d/50-entrypoint-script.sh
        mode: 0755
      - source: workspace-wait-for-bootstrap
        target: /startup-scripts/00-wait-for-bootstrap.sh
        mode: 0755
      - source: workspace-ci-secrets
        target: /startup-scripts/10-ci-secrets.sh
        mode: 0755
      - source: workspace-kubeconfig-setup
        target: /startup-scripts/10-kubeconfig-setup.sh
        mode: 0755
      - source: workspace-git-setup
        target: /startup-scripts/20-git-setup.sh
        mode: 0755
      - source: workspace-configure-k3s-coredns
        target: /startup-scripts/15-configure-k3s-coredns.sh
        mode: 0755
    depends_on:
      git:
        condition: service_healthy
      k3s:
        condition: service_healthy
      zone-file-creator:
        condition: service_completed_successfully

  interface:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.interface.rule=Host(`dockerlabs.xyz`)"
      - "traefik.http.routers.interface.entrypoints=web"
      - "traefik.http.routers.interface.service=interface"
      - "traefik.http.services.interface.loadbalancer.server.port=3030"

  ############################################################
  # New services
  ############################################################
  
  cert-creator:
    image: alpine
    command: /generate-certs.sh
    configs:
      - source: generate-traefik-certs
        target: /generate-certs.sh
        mode: 0755
    volumes:
      - labspace-traefik-certs:/certs

  zone-file-creator:
    image: dhi.io/wbitt-network-multitool:3-alpine3.23-dev
    command: /create-zone-file.sh
    configs:
      - source: create-zone-file
        target: /create-zone-file.sh
        mode: 0755
    volumes:
      - labspace-support:/labspace-support
    depends_on:
      traefik:
        condition: service_started 

  traefik:
    image: traefik:3.6.5
    command:
      - --api.insecure=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.ssh.address=:22
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls=true
      - --providers.file.filename=/etc/traefik/cert-config.yaml
    ports:
      - "22:22"
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - labspace-traefik-certs:/certs:ro
    configs:
      - source: traefik-cert-config
        target: /etc/traefik/cert-config.yaml
    networks:
      default:
        aliases:
          - git.dockerlabs.xyz
          - registry.dockerlabs.xyz
          - app.dockerlabs.xyz
    depends_on:
      cert-creator:
        condition: service_completed_successfully

  registry:
    image: ghcr.io/project-zot/zot:v2.1.14
    labels:
      traefik.enable: true
      traefik.http.routers.registry.rule: Host(`registry.dockerlabs.xyz`)
      traefik.http.services.registry.loadbalancer.server.port: "5000"
      traefik.http.middlewares.remove-frame-headers.headers.customresponseheaders.X-Frame-Options: ""
      traefik.http.routers.registry.middlewares: remove-frame-headers

  git:
    image: gitea/gitea:1.25.3
    volumes:
      - gitea-data:/data
      - gitea-outputs:/gitea-outputs
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    configs:
      - source: gitea-bootstrap
        target: /etc/gitea/bootstrap.sh
        mode: 0755
    environment:
      GITEA__server__ROOT_URL: http://git.dockerlabs.xyz/
      GITEA__server__SSH_DOMAIN: git.dockerlabs.xyz
      GITEA__server__SSH_LISTEN_PORT: 22
      GITEA__database__DB_TYPE: sqlite3

      # This prevents the first-time install page from showing (setup is done via script/env vars)
      GITEA__security__INSTALL_LOCK: "true"
    labels:
      traefik.enable: "true"
      traefik.http.routers.git.rule: Host(`git.dockerlabs.xyz`)
      traefik.http.routers.git.entrypoints: web
      traefik.http.routers.git.service: git
      traefik.http.services.git.loadbalancer.server.port: "3000"
      traefik.http.middlewares.remove-frame-headers.headers.customresponseheaders.X-Frame-Options: ""
      traefik.http.routers.git.middlewares: remove-frame-headers
      traefik.http.routers.git-secure.rule: Host(`git.dockerlabs.xyz`)
      traefik.http.routers.git-secure.entrypoints: websecure
      traefik.http.routers.git-secure.service: git
      traefik.tcp.routers.git-ssh.rule: HostSNI(`*`)
      traefik.tcp.routers.git-ssh.entrypoints: ssh
      traefik.tcp.routers.git-ssh.service: git-ssh
      traefik.tcp.services.git-ssh.loadbalancer.server.port: "22"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
      interval: 10s
      timeout: 10s
      retries: 3
      start_period: 5s
    post_start:
      - command: bash /etc/gitea/bootstrap.sh
        user: root

  gitea-runner:
    image: gitea/act_runner:0.2.13
    entrypoint: []
    command: bash -c "GITEA_RUNNER_REGISTRATION_TOKEN=$(cat /gitea-outputs/runner_token.txt) /sbin/tini -- run.sh"
    volumes:
      - type: volume
        source: socket-proxy
        target: /var/run/docker.sock
        volume:
          subpath: docker.sock
      - gitea-outputs:/gitea-outputs
    environment:
      GITEA_INSTANCE_URL: http://git.dockerlabs.xyz
      GITEA_RUNNER_NAME: labspace-runner

      # The runner uses 1.41 Docker API by default, but we need 1.45+ when using the socket proxy
      # due to volume rewrites using volume subpaths
      DOCKER_API_VERSION: "1.45"
    depends_on:
      git:
        condition: service_healthy

  k3s:
    # When updating the k8s version, ensure the kubectl version in the base workspace image is updated
    image: rancher/k3s:v1.35.0-k3s1
    command: server --tls-san k8s.dockerlabs.xyz
    privileged: true
    volumes:
      - k3s-server:/var/lib/rancher/k3s
      - k3s-output:/output
    environment:
      - K3S_TOKEN=labspace-k3s-token
      - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
      - K3S_KUBECONFIG_MODE=666
    tmpfs:
      - /run
      - /var/run
    ulimits:
      nproc: 65535
      nofile:
        soft: 65535
        hard: 65535
    networks:
      default:
        aliases:
          - k8s.dockerlabs.xyz
    configs:
      - source: k3s-registry-config
        target: /etc/rancher/k3s/registries.yaml
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.prod-app.rule=Host(`app.dockerlabs.xyz`)"
      - "traefik.http.routers.prod-app.entrypoints=web"
      - "traefik.http.routers.prod-app.service=prod-app"
      - "traefik.http.services.prod-app.loadbalancer.server.port=80"
    healthcheck:
      test: ["CMD", "kubectl", "get", "--raw", "/readyz"]
      interval: 10s
      timeout: 10s
      retries: 10
      start_period: 20s


volumes:
  gitea-data:
  gitea-outputs:
  k3s-server:
  k3s-output: 
  labspace-traefik-certs:


configs:
  # Generate the certs for Traefik to use
  generate-traefik-certs:
    content: |
      #!/bin/sh

      apk add --no-cache openssl >/dev/null 2>&1 || true;
      mkdir -p /certs/ca

      if [ -f /certs/dockerlabs.crt ] && [ -f /certs/dockerlabs.key ]; then
        echo 'Certs already exist, skipping generation';
        return
      fi

      # Create CA (one-time)
      if [ ! -f /certs/ca/ca.crt ] || [ ! -f /certs/ca/ca.key ]; then
        openssl genrsa -out /certs/ca/ca.key 4096
        openssl req -x509 -new -nodes -key /certs/ca/ca.key -sha256 -days 3650 \
          -subj "/CN=dockerlabs-local-ca" \
          -out /certs/ca/ca.crt
      fi

      # Create leaf key + CSR
      openssl genrsa -out /certs/dockerlabs.key 4096
      cat > /tmp/leaf.cnf <<'CNF'
      [req]
      distinguished_name = req_distinguished_name
      req_extensions = v3_req
      prompt = no

      [req_distinguished_name]
      CN = dockerlabs.xyz

      [v3_req]
      subjectAltName = @alt_names
      keyUsage = critical, digitalSignature, keyEncipherment
      extendedKeyUsage = serverAuth

      [alt_names]
      DNS.1 = dockerlabs.xyz
      DNS.2 = *.dockerlabs.xyz
      CNF

      openssl req -new -key /certs/dockerlabs.key -out /tmp/dockerlabs.csr -config /tmp/leaf.cnf

      # Sign leaf cert with CA
      openssl x509 -req -in /tmp/dockerlabs.csr \
        -CA /certs/ca/ca.crt -CAkey /certs/ca/ca.key -CAcreateserial \
        -out /certs/dockerlabs.crt -days 825 -sha256 \
        -extfile /tmp/leaf.cnf -extensions v3_req

      # combine cert & key into a PEM (optional)
      cat /certs/dockerlabs.crt /certs/dockerlabs.key > /certs/dockerlabs.pem || true;

      echo 'Generated CA and certificates';
      find /certs -type f

  # Traefik configuration to use the generated certs
  traefik-cert-config:
    content: |
      tls:
        certificates:
          - certFile: /certs/dockerlabs.crt
            keyFile: /certs/dockerlabs.key
        stores:
          default:
            defaultCertificate:
              certFile: /certs/dockerlabs.crt
              keyFile: /certs/dockerlabs.key

  # Override the default allow list to add buildkit builders and the gitea volumes
  docker-proxy-mount-allowlist:
    content: |
      gates:
        - type: mountSource
          allowedSources:
            - labspace-content
            - labspace-socket-proxy
            - buildx_buildkit_default_state
            - buildx_buildkit_builder-*
            - label:labspace-resource=true
            - act-toolcache
            - GITEA*

  # The /entrypoint.d hook in code-server does not guarantee order of execution, as it simply uses `find` to get files in the 
  # directory. To ensure our scripts run in the correct order, we prefix them with numbers and use a custom entrypoint script 
  # to sort and execute the scripts in order.
  workspace-entrypoint-script:
    content: |
      #!/bin/sh
      set -eu

      CUSTOM_DIR="/startup-scripts"

      # Nothing to do if the directory doesn't exist
      [ -d "$$CUSTOM_DIR" ] || exit 0

      # Find executable regular files, sort them, and run one by one
      find "$$CUSTOM_DIR" -type f -executable -print \
        | sort \
        | while IFS= read -r script; do
            echo "→ Running $$script"
            "$$script"
          done

  # Waits for the Gitea bootstrap to complete before allowing other startup scripts to proceed.
  # Validates that the moby user exists and the SSH key is registered, which are the last steps
  # of the bootstrap and gate everything the workspace startup scripts depend on.
  workspace-wait-for-bootstrap:
    content: |
      #!/bin/bash

      echo "Waiting for Gitea bootstrap to complete..."

      until curl -sf -u 'moby:moby1234' http://git.dockerlabs.xyz/api/v1/user > /dev/null 2>&1; do
        echo "Waiting for moby user to be ready..."
        sleep 2
      done
      echo "moby user is ready."

      until curl -s -u 'moby:moby1234' http://git.dockerlabs.xyz/api/v1/user/keys | grep -q '"moby-key"'; do
        echo "Waiting for SSH key to be registered..."
        sleep 2
      done
      echo "SSH key is registered. Bootstrap complete."

  # Creates all CI secrets in the Gitea repo. Can be skipped by setting SKIP_CI_SECRET_SETUP=true,
  # which is useful for labs that guide users through creating the secrets themselves.
  workspace-ci-secrets:
    content: |
      #!/bin/bash

      if [ "$$SKIP_CI_SECRET_SETUP" = "true" ]; then
        echo "SKIP_CI_SECRET_SETUP is set. Skipping CI secret creation."
        exit 0
      fi

      SECRETS_JSON=$(curl -s -u 'moby:moby1234' http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets)

      # Create Gitea registry secrets
      if echo "$$SECRETS_JSON" | grep -q '"DOCKER_USERNAME"'; then
        echo "Gitea registry secrets already exist. Skipping."
      else
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKER_USERNAME -H "Content-Type: application/json" -d '{"data":"moby"}'
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKER_PASSWORD -H "Content-Type: application/json" -d '{"data":"moby1234"}'
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKER_REGISTRY -H "Content-Type: application/json" -d '{"data":"registry.dockerlabs.xyz"}'
        echo "Gitea registry secrets created."
      fi

      # Create Docker Hub secrets
      if echo "$$SECRETS_JSON" | grep -q '"DOCKERHUB_USERNAME"'; then
        echo "Docker Hub secrets already exist. Skipping."
      else
        HUB_AUTH=$(jq '.auths."https://index.docker.io/v1/".auth' -r ~/.docker/config.json | base64 -d -)
        USERNAME=$(echo $$HUB_AUTH | cut -d: -f1)
        TOKEN=$(echo $$HUB_AUTH | cut -d: -f2)

        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKERHUB_USERNAME -H "Content-Type: application/json" -d '{"data":"'"$$USERNAME"'"}'
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKERHUB_PASSWORD -H "Content-Type: application/json" -d '{"data":"'"$$TOKEN"'"}'
        echo "Docker Hub secrets created."

        unset HUB_AUTH
        unset USERNAME
        unset TOKEN
      fi

      # Create KUBECONFIG secret
      if echo "$$SECRETS_JSON" | grep -q '"KUBECONFIG"'; then
        echo "KUBECONFIG secret already exists. Skipping."
      else
        KUBE_CONFIG=$(cat /kubeconfig/kubeconfig.yaml | sed 's/127.0.0.1/k8s.dockerlabs.xyz/g')
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/KUBECONFIG -H "Content-Type: application/json" -d '{"data":"'"$(echo -n "$$KUBE_CONFIG")"'"}'
        echo "KUBECONFIG secret created."
      fi

  workspace-git-setup:
    content: |
      #!/bin/bash

      # Configure the SSH key for git access
      sudo mkdir -p /home/coder/.ssh
      sudo cp /gitea-outputs/id_rsa /home/coder/.ssh/id_rsa 
      sudo cp /gitea-outputs/id_rsa.pub /home/coder/.ssh/id_rsa.pub
      sudo chown -R coder:coder /home/coder/.ssh

      # Setup git config
      ssh-keyscan -H git.dockerlabs.xyz >> /home/coder/.ssh/known_hosts
      git config --global init.defaultBranch main
      git config --global user.name 'Moby'
      git config --global user.email 'moby@local'
      
      # Commit the initial Labspace project to the git repo
      cd /home/coder/project

      # Ensure .gitignore exists
      touch .gitignore

      # Function to safely append to gitignore
      add_to_gitignore() {
        local entry="$$1"

        # Skip if entry already exists
        if grep -qxF "$$entry" .gitignore 2>/dev/null; then
          return
        fi

        # Ensure file ends with newline before appending (sed adds it if missing)
        sed -i -e '$$a\' .gitignore 2>/dev/null || true

        # Now append the entry
        echo "$$entry" >> .gitignore
      }

      rm -rf .git
      git init
      git remote add origin [email protected]:moby/demo-app.git
      git add -A
      git commit -m 'Initial commit'
      git push -u origin main

  gitea-bootstrap:
    content: |
      #!/bin/bash

      echo "Running"
      while ! curl -sL http://localhost:3000/user/login > /dev/null; do
        sleep 2
      done
      echo "Gitea is up - proceeding with bootstrap"

      # Create the 'moby' user if it doesn't exist
      users=$(su-exec git /usr/local/bin/gitea admin user list)
      if echo "$$users" | grep -q '^moby '; then
          echo "User 'moby' already exists. Skipping account creation."
      else
          su-exec git /usr/local/bin/gitea admin user create --username moby --password 'moby1234' --email admin@local --admin --must-change-password=false

          while ! curl -sL -u 'moby:moby1234' http://localhost:3000/api/v1/user > /dev/null; do
            echo "Waiting for user to be fully ready to use..."
            sleep 2
          done          
      fi
      
      # Check if demo-app repo exists. Create it if it doesn't.
      repo_status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api/v1/repos/moby/demo-app)
      if [ "$$repo_status" = "404" ]; then
          echo "Repository 'moby/demo-app' not found. Creating it..."
          curl -s -H "Content-type: application/json" -u 'moby:moby1234' -d '{"name":"demo-app","private":false}' http://localhost:3000/api/v1/user/repos
          echo "Repository 'moby/demo-app' created."
      else
          echo "Repository 'moby/demo-app' already exists. Skipping creation."
      fi

      # Get and store registration token for the runner
      TOKEN_JSON=$(curl -s -u 'moby:moby1234' http://localhost:3000/api/v1/repos/moby/demo-app/actions/runners/registration-token)
      REGISTRATION_TOKEN=$(echo $$TOKEN_JSON | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
      echo -n $$REGISTRATION_TOKEN > /gitea-outputs/runner_token.txt

      # Create SSH key for user 'moby' if it doesn't exist
      if [ ! -f /gitea-outputs/id_rsa ]; then
          ssh-keygen -t rsa -b 4096 -N "" -f /gitea-outputs/id_rsa
          echo "SSH key generated for user 'moby'."
      fi

      # Add the public key to the user's SSH keys via Gitea API if not already added
      keys=$(curl -s -u 'moby:moby1234' http://localhost:3000/api/v1/user/keys)
      if echo "$$keys" | grep -q "moby-key"; then
          echo "SSH key already added to user 'moby'. Skipping key addition."
      else
        pub_key=$(cat /gitea-outputs/id_rsa.pub)
        curl -s -u 'moby:moby1234' http://localhost:3000/api/v1/user/keys -H "Content-Type: application/json" -d "{\"title\":\"moby-key\",\"key\":\"$$pub_key\"}"
        echo "SSH key added to user 'moby'."
      fi

  # Configure k3s with credentials and TLS settings for the Gitea registry
  k3s-registry-config:
    content: |
      configs:
        "registry.dockerlabs.xyz":
          auth:
            username: moby
            password: moby1234
          tls:
            insecure_skip_verify: true

  workspace-kubeconfig-setup:
    content: |
      #!/bin/bash

      set -e

      # Setup the kubeconfig for the coder user
      mkdir -p ~/.kube
      cat /kubeconfig/kubeconfig.yaml | sed 's/127.0.0.1/k8s.dockerlabs.xyz/g' > ~/.kube/config

  create-zone-file:
    content: |
      #!/bin/sh

      set -e

      IP=$$(dig +short git.dockerlabs.xyz A | grep -E '^[0-9.]+$$' | head -n 1)

      mkdir -p /labspace-support/k8s-setup
      cat << EOF > /labspace-support/k8s-setup/zone-file-config-map.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: coredns-custom
        namespace: kube-system
      data:
        dockerlabs-xyz.server: |
          dockerlabs.xyz:53 {
            log
            errors
            file /etc/coredns/custom/dockerlabs.xyz
          }
        dockerlabs.xyz: |
          \$$ORIGIN dockerlabs.xyz.
          \$$TTL 3600

          @   IN  SOA ns1.dockerlabs.xyz. hostmaster.dockerlabs.xyz. (
                  2026020601 ; serial (YYYYMMDDnn)
                  3600       ; refresh
                  900        ; retry
                  1209600    ; expire
                  3600       ; minimum
          )

              IN  NS  ns1.dockerlabs.xyz.

          ; A records
          @   IN  A   $$IP
          *   IN  A   $$IP
      EOF

  workspace-configure-k3s-coredns:
    content: |
      #!/bin/bash

      kubectl apply -f /etc/labspace-support/k8s-setup/zone-file-config-map.yaml
      if kubectl -n kube-system get deployment coredns > /dev/null 2>&1; then
        kubectl -n kube-system rollout restart deployment/coredns
      fi

Docker commands

docker compose -f oci://dockersamples/labspace-content-dev:v0.25.0-sdlc up

Use the above command to pull and run the Compose file. Learn more.

Images used

Image

A minimal Docker image based on Alpine Linux with a complete package index and only 5 MB in size!


Pulls

1B+

Stars

11554

Last Updated

about 12 hours

Image + 1 more

Traefik, The Cloud Native Edge Router


Pulls

1B+

Stars

3664

Last Updated

1 day

Image


Pulls

50M+

Stars

70

Last Updated

5 days

Image

Gitea: Git with a cup of tea - A painless self-hosted Git service.


Pulls

100M+

Stars

1057

Last Updated

about 4 hours

Image

Deprecated Gitea runner. Moved to gitea/runner


Pulls

5M+

Stars

34

Last Updated

5 months

Image


Pulls

10K+

Stars

0

Last Updated

2 months

Image


Pulls

10K+

Stars

0

Last Updated

2 months

Image


Pulls

10K+

Stars

0

Last Updated

2 months

Image


Pulls

10K+

Stars

0

Last Updated

2 months

Image


Pulls

10K+

Stars

0

Last Updated

2 months

Image


Pulls

10K+

Stars

0

Last Updated

7 months