This is an automated email from the ASF dual-hosted git repository.

shahar1 pushed a commit to branch gha-arc-rebase
in repository https://gitbox.apache.org/repos/asf/airflow-ci-infra.git

commit 061ffcb40c913ad6f28b4679fb1efccf09e3811e
Author: Hussein Awala <[email protected]>
AuthorDate: Sun Jul 23 19:21:43 2023 +0200

    add Github Actions Runners docker image based on Airflow runner release 
managed by Ash
---
 docker/Dockerfile                          | 159 +++++++++++++++++++++++
 docker/docker-shim.sh                      |  36 ++++++
 docker/entrypoint.sh                       |  49 +++++++
 docker/graceful-stop.sh                    | 118 +++++++++++++++++
 docker/hooks/job-completed.d/update-status |  24 ++++
 docker/hooks/job-completed.sh              |  32 +++++
 docker/hooks/job-started.d/update-status   |  24 ++++
 docker/hooks/job-started.sh                |  32 +++++
 docker/logger.sh                           |  92 +++++++++++++
 docker/startup.sh                          | 199 +++++++++++++++++++++++++++++
 docker/update-status                       |  71 ++++++++++
 11 files changed, 836 insertions(+)

diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..cebd9b5
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,159 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+FROM ubuntu:20.04
+
+ARG TARGETPLATFORM=linux/arm64
+ARG RUNNER_VERSION=2.304.0-airflow8
+ARG RUNNER_CONTAINER_HOOKS_VERSION=0.3.2
+ARG RUNNER_OWNER=ashb
+# Docker and Docker Compose arguments
+ARG CHANNEL=stable
+ARG DOCKER_VERSION=20.10.23
+ARG DOCKER_COMPOSE_VERSION=v2.16.0
+ARG DUMB_INIT_VERSION=1.2.5
+
+# Use 1001 and 121 for compatibility with GitHub-hosted runners
+ARG RUNNER_UID=1000
+ARG DOCKER_GID=1001
+
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt-get update -y \
+    && apt-get install -y software-properties-common \
+    && add-apt-repository -y ppa:git-core/ppa \
+    && apt-get update -y \
+    && apt-get install -y --no-install-recommends \
+    build-essential \
+    curl \
+    ca-certificates \
+    dnsutils \
+    ftp \
+    git \
+    git-lfs \
+    iproute2 \
+    iputils-ping \
+    jq \
+    libunwind8 \
+    locales \
+    netcat \
+    openssh-client \
+    parallel \
+    python3-pip \
+    rsync \
+    shellcheck \
+    sudo \
+    telnet \
+    time \
+    tzdata \
+    unzip \
+    upx \
+    wget \
+    zip \
+    zstd \
+    && ln -sf /usr/bin/python3 /usr/bin/python \
+    && ln -sf /usr/bin/pip3 /usr/bin/pip \
+    && rm -rf /var/lib/apt/lists/*
+
+RUN adduser --disabled-password --gecos "" --uid $RUNNER_UID runner \
+    && groupadd docker --gid $DOCKER_GID \
+    && usermod -aG sudo runner \
+    && usermod -aG docker runner \
+    && echo "%sudo   ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers \
+    && echo "Defaults env_keep += \"DEBIAN_FRONTEND\"" >> /etc/sudoers
+
+ENV HOME=/home/runner
+
+RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
+    && if [ "$ARCH" = "arm64" ]; then export ARCH=aarch64 ; fi \
+    && if [ "$ARCH" = "amd64" ] || [ "$ARCH" = "i386" ]; then export 
ARCH=x86_64 ; fi \
+    && curl -fLo /usr/bin/dumb-init 
https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_${ARCH}
 \
+    && chmod +x /usr/bin/dumb-init
+
+ENV RUNNER_ASSETS_DIR=/runnertmp
+RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
+    && if [ "$ARCH" = "amd64" ] || [ "$ARCH" = "x86_64" ] || [ "$ARCH" = 
"i386" ]; then export ARCH=x64 ; fi \
+    && mkdir -p "$RUNNER_ASSETS_DIR" \
+    && cd "$RUNNER_ASSETS_DIR" \
+    && curl -fLo runner.tar.gz 
https://github.com/${RUNNER_OWNER}/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${ARCH}-${RUNNER_VERSION}.tar.gz
 \
+    && tar xzf ./runner.tar.gz \
+    && rm runner.tar.gz \
+    && ./bin/installdependencies.sh \
+    && mv ./externals ./externalstmp \
+    # libyaml-dev is required for ruby/setup-ruby action.
+    # It is installed after installdependencies.sh and before removing 
/var/lib/apt/lists
+    # to avoid rerunning apt-update on its own.
+    && apt-get install -y libyaml-dev \
+    && rm -rf /var/lib/apt/lists/*
+
+ENV RUNNER_TOOL_CACHE=/opt/hostedtoolcache
+RUN mkdir /opt/hostedtoolcache \
+    && chgrp docker /opt/hostedtoolcache \
+    && chmod g+rwx /opt/hostedtoolcache
+
+RUN cd "$RUNNER_ASSETS_DIR" \
+    && curl -fLo runner-container-hooks.zip 
https://github.com/actions/runner-container-hooks/releases/download/v${RUNNER_CONTAINER_HOOKS_VERSION}/actions-runner-hooks-k8s-${RUNNER_CONTAINER_HOOKS_VERSION}.zip
 \
+    && unzip ./runner-container-hooks.zip -d ./k8s \
+    && rm -f runner-container-hooks.zip
+
+RUN set -vx; \
+    export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
+    && if [ "$ARCH" = "arm64" ]; then export ARCH=aarch64 ; fi \
+    && if [ "$ARCH" = "amd64" ] || [ "$ARCH" = "i386" ]; then export 
ARCH=x86_64 ; fi \
+    && curl -fLo docker.tgz 
https://download.docker.com/linux/static/${CHANNEL}/${ARCH}/docker-${DOCKER_VERSION}.tgz
 \
+    && tar zxvf docker.tgz \
+    && install -o root -g root -m 755 docker/docker /usr/bin/docker \
+    && rm -rf docker docker.tgz
+
+RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
+    && if [ "$ARCH" = "arm64" ]; then export ARCH=aarch64 ; fi \
+    && if [ "$ARCH" = "amd64" ] || [ "$ARCH" = "i386" ]; then export 
ARCH=x86_64 ; fi \
+    && mkdir -p /usr/libexec/docker/cli-plugins \
+    && curl -fLo /usr/libexec/docker/cli-plugins/docker-compose 
https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-linux-${ARCH}
 \
+    && chmod +x /usr/libexec/docker/cli-plugins/docker-compose \
+    && ln -s /usr/libexec/docker/cli-plugins/docker-compose 
/usr/bin/docker-compose \
+    && which docker-compose \
+    && docker compose version
+
+# We place the scripts in `/usr/bin` so that users who extend this image can
+# override them with scripts of the same name placed in `/usr/local/bin`.
+COPY entrypoint.sh startup.sh logger.sh graceful-stop.sh update-status 
/usr/bin/
+
+# Copy the docker shim which propagates the docker MTU to underlying networks
+# to replace the docker binary in the PATH.
+COPY docker-shim.sh /usr/local/bin/docker
+
+# Configure hooks folder structure.
+COPY hooks /etc/arc/hooks/
+
+# Add the Python "User Script Directory" to the PATH
+ENV PATH="${PATH}:${HOME}/.local/bin/"
+ENV ImageOS=ubuntu20
+
+RUN echo "PATH=${PATH}" > /etc/environment \
+    && echo "ImageOS=${ImageOS}" >> /etc/environment
+
+# TODO: Remove this once https://github.com/actions/setup-python/issues/705 is 
solved
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+    curl nodejs wget unzip vim git jq build-essential libssl-dev libffi-dev 
python3 python3-venv python3-dev python3-pip
+
+USER runner
+
+ENTRYPOINT ["/bin/bash", "-c"]
+CMD ["entrypoint.sh"]
\ No newline at end of file
diff --git a/docker/docker-shim.sh b/docker/docker-shim.sh
new file mode 100755
index 0000000..f522f8f
--- /dev/null
+++ b/docker/docker-shim.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -Eeuo pipefail
+
+DOCKER=/usr/bin/docker
+if [ ! -e $DOCKER ]; then
+  DOCKER=/home/runner/bin/docker
+fi
+
+if [[ ${ARC_DOCKER_MTU_PROPAGATION:-false} == true ]] &&
+  (($# >= 2)) && [[ $1 == network && $2 == create ]] &&
+  mtu=$($DOCKER network inspect bridge --format '{{index .Options 
"com.docker.network.driver.mtu"}}' 2>/dev/null); then
+  shift 2
+  set -- network create --opt com.docker.network.driver.mtu="$mtu" "$@"
+fi
+
+exec $DOCKER "$@"
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
new file mode 100755
index 0000000..5decfde
--- /dev/null
+++ b/docker/entrypoint.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+source logger.sh
+source graceful-stop.sh
+trap graceful_stop TERM
+
+dumb-init bash <<'SCRIPT' &
+source logger.sh
+
+startup.sh
+SCRIPT
+
+RUNNER_INIT_PID=$!
+log.notice "Runner init started with pid $RUNNER_INIT_PID"
+wait $RUNNER_INIT_PID
+log.notice "Runner init exited. Exiting this process with code 0 so that the 
container and the pod is GC'ed Kubernetes soon."
+
+if [ -f /runner/.runner ]; then
+# If the runner failed with the following error:
+#   √ Connected to GitHub
+#   Failed to create a session. The runner registration has been deleted from 
the server, please re-configure.
+#   Runner listener exit with terminated error, stop the service, no retry 
needed.
+#   Exiting runner...
+# It might have failed to delete the .runner file.
+# We use the existence of the .runner file as the indicator that the runner 
agent has not stopped yet.
+# Remove it by ourselves now, so that the dockerd sidecar prestop won't hang 
waiting for the .runner file to appear.
+  echo "Removing the .runner file"
+  rm -f /runner/.runner
+fi
+
+trap - TERM
diff --git a/docker/graceful-stop.sh b/docker/graceful-stop.sh
new file mode 100755
index 0000000..36431f0
--- /dev/null
+++ b/docker/graceful-stop.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# This should be shorter enough than the terminationGracePeriodSeconds,
+# so that the job is cancelled immediately, instead of hanging for 10 minutes 
or so and failing without any error message.
+RUNNER_GRACEFUL_STOP_TIMEOUT=${RUNNER_GRACEFUL_STOP_TIMEOUT:-15}
+
+graceful_stop() {
+  log.notice "Executing actions-runner-controller's SIGTERM handler."
+  log.notice "Note that if this takes more time than 
terminationGracePeriodSeconds, the runner will be forcefully terminated by 
Kubernetes, which may result in the in-progress workflow job, if any, to fail."
+
+  log.notice "Ensuring dockerd is still running."
+  if ! docker ps -a; then
+    log.warning "Detected configuration error: dockerd should be running but 
is already nowhere. This is wrong. Ensure that your init system to NOT pass 
SIGTERM directly to dockerd!"
+  fi
+
+  # The below procedure atomically removes the runner from GitHub Actions 
service,
+  # to ensure that the runner is not running any job.
+  # This is required to not terminate the actions runner agent while running 
the job.
+  # If we didn't do this atomically, we might end up with a rare race where
+  # the runner agent is terminated while it was about to start a job.
+
+  # `pushd`` is needed to run the config.sh successfully.
+  # Without this the author of this script ended up with errors like the below:
+  #   Cannot connect to server, because config files are missing. Skipping 
removing runner from the server.
+  #   Does not exist. Skipping Removing .credentials
+  #   Does not exist. Skipping Removing .runner
+  if ! pushd /runner; then
+    log.error "Failed to pushd ${RUNNER_HOME}"
+    exit 1
+  fi
+
+  # We need to wait for the registration first.
+  # Otherwise a direct runner pod deletion triggered while the runner 
entrypoint.sh is about to register itself with
+  # config.sh can result in this graceful stop process to get skipped.
+  # In that case, the pod is eventually and forcefully terminated by ARC and 
K8s, resulting
+  # in the possible running workflow job after this graceful stop process 
failed might get cancelled prematurely.
+  log.notice "Waiting for the runner to register first."
+  while ! [ -f /runner/.runner ]; do
+    sleep 1
+  done
+  log.notice "Observed that the runner has been registered."
+
+  if ! /runner/config.sh remove --token "$RUNNER_TOKEN"; then
+    i=0
+    log.notice "Waiting for 
RUNNER_GRACEFUL_STOP_TIMEOUT=$RUNNER_GRACEFUL_STOP_TIMEOUT seconds until the 
runner agent to stop by itself."
+    while [[ $i -lt $RUNNER_GRACEFUL_STOP_TIMEOUT ]]; do
+      sleep 1
+      if ! pgrep Runner.Listener > /dev/null; then
+        log.notice "The runner agent stopped before 
RUNNER_GRACEFUL_STOP_TIMEOUT=$RUNNER_GRACEFUL_STOP_TIMEOUT"
+        break
+      fi
+      i=$((i+1))
+    done
+  fi
+
+  if ! popd; then
+    log.error "Failed to popd from ${RUNNER_HOME}"
+    exit 1
+  fi
+
+  if pgrep Runner.Listener > /dev/null; then
+    # The below procedure fixes the runner to correctly notify the Actions 
service for the cancellation of this runner.
+    # It enables you to see `Error: The operation was canceled.` in the 
worklow job log, in case a job was still running on this runner when the
+    # termination is requested.
+    #
+    # Note though, due to how Actions work, no all job steps gets `Error: The 
operation was canceled.` in the job step logs.
+    # Jobs that were still in the first `Stet up job` step` seem to get 
`Error: A task was canceled.`,
+    #
+    # Anyway, without this, a runer pod is "forcefully" killed by any other 
controller (like cluster-autoscaler) can result in the workflow job to
+    # hang for 10 minutes or so.
+    # After 10 minutes, the Actions UI just shows the failure icon for the 
step, without `Error: The operation was canceled.`,
+    # not even showing `Error: The operation was canceled.`, which is 
confusing.
+    runner_listener_pid=$(pgrep Runner.Listener)
+    log.notice "Sending SIGTERM to the actions runner agent 
($runner_listener_pid)."
+    kill -TERM "$runner_listener_pid"
+
+    log.notice "SIGTERM sent. If the runner is still running a job, you'll 
probably see \"Error: The operation was canceled.\" in its log."
+    log.notice "Waiting for the actions runner agent to stop."
+    while pgrep Runner.Listener > /dev/null; do
+      sleep 1
+    done
+  fi
+
+  # This message is supposed to be output only after the runner agent output:
+  #   2022-08-27 02:04:37Z: Job test3 completed with result: Canceled
+  # because this graceful stopping logic is basically intended to let the 
runner agent have some time
+  # needed to "Cancel" it.
+  # At the times we didn't have this logic, the runner agent was even unable 
to output the Cancelled message hence
+  # unable to gracefully stop, hence the workflow job hanged like forever.
+  log.notice "The actions runner process exited."
+
+  if [ "$RUNNER_INIT_PID" != "" ]; then
+    log.notice "Holding on until runner init (pid $RUNNER_INIT_PID) exits, so 
that there will hopefully be no zombie processes remaining."
+    # We don't need to kill -TERM $RUNNER_INIT_PID as the init is supposed to 
exit by itself once the foreground process(=the runner agent) exists.
+    wait "$RUNNER_INIT_PID" || :
+  fi
+
+  log.notice "Graceful stop completed."
+}
diff --git a/docker/hooks/job-completed.d/update-status 
b/docker/hooks/job-completed.d/update-status
new file mode 100755
index 0000000..fd01665
--- /dev/null
+++ b/docker/hooks/job-completed.d/update-status
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -u
+
+exec update-status Idle
diff --git a/docker/hooks/job-completed.sh b/docker/hooks/job-completed.sh
new file mode 100755
index 0000000..0fd2abe
--- /dev/null
+++ b/docker/hooks/job-completed.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -Eeuo pipefail
+
+# shellcheck source=runner/logger.sh
+source logger.sh
+
+log.debug "Running ARC Job Completed Hooks"
+
+for hook in /etc/arc/hooks/job-completed.d/*; do
+  log.debug "Running hook: $hook"
+  "$hook" "$@"
+done
diff --git a/docker/hooks/job-started.d/update-status 
b/docker/hooks/job-started.d/update-status
new file mode 100755
index 0000000..17d2d08
--- /dev/null
+++ b/docker/hooks/job-started.d/update-status
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -u
+
+exec update-status Running "Run $GITHUB_RUN_ID from $GITHUB_REPOSITORY"
diff --git a/docker/hooks/job-started.sh b/docker/hooks/job-started.sh
new file mode 100644
index 0000000..3e08e74
--- /dev/null
+++ b/docker/hooks/job-started.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -Eeuo pipefail
+
+# shellcheck source=runner/logger.sh
+source logger.sh
+
+log.debug "Running ARC Job Started Hooks"
+
+for hook in /etc/arc/hooks/job-started.d/*; do
+  log.debug "Running hook: $hook"
+  "$hook" "$@"
+done
diff --git a/docker/logger.sh b/docker/logger.sh
new file mode 100755
index 0000000..08c0283
--- /dev/null
+++ b/docker/logger.sh
@@ -0,0 +1,92 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# We are not using `set -Eeuo pipefail` here because this file is sourced by
+# other scripts that might not be ready for a strict Bash setup. The functions
+# in this file do not require it, because they are not handling signals, have
+# no external calls that can fail (printf as well as date failures are 
ignored),
+# are not using any variables that need to be set, and are not using any pipes.
+
+# This logger implementation can be replaced with another logger implementation
+# by placing a script called `logger.sh` in `/usr/local/bin` of the image. The
+# only requirement for the script is that it defines the following functions:
+#
+# - `log.debug`
+# - `log.notice`
+# - `log.warning`
+# - `log.error`
+# - `log.success`
+#
+# Each function **MUST** accept an arbitrary amount of arguments that make up
+# the (unstructured) logging message.
+#
+# Additionally the following environment variables **SHOULD** be supported to
+# disable their corresponding log entries, the value of the variables **MUST**
+# not matter the mere fact that they are set is all that matters:
+#
+# - `LOG_DEBUG_DISABLED`
+# - `LOG_NOTICE_DISABLED`
+# - `LOG_WARNING_DISABLED`
+# - `LOG_ERROR_DISABLED`
+# - `LOG_SUCCESS_DISABLED`
+
+# The log format is constructed in a way that it can easily be parsed with
+# standard tools and simple string manipulations; pattern and example:
+#
+#     YYYY-MM-DD hh:mm:ss.SSS  $level --- $message
+#     2022-03-19 10:01:23.172  NOTICE --- example message
+#
+# This function is an implementation detail and **MUST NOT** be called from
+# outside this script (which is possible if the file is sourced).
+__log() {
+  local color instant level
+
+  color=${1:?missing required <color> argument}
+  shift
+
+  level=${FUNCNAME[1]} # `main` if called from top-level
+  level=${level#log.} # substring after `log.`
+  level=${level^^} # UPPERCASE
+
+  if [[ ! -v "LOG_${level}_DISABLED" ]]; then
+    instant=$(date '+%F %T.%-3N' 2>/dev/null || :)
+
+    # https://no-color.org/
+    if [[ -v NO_COLOR ]]; then
+      printf -- '%s  %s --- %s\n' "$instant" "$level" "$*" 1>&2 || :
+    else
+      printf -- '\033[0;%dm%s  %s --- %s\033[0m\n' "$color" "$instant" 
"$level" "$*" 1>&2 || :
+    fi
+  fi
+}
+
+# To log with a dynamic level use standard Bash capabilities:
+#
+#     level=notice
+#     command || level=error
+#     "log.$level" message
+#
+# @formatter:off
+log.debug   () { __log 37 "$@"; } # white
+log.notice  () { __log 34 "$@"; } # blue
+log.warning () { __log 33 "$@"; } # yellow
+log.error   () { __log 31 "$@"; } # red
+log.success () { __log 32 "$@"; } # green
+# @formatter:on
diff --git a/docker/startup.sh b/docker/startup.sh
new file mode 100755
index 0000000..a955127
--- /dev/null
+++ b/docker/startup.sh
@@ -0,0 +1,199 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+source logger.sh
+
+RUNNER_ASSETS_DIR=${RUNNER_ASSETS_DIR:-/runnertmp}
+RUNNER_HOME=${RUNNER_HOME:-/runner}
+
+# Let GitHub runner execute these hooks. These environment variables are used 
by GitHub's Runner as described here
+# 
https://github.com/actions/runner/blob/main/docs/adrs/1751-runner-job-hooks.md
+# Scripts referenced in the ACTIONS_RUNNER_HOOK_ environment variables must 
end in .sh or .ps1
+# for it to become a valid hook script, otherwise GitHub will fail to run the 
hook
+export ACTIONS_RUNNER_HOOK_JOB_STARTED=/etc/arc/hooks/job-started.sh
+export ACTIONS_RUNNER_HOOK_JOB_COMPLETED=/etc/arc/hooks/job-completed.sh
+
+if [ -n "${STARTUP_DELAY_IN_SECONDS}" ]; then
+  log.notice "Delaying startup by ${STARTUP_DELAY_IN_SECONDS} seconds"
+  sleep "${STARTUP_DELAY_IN_SECONDS}"
+fi
+
+if [ -z "${GITHUB_URL}" ]; then
+  log.debug 'Working with public GitHub'
+  GITHUB_URL="https://github.com/";
+else
+  length=${#GITHUB_URL}
+  last_char=${GITHUB_URL:length-1:1}
+
+  [[ $last_char != "/" ]] && GITHUB_URL="$GITHUB_URL/"; :
+  log.debug "Github endpoint URL ${GITHUB_URL}"
+fi
+
+if [ -z "${RUNNER_NAME}" ]; then
+  log.error 'RUNNER_NAME must be set'
+  exit 1
+fi
+
+if [ -n "${RUNNER_ORG}" ] && [ -n "${RUNNER_REPO}" ] && [ -n 
"${RUNNER_ENTERPRISE}" ]; then
+  ATTACH="${RUNNER_ORG}/${RUNNER_REPO}"
+elif [ -n "${RUNNER_ORG}" ]; then
+  ATTACH="${RUNNER_ORG}"
+elif [ -n "${RUNNER_REPO}" ]; then
+  ATTACH="${RUNNER_REPO}"
+elif [ -n "${RUNNER_ENTERPRISE}" ]; then
+  ATTACH="enterprises/${RUNNER_ENTERPRISE}"
+else
+  log.error 'At least one of RUNNER_ORG, RUNNER_REPO, or RUNNER_ENTERPRISE 
must be set'
+  exit 1
+fi
+
+if [ -z "${RUNNER_TOKEN}" ]; then
+  log.error 'RUNNER_TOKEN must be set'
+  exit 1
+fi
+
+if [ -z "${RUNNER_REPO}" ] && [ -n "${RUNNER_GROUP}" ];then
+  RUNNER_GROUPS=${RUNNER_GROUP}
+fi
+
+# Hack due to 
https://github.com/actions/actions-runner-controller/issues/252#issuecomment-758338483
+if [ ! -d "${RUNNER_HOME}" ]; then
+  log.error "$RUNNER_HOME should be an emptyDir mount. Please fix the pod 
spec."
+  exit 1
+fi
+
+# if this is not a testing environment
+if [[ "${UNITTEST:-}" == '' ]]; then
+  sudo chown -R runner:docker "$RUNNER_HOME"
+  # enable dotglob so we can copy a ".env" file to load in env vars as part of 
the service startup if one is provided
+  # loading a .env from the root of the service is part of the actions/runner 
logic
+  shopt -s dotglob
+  # use cp instead of mv to avoid issues when src and dst are on different 
devices
+  cp -r "$RUNNER_ASSETS_DIR"/* "$RUNNER_HOME"/
+  shopt -u dotglob
+fi
+
+if ! cd "${RUNNER_HOME}"; then
+  log.error "Failed to cd into ${RUNNER_HOME}"
+  exit 1
+fi
+
+# past that point, it's all relative pathes from /runner
+
+config_args=()
+if [ "${RUNNER_FEATURE_FLAG_ONCE:-}" != "true" ] && [ "${RUNNER_EPHEMERAL}" == 
"true" ]; then
+  config_args+=(--ephemeral)
+  log.debug 'Passing --ephemeral to config.sh to enable the ephemeral runner.'
+fi
+if [ "${DISABLE_RUNNER_UPDATE:-}" == "true" ]; then
+  config_args+=(--disableupdate)
+  log.debug 'Passing --disableupdate to config.sh to disable automatic runner 
updates.'
+fi
+
+update-status "Registering"
+
+retries_left=10
+while [[ ${retries_left} -gt 0 ]]; do
+  log.debug 'Configuring the runner.'
+  ./config.sh --unattended --replace \
+    --name "${RUNNER_NAME}" \
+    --url "${GITHUB_URL}${ATTACH}" \
+    --token "${RUNNER_TOKEN}" \
+    --runnergroup "${RUNNER_GROUPS}" \
+    --labels "${RUNNER_LABELS}" \
+    --work "${RUNNER_WORKDIR}" "${config_args[@]}"
+
+  if [ -f .runner ]; then
+    log.debug 'Runner successfully configured.'
+    break
+  fi
+
+  log.debug 'Configuration failed. Retrying'
+  retries_left=$((retries_left - 1))
+  sleep 1
+done
+
+if [ ! -f .runner ]; then
+  # we couldn't configure and register the runner; no point continuing
+  log.error 'Configuration failed!'
+  exit 2
+fi
+
+cat .runner
+# Note: the `.runner` file's content should be something like the below:
+#
+# $ cat /runner/.runner
+# {
+# "agentId": 117, #=> corresponds to the ID of the runner
+# "agentName": "THE_RUNNER_POD_NAME",
+# "poolId": 1,
+# "poolName": "Default",
+# "serverUrl": 
"https://pipelines.actions.githubusercontent.com/SOME_RANDOM_ID";,
+# "gitHubUrl": "https://github.com/USER/REPO";,
+# "workFolder": "/some/work/dir" #=> corresponds to Runner.Spec.WorkDir
+# }
+#
+# Especially `agentId` is important, as other than listing all the runners in 
the repo,
+# this is the only change we could get the exact runnner ID which can be 
useful for further
+# GitHub API call like the below. Note that 171 is the agentId seen above.
+#   curl \
+#     -H "Accept: application/vnd.github.v3+json" \
+#     -H "Authorization: bearer ${GITHUB_TOKEN}"
+#     https://api.github.com/repos/USER/REPO/actions/runners/171
+
+# Hack due to the DinD volumes
+if [ -z "${UNITTEST:-}" ] && [ -e ./externalstmp ]; then
+  mkdir -p ./externals
+  mv ./externalstmp/* ./externals/
+fi
+
+WAIT_FOR_DOCKER_SECONDS=${WAIT_FOR_DOCKER_SECONDS:-120}
+if [[ "${DISABLE_WAIT_FOR_DOCKER}" != "true" ]] && [[ "${DOCKER_ENABLED}" == 
"true" ]]; then
+    log.debug 'Docker enabled runner detected and Docker daemon wait is 
enabled'
+    log.debug "Waiting until Docker is available or the timeout of 
${WAIT_FOR_DOCKER_SECONDS} seconds is reached"
+    if ! timeout "${WAIT_FOR_DOCKER_SECONDS}s" bash -c 'until docker ps ;do 
sleep 1; done'; then
+      log.notice "Docker has not become available within 
${WAIT_FOR_DOCKER_SECONDS} seconds. Exiting with status 1."
+      exit 1
+    fi
+else
+  log.notice 'Docker wait check skipped. Either Docker is disabled or the wait 
is disabled, continuing with entrypoint'
+fi
+
+# Unset entrypoint environment variables so they don't leak into the runner 
environment
+unset RUNNER_NAME RUNNER_REPO RUNNER_TOKEN STARTUP_DELAY_IN_SECONDS 
DISABLE_WAIT_FOR_DOCKER
+
+# Docker ignores PAM and thus never loads the system environment variables that
+# are meant to be set in every environment of every user. We emulate the PAM
+# behavior by reading the environment variables without interpreting them.
+#
+# https://github.com/actions/actions-runner-controller/issues/1135
+# https://github.com/actions/runner/issues/1703
+
+# /etc/environment may not exist when running unit tests depending on the 
platform being used
+# (e.g. Mac OS) so we just skip the mapping entirely
+if [ -z "${UNITTEST:-}" ]; then
+  mapfile -t env </etc/environment
+fi
+
+log.notice "WARNING LATEST TAG HAS BEEN DEPRECATED. SEE GITHUB ISSUE FOR 
DETAILS:"
+log.notice "https://github.com/actions/actions-runner-controller/issues/2056";
+
+update-status "Idle"
+exec env -- "${env[@]}" ./run.sh
diff --git a/docker/update-status b/docker/update-status
new file mode 100755
index 0000000..40e8bd9
--- /dev/null
+++ b/docker/update-status
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -Eeuo pipefail
+
+if [[ ${1:-} == '' ]]; then
+  # shellcheck source=runner/logger.sh
+  source logger.sh
+  log.error "Missing required argument -- '<phase>'"
+  exit 64
+fi
+
+if [[ ${RUNNER_STATUS_UPDATE_HOOK:-false} == true ]]; then
+
+    
apiserver=https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}
+    serviceaccount=/var/run/secrets/kubernetes.io/serviceaccount
+    namespace=$(cat ${serviceaccount}/namespace)
+    token=$(cat ${serviceaccount}/token)
+    phase=$1
+    message=${2:-}
+
+    data=$(jq -n --arg phase "$phase" \
+      --arg message "$message" \
+      --arg workflow_repository "${GITHUB_REPOSITORY:-}" \
+      --arg workflow_repository_owner "${GITHUB_REPOSITORY_OWNER:-}" \
+      --arg workflow_name "${GITHUB_WORKFLOW:-}" \
+      --arg workflow_run_id "${GITHUB_RUN_ID:-}" \
+      --arg workflow_run_number "${GITHUB_RUN_NUMBER:-}" \
+      --arg workflow_job "${GITHUB_JOB:-}" \
+      --arg workflow_action "${GITHUB_ACTION:-}" \
+      '
+       .status.phase = $phase
+     | .status.message = $message
+     | .status.workflow.name = $workflow_name
+     | .status.workflow.runID = $workflow_run_id
+     | .status.workflow.runNumber = $workflow_run_number
+     | .status.workflow.repository = $workflow_repository
+     | .status.workflow.repositoryOwner = $workflow_repository_owner
+     | .status.workflow.job = $workflow_job
+     | .status.workflow.action = $workflow_action
+      ')
+      
+    echo "$data" | curl \
+        --cacert ${serviceaccount}/ca.crt \
+        --data @- \
+        --noproxy '*' \
+        --header "Content-Type: application/merge-patch+json" \
+        --header "Authorization: Bearer ${token}" \
+        --show-error \
+        --silent \
+        --request PATCH \
+        
"${apiserver}/apis/actions.summerwind.dev/v1alpha1/namespaces/${namespace}/runners/${HOSTNAME}/status"
 \
+        1>/dev/null
+fi
\ No newline at end of file

Reply via email to