mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
11 Commits
v2.272.0
...
users/tihu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8f3726265 | ||
|
|
ef72239ff8 | ||
|
|
993357df7d | ||
|
|
c62ab23bdd | ||
|
|
9d48d2be87 | ||
|
|
69aa8d8984 | ||
|
|
7da6739eae | ||
|
|
58afa42109 | ||
|
|
3dc52b28af | ||
|
|
993edc3172 | ||
|
|
6395efe7e0 |
40
.github/workflows/build.yml
vendored
40
.github/workflows/build.yml
vendored
@@ -18,28 +18,12 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ]
|
||||
runtime: [ linux-x64 ]
|
||||
include:
|
||||
- runtime: linux-x64
|
||||
os: ubuntu-latest
|
||||
devScript: ./dev.sh
|
||||
|
||||
- runtime: linux-arm64
|
||||
os: ubuntu-latest
|
||||
devScript: ./dev.sh
|
||||
|
||||
- runtime: linux-arm
|
||||
os: ubuntu-latest
|
||||
devScript: ./dev.sh
|
||||
|
||||
- runtime: osx-x64
|
||||
os: macOS-latest
|
||||
devScript: ./dev.sh
|
||||
|
||||
- runtime: win-x64
|
||||
os: windows-latest
|
||||
devScript: ./dev
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
@@ -50,13 +34,6 @@ jobs:
|
||||
${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
|
||||
working-directory: src
|
||||
|
||||
# Run tests
|
||||
- name: L0
|
||||
run: |
|
||||
${{ matrix.devScript }} test
|
||||
working-directory: src
|
||||
if: matrix.runtime != 'linux-arm64' && matrix.runtime != 'linux-arm'
|
||||
|
||||
# Create runner package tar.gz/zip
|
||||
- name: Package Release
|
||||
if: github.event_name != 'pull_request'
|
||||
@@ -71,3 +48,18 @@ jobs:
|
||||
with:
|
||||
name: runner-package-${{ matrix.runtime }}
|
||||
path: _package
|
||||
|
||||
- name: Build old version
|
||||
run: |
|
||||
echo 2.270.0 > runnerversion
|
||||
${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
|
||||
${{ matrix.devScript }} package Release
|
||||
working-directory: src
|
||||
|
||||
# Upload runner package tar.gz/zip as artifact
|
||||
- name: Publish Artifact old
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: runner-package-${{ matrix.runtime }}-old
|
||||
path: _package
|
||||
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Runner CD
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- releaseVersion
|
||||
|
||||
57
Dockerfile
Normal file
57
Dockerfile
Normal file
@@ -0,0 +1,57 @@
|
||||
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-buster-slim
|
||||
|
||||
ENV RUNNER_CONFIG_URL=""
|
||||
ENV GITHUB_PAT=""
|
||||
ENV RUNNER_NAME=""
|
||||
ENV RUNNER_GROUP=""
|
||||
ENV RUNNER_LABELS=""
|
||||
# ENV GITHUB_RUNNER_SCOPE=""
|
||||
# ENV GITHUB_SERVER_URL=""
|
||||
# ENV GITHUB_API_URL=""
|
||||
# ENV K8S_HOST_IP=""
|
||||
|
||||
RUN apt-get update --fix-missing \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
jq \
|
||||
apt-utils \
|
||||
apt-transport-https \
|
||||
unzip \
|
||||
net-tools\
|
||||
gnupg2\
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install kubectl
|
||||
RUN curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
|
||||
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee -a /etc/apt/sources.list.d/kubernetes.list && \
|
||||
apt-get update && apt-get -y install --no-install-recommends kubectl
|
||||
|
||||
# Install docker
|
||||
RUN curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
RUN sh get-docker.sh
|
||||
|
||||
|
||||
# Allow runner to run as root
|
||||
ENV RUNNER_ALLOW_RUNASROOT=1
|
||||
|
||||
# Directory for runner to operate in
|
||||
RUN mkdir /actions-runner
|
||||
WORKDIR /actions-runner
|
||||
COPY ./src/Misc/download-runner.sh /actions-runner/download-runner.sh
|
||||
COPY ./src/Misc/entrypoint.sh /actions-runner/entrypoint.sh
|
||||
# COPY ./src/Misc/jobstart.sh /actions-runner/jobstart.sh
|
||||
# COPY ./src/Misc/jobrunning.sh /actions-runner/jobrunning.sh
|
||||
# COPY ./src/Misc/jobcomplete.sh /actions-runner/jobcomplete.sh
|
||||
COPY ./src/Misc/runner_lifecycle.sh /actions-runner/runner_lifecycle.sh
|
||||
|
||||
RUN /actions-runner/download-runner.sh
|
||||
RUN rm -f /actions-runner/download-runner.sh
|
||||
|
||||
# ENV _INTERNAL_JOBSTART_NOTIFICATION=/actions-runner/jobstart.sh
|
||||
# ENV _INTERNAL_JOBRUNNING_NOTIFICATION=/actions-runner/jobrunning.sh
|
||||
# ENV _INTERNAL_JOBCOMPLETE_NOTIFICATION=/actions-runner/jobcomplete.sh
|
||||
ENV _INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION=/actions-runner/runner_lifecycle.sh
|
||||
|
||||
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
46
Dockerfile.dind
Normal file
46
Dockerfile.dind
Normal file
@@ -0,0 +1,46 @@
|
||||
FROM docker:19.03
|
||||
|
||||
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies
|
||||
RUN set -eux; \
|
||||
apk add --no-cache \
|
||||
btrfs-progs \
|
||||
e2fsprogs \
|
||||
e2fsprogs-extra \
|
||||
iptables \
|
||||
openssl \
|
||||
shadow-uidmap \
|
||||
xfsprogs \
|
||||
xz \
|
||||
# pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation)
|
||||
pigz \
|
||||
; \
|
||||
# only install zfs if it's available for the current architecture
|
||||
# https://git.alpinelinux.org/cgit/aports/tree/main/zfs/APKBUILD?h=3.6-stable#n9 ("all !armhf !ppc64le" as of 2017-11-01)
|
||||
# "apk info XYZ" exits with a zero exit code but no output when the package exists but not for this arch
|
||||
if zfs="$(apk info --no-cache --quiet zfs)" && [ -n "$zfs" ]; then \
|
||||
apk add --no-cache zfs; \
|
||||
fi
|
||||
|
||||
# TODO aufs-tools
|
||||
|
||||
# set up subuid/subgid so that "--userns-remap=default" works out-of-the-box
|
||||
RUN set -x \
|
||||
&& addgroup -S dockremap \
|
||||
&& adduser -S -G dockremap dockremap \
|
||||
&& echo 'dockremap:165536:65536' >> /etc/subuid \
|
||||
&& echo 'dockremap:165536:65536' >> /etc/subgid
|
||||
|
||||
# https://github.com/docker/docker/tree/master/hack/dind
|
||||
ENV DIND_COMMIT ed89041433a031cafc0a0f19cfe573c31688d377
|
||||
|
||||
RUN set -eux; \
|
||||
wget -O /usr/local/bin/dind "https://raw.githubusercontent.com/docker/docker/${DIND_COMMIT}/hack/dind"; \
|
||||
chmod +x /usr/local/bin/dind
|
||||
|
||||
COPY dockerd-entrypoint.sh /usr/local/bin/
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
EXPOSE 6788 6789
|
||||
|
||||
ENTRYPOINT ["dockerd-entrypoint.sh"]
|
||||
CMD []
|
||||
14
autoscalev0.yaml
Normal file
14
autoscalev0.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: actions.summerwind.dev/v1alpha1
|
||||
kind: RunnerDeployment
|
||||
metadata:
|
||||
name: auto-scale-runners
|
||||
spec:
|
||||
replicas: 1
|
||||
maxRunnerLimit: 5
|
||||
template:
|
||||
spec:
|
||||
configURL: https://github.com/bbq-beets/ting-test
|
||||
githubTokenSecretKeyRef:
|
||||
name: githubtoken
|
||||
key: GITHUB_PAT
|
||||
|
||||
63
deployment.yaml
Normal file
63
deployment.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: runner-pod
|
||||
labels:
|
||||
name: runner-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: runner-pod
|
||||
image: huangtingluo/autoscale-runner:v0.0
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: GITHUB_PAT
|
||||
value: 62c13e14e947958516c103a9584f66227697c447
|
||||
- name: GITHUB_RUNNER_SCOPE
|
||||
value: monalisa/main123
|
||||
- name: K8S_HOST_IP
|
||||
value: "192.168.120.1"
|
||||
|
||||
# apiVersion: apps/v1
|
||||
# kind: Deployment
|
||||
# metadata:
|
||||
# name: runner-deployment
|
||||
# spec:
|
||||
# replicas: 1
|
||||
# selector:
|
||||
# matchLabels:
|
||||
# app: runners
|
||||
# template:
|
||||
# metadata:
|
||||
# labels:
|
||||
# app: runners
|
||||
# spec:
|
||||
# # hostNetwork: true
|
||||
# # volumes:
|
||||
# # - name: docker-storage
|
||||
# # emptyDir: {}
|
||||
# # containers:
|
||||
# # - name: docker-host
|
||||
# # image: docker:18.05-dind
|
||||
# # imagePullPolicy: Always
|
||||
# # securityContext:
|
||||
# # privileged: true
|
||||
# # volumeMounts:
|
||||
# # - name: docker-storage
|
||||
# # mountPath: /var/lib/docker
|
||||
|
||||
# # hostNetwork: true
|
||||
# containers:
|
||||
# - name: runner
|
||||
# image: huangtingluo/autoscale-runner:v0.0
|
||||
# imagePullPolicy: Always
|
||||
# env:
|
||||
# - name: GITHUB_PAT
|
||||
# value: 62c13e14e947958516c103a9584f66227697c447
|
||||
# - name: GITHUB_RUNNER_SCOPE
|
||||
# value: monalisa/main123
|
||||
# - name: K8S_HOST_IP
|
||||
# value: "192.168.120.1"
|
||||
# resources:
|
||||
# limits:
|
||||
# memory: "128Mi"
|
||||
# cpu: "500m"
|
||||
186
dockerd-entrypoint.sh
Executable file
186
dockerd-entrypoint.sh
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
_tls_ensure_private() {
|
||||
local f="$1"; shift
|
||||
[ -s "$f" ] || openssl genrsa -out "$f" 4096
|
||||
}
|
||||
_tls_san() {
|
||||
{
|
||||
ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }'
|
||||
{
|
||||
cat /etc/hostname
|
||||
echo 'docker'
|
||||
echo 'localhost'
|
||||
hostname -f
|
||||
hostname -s
|
||||
} | sed 's/^/DNS:/'
|
||||
[ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
|
||||
} | sort -u | xargs printf '%s,' | sed "s/,\$//"
|
||||
}
|
||||
_tls_generate_certs() {
|
||||
local dir="$1"; shift
|
||||
|
||||
# if ca/key.pem || !ca/cert.pem, generate CA public if necessary
|
||||
# if ca/key.pem, generate server public
|
||||
# if ca/key.pem, generate client public
|
||||
# (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
|
||||
|
||||
# https://github.com/FiloSottile/mkcert/issues/174
|
||||
local certValidDays='825'
|
||||
|
||||
if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
|
||||
# if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
|
||||
mkdir -p "$dir/ca"
|
||||
_tls_ensure_private "$dir/ca/key.pem"
|
||||
openssl req -new -key "$dir/ca/key.pem" \
|
||||
-out "$dir/ca/cert.pem" \
|
||||
-subj '/CN=docker:dind CA' -x509 -days "$certValidDays"
|
||||
fi
|
||||
|
||||
if [ -s "$dir/ca/key.pem" ]; then
|
||||
# if we have a CA private key, we should create/manage a server key
|
||||
mkdir -p "$dir/server"
|
||||
_tls_ensure_private "$dir/server/key.pem"
|
||||
openssl req -new -key "$dir/server/key.pem" \
|
||||
-out "$dir/server/csr.pem" \
|
||||
-subj '/CN=docker:dind server'
|
||||
cat > "$dir/server/openssl.cnf" <<-EOF
|
||||
[ x509_exts ]
|
||||
subjectAltName = $(_tls_san)
|
||||
EOF
|
||||
openssl x509 -req \
|
||||
-in "$dir/server/csr.pem" \
|
||||
-CA "$dir/ca/cert.pem" \
|
||||
-CAkey "$dir/ca/key.pem" \
|
||||
-CAcreateserial \
|
||||
-out "$dir/server/cert.pem" \
|
||||
-days "$certValidDays" \
|
||||
-extfile "$dir/server/openssl.cnf" \
|
||||
-extensions x509_exts
|
||||
cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
|
||||
openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
|
||||
fi
|
||||
|
||||
if [ -s "$dir/ca/key.pem" ]; then
|
||||
# if we have a CA private key, we should create/manage a client key
|
||||
mkdir -p "$dir/client"
|
||||
_tls_ensure_private "$dir/client/key.pem"
|
||||
chmod 0644 "$dir/client/key.pem" # openssl defaults to 0600 for the private key, but this one needs to be shared with arbitrary client contexts
|
||||
openssl req -new \
|
||||
-key "$dir/client/key.pem" \
|
||||
-out "$dir/client/csr.pem" \
|
||||
-subj '/CN=docker:dind client'
|
||||
cat > "$dir/client/openssl.cnf" <<-'EOF'
|
||||
[ x509_exts ]
|
||||
extendedKeyUsage = clientAuth
|
||||
EOF
|
||||
openssl x509 -req \
|
||||
-in "$dir/client/csr.pem" \
|
||||
-CA "$dir/ca/cert.pem" \
|
||||
-CAkey "$dir/ca/key.pem" \
|
||||
-CAcreateserial \
|
||||
-out "$dir/client/cert.pem" \
|
||||
-days "$certValidDays" \
|
||||
-extfile "$dir/client/openssl.cnf" \
|
||||
-extensions x509_exts
|
||||
cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
|
||||
openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
|
||||
fi
|
||||
}
|
||||
|
||||
# no arguments passed
|
||||
# or first arg is `-f` or `--some-option`
|
||||
if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
|
||||
# set "dockerSocket" to the default "--host" *unix socket* value (for both standard or rootless)
|
||||
uid="$(id -u)"
|
||||
if [ "$uid" = '0' ]; then
|
||||
dockerSocket='unix:///var/run/docker.sock'
|
||||
else
|
||||
# if we're not root, we must be trying to run rootless
|
||||
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
|
||||
dockerSocket="unix://$XDG_RUNTIME_DIR/docker.sock"
|
||||
fi
|
||||
case "${DOCKER_HOST:-}" in
|
||||
unix://*)
|
||||
dockerSocket="$DOCKER_HOST"
|
||||
;;
|
||||
esac
|
||||
|
||||
# add our default arguments
|
||||
if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \
|
||||
&& _tls_generate_certs "$DOCKER_TLS_CERTDIR" \
|
||||
&& [ -s "$DOCKER_TLS_CERTDIR/server/ca.pem" ] \
|
||||
&& [ -s "$DOCKER_TLS_CERTDIR/server/cert.pem" ] \
|
||||
&& [ -s "$DOCKER_TLS_CERTDIR/server/key.pem" ] \
|
||||
; then
|
||||
# generate certs and use TLS if requested/possible (default in 19.03+)
|
||||
set -- dockerd \
|
||||
--host="$dockerSocket" \
|
||||
--host=tcp://0.0.0.0:6789 \
|
||||
--tlsverify \
|
||||
--tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \
|
||||
--tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \
|
||||
--tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \
|
||||
"$@"
|
||||
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:6789:6789/tcp"
|
||||
else
|
||||
# TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs
|
||||
set -- dockerd \
|
||||
--host="$dockerSocket" \
|
||||
--host=tcp://0.0.0.0:6788 \
|
||||
"$@"
|
||||
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:6788:6788/tcp"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = 'dockerd' ]; then
|
||||
# explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file)
|
||||
find /run /var/run -iname 'docker*.pid' -delete || :
|
||||
|
||||
uid="$(id -u)"
|
||||
if [ "$uid" != '0' ]; then
|
||||
# if we're not root, we must be trying to run rootless
|
||||
if ! command -v rootlesskit > /dev/null; then
|
||||
echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)"
|
||||
exit 1
|
||||
fi
|
||||
user="$(id -un 2>/dev/null || :)"
|
||||
if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then
|
||||
echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid"
|
||||
exit 1
|
||||
fi
|
||||
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
|
||||
export XDG_RUNTIME_DIR
|
||||
if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then
|
||||
echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid"
|
||||
exit 1
|
||||
fi
|
||||
if [ -f /proc/sys/kernel/unprivileged_userns_clone ] && unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone)" && [ "$unprivClone" != '1' ]; then
|
||||
echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1"
|
||||
exit 1
|
||||
fi
|
||||
if [ -f /proc/sys/user/max_user_namespaces ] && maxUserns="$(cat /proc/sys/user/max_user_namespaces)" && [ "$maxUserns" = '0' ]; then
|
||||
echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value"
|
||||
exit 1
|
||||
fi
|
||||
# TODO overlay support detection?
|
||||
exec rootlesskit \
|
||||
--net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \
|
||||
--mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \
|
||||
--disable-host-loopback \
|
||||
--port-driver=builtin \
|
||||
--copy-up=/etc \
|
||||
--copy-up=/run \
|
||||
${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \
|
||||
"$@"
|
||||
elif [ -x '/usr/local/bin/dind' ]; then
|
||||
# if we have the (mostly defunct now) Docker-in-Docker wrapper script, use it
|
||||
set -- '/usr/local/bin/dind' "$@"
|
||||
fi
|
||||
else
|
||||
# if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too
|
||||
set -- docker-entrypoint.sh "$@"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
48
ephemeralJob.yaml
Normal file
48
ephemeralJob.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
apiVersion: batch.github.actions/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: cronjob-sample
|
||||
spec:
|
||||
schedule: "*/1 * * * *"
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: k8srunner
|
||||
image: huangtingluo/autoscale-runner:v0.0
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: GITHUB_PAT
|
||||
value: 62c13e14e947958516c103a9584f66227697c447
|
||||
- name: GITHUB_RUNNER_SCOPE
|
||||
value: monalisa/main123
|
||||
restartPolicy: Never
|
||||
# spec:
|
||||
# containers:
|
||||
# - name: hello
|
||||
# image: busybox
|
||||
# args:
|
||||
# - /bin/sh
|
||||
# - -c
|
||||
# - date; echo Hello from the Kubernetes cluster
|
||||
# restartPolicy: Never
|
||||
# jobTemplate:
|
||||
# spec:
|
||||
# template:
|
||||
# spec:
|
||||
# hostNetwork: true
|
||||
# containers:
|
||||
# - name: k8srunner
|
||||
# image: huangtingluo/autoscale-runner:v0.0
|
||||
# imagePullPolicy: Always
|
||||
# env:
|
||||
# - name: GITHUB_PAT
|
||||
# value: 62c13e14e947958516c103a9584f66227697c447
|
||||
# - name: GITHUB_RUNNER_SCOPE
|
||||
# value: monalisa/main123
|
||||
# restartPolicy: Never
|
||||
# backoffLimit: 1
|
||||
# completions: 0
|
||||
# parallelism: 3
|
||||
56
hpa-v2.yaml
Normal file
56
hpa-v2.yaml
Normal file
@@ -0,0 +1,56 @@
|
||||
apiVersion: v1
|
||||
items:
|
||||
- apiVersion: autoscaling/v2beta2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
creationTimestamp: "2020-08-05T19:14:04Z"
|
||||
name: runner-deployment
|
||||
namespace: default
|
||||
resourceVersion: "167447"
|
||||
selfLink: /apis/autoscaling/v2beta2/namespaces/default/horizontalpodautoscalers/runner-deployment
|
||||
uid: 54d86943-eca9-468c-9698-c843f6b6183a
|
||||
spec:
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Object
|
||||
object:
|
||||
metric:
|
||||
name: test-metric
|
||||
describedObject:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
name: kubernetes
|
||||
target:
|
||||
type: Value
|
||||
value: 300m
|
||||
- resource:
|
||||
name: cpu
|
||||
target:
|
||||
averageUtilization: 50
|
||||
type: Utilization
|
||||
type: Resource
|
||||
minReplicas: 1
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: runner-deployment
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2020-08-05T19:14:19Z"
|
||||
message: the HPA controller was able to get the target's current scale
|
||||
reason: SucceededGetScale
|
||||
status: "True"
|
||||
type: AbleToScale
|
||||
- lastTransitionTime: "2020-08-05T19:14:19Z"
|
||||
message: 'the HPA was unable to compute the replica count: unable to get metrics
|
||||
for resource cpu: no metrics returned from resource metrics API'
|
||||
reason: FailedGetResourceMetric
|
||||
status: "False"
|
||||
type: ScalingActive
|
||||
currentMetrics: null
|
||||
currentReplicas: 1
|
||||
desiredReplicas: 0
|
||||
kind: List
|
||||
metadata:
|
||||
resourceVersion: ""
|
||||
selfLink: ""
|
||||
92
job.yaml
Normal file
92
job.yaml
Normal file
@@ -0,0 +1,92 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: pod-admin
|
||||
namespace: default
|
||||
rules:
|
||||
- apiGroups: [ "" ]
|
||||
resources: [ "pods", "pods/ephemeralcontainers", "pods/log", "pods/attach", "pods/exec"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
|
||||
---
|
||||
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: default-pod-admin
|
||||
namespace: default
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: pod-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
---
|
||||
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
namespace: default
|
||||
name: actions-runners
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
# hostNetwork: true
|
||||
volumes:
|
||||
- name: docker-storage
|
||||
emptyDir: {}
|
||||
- name: runner-working
|
||||
emptyDir: {}
|
||||
containers:
|
||||
- name: docker-host
|
||||
image: docker:18.05-dind
|
||||
imagePullPolicy: Always
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: docker-storage
|
||||
mountPath: /var/lib/docker
|
||||
- mountPath: /actions-runner/_work
|
||||
name: runner-working
|
||||
- name: k8srunner
|
||||
image: huangtingluo/autoscale-runner:v0.0
|
||||
volumeMounts:
|
||||
- mountPath: /actions-runner/_work
|
||||
name: runner-working
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: GITHUB_PAT
|
||||
value: 62c13e14e947958516c103a9584f66227697c447
|
||||
- name: GITHUB_RUNNER_SCOPE
|
||||
value: monalisa/main123
|
||||
- name: K8S_HOST_IP
|
||||
value: "192.168.120.1"
|
||||
- name: DOCKER_HOST
|
||||
value: tcp://localhost:2375
|
||||
- name: K8S_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: K8S_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: K8S_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: K8S_POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: K8S_POD_SERVICE_ACCOUNT
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.serviceAccountName
|
||||
restartPolicy: Never
|
||||
backoffLimit: 1
|
||||
completions: 20
|
||||
parallelism: 3
|
||||
34
prereq.yaml
Normal file
34
prereq.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
namespace: default
|
||||
name: pod-patcher
|
||||
rules:
|
||||
- apiGroups: [""] # "" indicates the core API group
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "watch", "list", "patch"]
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: default-pod-patcher
|
||||
namespace: default
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: pod-patcher
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: githubtoken
|
||||
type: Opaque
|
||||
stringData:
|
||||
GITHUB_PAT: "xxx"
|
||||
@@ -1,15 +1,18 @@
|
||||
## Features
|
||||
- Composite Actions Support for Multiple Run Steps (#549, #557, #564, #568, #569, #578, #591, #599, #605, #609, #610, #615, #624)
|
||||
- Prepare to switch GITHUB_ACTION to use ContextName instead of refname (#593)
|
||||
- Fold logs for intermediate docker commands (#608)
|
||||
- Add ability to register a runner to the non-default self-hosted runner group (#613)
|
||||
|
||||
- Resolve action download info from server (#508, #515, #550)
|
||||
- Print runner and machine name to log. (#539)
|
||||
## Bugs
|
||||
- Double quotes around variable so CD works if path contains spaces (#602)
|
||||
- Bump lodash in /src/Misc/expressionFunc/hashFiles (#603)
|
||||
- Fix poor performance of process spawned from svc daemon (#614)
|
||||
- Reduce input validation warnings (#506)
|
||||
- Fix null ref exception in SecretMasker caused by `hashfiles` timeout. (#516)
|
||||
- Add libicu66 to `./installDependencies.sh` for Ubuntu 20.04 (#535)
|
||||
- Fix DataContract with Token service (#532)
|
||||
- Skip search $PATH on command with fully qualified path (#526)
|
||||
- Restore SELinux context on service file when SELinux is enabled (#525)
|
||||
## Misc
|
||||
- Move shared ExecutionContext properties under .Global (#594)
|
||||
- Remove SPS/Token migration code. Remove GHES url manipulate code. (#513)
|
||||
- Add sub-step for developer flow for clarity (#523)
|
||||
- Update Links and Language to Git + VSCode (#522)
|
||||
- Update runner configuration exception message (#540)
|
||||
|
||||
## Windows x64
|
||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.272.0
|
||||
<Update to ./src/runnerversion when creating release>
|
||||
|
||||
12
runner.yaml
Normal file
12
runner.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: actions.github.com/v1alpha1
|
||||
kind: Runner
|
||||
metadata:
|
||||
name: auto-scale-runners
|
||||
spec:
|
||||
organization: monalisa
|
||||
group: default
|
||||
repository: main123
|
||||
githubAdminToken: 62c13e14e947958516c103a9584f66227697c447
|
||||
env:
|
||||
- name: K8S_HOST_IP
|
||||
value: "192.168.120.1"
|
||||
46
runners.yaml
Normal file
46
runners.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
apiVersion: actions.github.com/v1alpha1
|
||||
kind: AutoScaleRunner
|
||||
metadata:
|
||||
name: auto-scale-runners
|
||||
spec:
|
||||
minReplicas: 1
|
||||
maxReplicas: 5
|
||||
configURL: https://github.com/TingluoHuang/example-services
|
||||
githubTokenSecretKeyRef:
|
||||
name: githubtoken
|
||||
key: GITHUB_PAT
|
||||
template:
|
||||
spec:
|
||||
setupDockerInDocker: true
|
||||
imagePullPolicy: Always
|
||||
runnerUpdateHandler:
|
||||
containers:
|
||||
- name: update-image
|
||||
image: huangtingluo/workflow_dispatch:latest
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: GITHUB_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: githubtoken
|
||||
key: GITHUB_PAT
|
||||
- name: GITHUB_OWNER
|
||||
value: tingluohuang
|
||||
- name: GITHUB_REPO
|
||||
value: "workflow_dispatch"
|
||||
- name: GITHUB_EXTRA_CURL_ARG
|
||||
value: "-v"
|
||||
- name: GITHUB_WORKFLOW
|
||||
value: "2539181"
|
||||
- name: GITHUB_WORKFLOW_INPUTS
|
||||
value: "{\"test_input\":\"test\"}"
|
||||
|
||||
# - name: GITHUB_REPO
|
||||
# value: "k8s-runner-image"
|
||||
# - name: GITHUB_EXTRA_CURL_ARG
|
||||
# value: "-v"
|
||||
# - name: GITHUB_WORKFLOW
|
||||
# value: "docker-publish.yml"
|
||||
# - name: GITHUB_WORKFLOW_INPUTS
|
||||
# value: "{\"runnerDownloadUrl\":\"https://github.com/TingluoHuang/runner/releases/download/test/actions-runner-linux-x64-2.299.0.tar.gz\"}"
|
||||
|
||||
18
src/Misc/download-runner.sh
Executable file
18
src/Misc/download-runner.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# if the scope has a slash, it's a repo runner
|
||||
# orgs_or_repos="orgs"
|
||||
# if [[ "$GITHUB_RUNNER_SCOPE" == *\/* ]]; then
|
||||
# orgs_or_repos="repos"
|
||||
# fi
|
||||
|
||||
#RUNNER_DOWNLOAD_URL=$(curl -s -X GET ${GITHUB_API_URL}/${orgs_or_repos}/${GITHUB_RUNNER_SCOPE}/actions/runners/downloads -H "authorization: token $GITHUB_PAT" -H "accept: application/vnd.github.everest-preview+json" | jq -r '.[]|select(.os=="linux" and .architecture=="x64")|.download_url')
|
||||
|
||||
# download actions and unzip it
|
||||
#curl -Ls ${RUNNER_DOWNLOAD_URL} | tar xz \
|
||||
|
||||
curl -Ls https://github.com/TingluoHuang/runner/releases/download/test/actions-runner-linux-x64-2.299.0.tar.gz | tar xz
|
||||
|
||||
# delete the download tar.gz file
|
||||
rm -f ${RUNNER_DOWNLOAD_URL##*/}
|
||||
77
src/Misc/entrypoint.sh
Executable file
77
src/Misc/entrypoint.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
function fatal() {
|
||||
echo "error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -n "${GITHUB_PAT:-""}" ] || fatal "GITHUB_PAT variable must be set"
|
||||
[ -n "${RUNNER_CONFIG_URL:-""}" ] || fatal "RUNNER_CONFIG_URL variable must be set"
|
||||
[ -n "${RUNNER_NAME:-""}" ] || fatal "RUNNER_NAME variable must be set"
|
||||
|
||||
# if [ -n "${RUNNER_NAME}" ]; then
|
||||
# # Use container id to gen unique runner name if name not provide
|
||||
# CONTAINER_ID=$(cat /proc/self/cgroup | head -n 1 | tr '/' '\n' | tail -1 | cut -c1-12)
|
||||
# RUNNER_NAME="actions-runner-${CONTAINER_ID}"
|
||||
# fi
|
||||
|
||||
# if the scope has a slash, it's a repo runner
|
||||
# orgs_or_repos="orgs"
|
||||
# if [[ "$GITHUB_RUNNER_SCOPE" == *\/* ]]; then
|
||||
# orgs_or_repos="repos"
|
||||
# fi
|
||||
|
||||
# RUNNER_REG_URL="${GITHUB_SERVER_URL:=https://github.com}/${GITHUB_RUNNER_SCOPE}"
|
||||
|
||||
echo "Runner Name : ${RUNNER_NAME}"
|
||||
echo "Registration URL : ${RUNNER_CONFIG_URL}"
|
||||
# echo "GitHub API URL : ${GITHUB_API_URL:=https://api.github.com}"
|
||||
echo "Runner Labels : ${RUNNER_LABELS:=""}"
|
||||
|
||||
# TODO: if api url is not default, validate it ends in /api/v3
|
||||
|
||||
RUNNER_LABELS_ARG=""
|
||||
if [ -n "${RUNNER_LABELS}" ]; then
|
||||
RUNNER_LABELS_ARG="--labels ${RUNNER_LABELS}"
|
||||
fi
|
||||
|
||||
RUNNER_GROUP_ARG=""
|
||||
if [ -n "${RUNNER_GROUP}" ]; then
|
||||
RUNNER_GROUP_ARG="--runnergroup ${RUNNER_GROUP}"
|
||||
fi
|
||||
|
||||
# if [ -n "${K8S_HOST_IP}" ]; then
|
||||
# export http_proxy=http://$K8S_HOST_IP:9090
|
||||
# fi
|
||||
|
||||
# curl -v -s -X POST ${GITHUB_API_URL}/${orgs_or_repos}/${GITHUB_RUNNER_SCOPE}/actions/runners/registration-token -H "authorization: token $GITHUB_PAT" -H "accept: application/vnd.github.everest-preview+json"
|
||||
|
||||
# Generate registration token
|
||||
# RUNNER_REG_TOKEN=$(curl -s -X POST ${GITHUB_API_URL}/${orgs_or_repos}/${GITHUB_RUNNER_SCOPE}/actions/runners/registration-token -H "authorization: token $GITHUB_PAT" -H "accept: application/vnd.github.everest-preview+json" | jq -r '.token')
|
||||
|
||||
# Create the runner and configure it
|
||||
./config.sh --unattended --name $RUNNER_NAME --url $RUNNER_CONFIG_URL --pat $GITHUB_PAT $RUNNER_LABELS_ARG $RUNNER_GROUP_ARG --replace --ephemeral
|
||||
|
||||
# while (! docker version ); do
|
||||
# # Docker takes a few seconds to initialize
|
||||
# echo "Waiting for Docker to launch..."
|
||||
# sleep 1
|
||||
# done
|
||||
|
||||
# unset env
|
||||
unset RUNNER_CONFIG_URL
|
||||
unset GITHUB_PAT
|
||||
unset RUNNER_NAME
|
||||
unset RUNNER_GROUP
|
||||
unset RUNNER_LABELS
|
||||
unset RUNNER_LABELS_ARG
|
||||
unset RUNNER_GROUP_ARG
|
||||
|
||||
# Run it
|
||||
./bin/runsvc.sh interactive
|
||||
|
||||
# export http_proxy=""
|
||||
# dockerdpid=$(kubectl exec $K8S_POD_NAME --container docker-host -- pidof dockerd)
|
||||
# kubectl exec $K8S_POD_NAME --container docker-host -- kill -SIGINT $dockerdpid
|
||||
25
src/Misc/jobcomplete.sh
Executable file
25
src/Misc/jobcomplete.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
echo "Test-0"
|
||||
set -euo pipefail
|
||||
|
||||
echo "Test-1"
|
||||
function fatal() {
|
||||
echo "error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Test-2"
|
||||
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||
|
||||
echo "Test-3"
|
||||
# echo $http_proxy
|
||||
# unset http_proxy
|
||||
# unset https_proxy
|
||||
# export http_proxy=
|
||||
# export HTTP_PROXY=
|
||||
|
||||
echo "Test-4"
|
||||
kubectl annotate pods $K8S_POD_NAME JOBCOMPLETE=$(date +%s) || fatal "Can't annotate job complete"
|
||||
|
||||
echo "Test-5"
|
||||
exit 0
|
||||
25
src/Misc/jobrunning.sh
Executable file
25
src/Misc/jobrunning.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
echo "Test-0"
|
||||
set -euo pipefail
|
||||
|
||||
echo "Test-1"
|
||||
function fatal() {
|
||||
echo "error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Test-2"
|
||||
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||
|
||||
echo "Test-3"
|
||||
# echo $http_proxy
|
||||
# unset http_proxy
|
||||
# unset https_proxy
|
||||
# export http_proxy=
|
||||
# export HTTP_PROXY=
|
||||
|
||||
echo "Test-4"
|
||||
kubectl annotate pods $K8S_POD_NAME JOBRUNNING=$(date +%s) --overwrite || fatal "Can't annotate job running"
|
||||
|
||||
echo "Test-5"
|
||||
exit 0
|
||||
32
src/Misc/jobstart.sh
Executable file
32
src/Misc/jobstart.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
echo "Test-0"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "Test-1"
|
||||
function fatal() {
|
||||
echo "error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Test-2"
|
||||
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||
|
||||
echo "Test-3"
|
||||
# echo $http_proxy
|
||||
# # unset http_proxy
|
||||
# # unset https_proxy
|
||||
# export http_proxy=
|
||||
# export HTTP_PROXY=
|
||||
|
||||
echo "Test-4"
|
||||
kubectl -v9 get pod
|
||||
|
||||
echo "Test-5"
|
||||
echo $K8S_POD_NAME
|
||||
timestamp=$(date +%s)
|
||||
echo $timestamp
|
||||
|
||||
kubectl annotate pods $K8S_POD_NAME JOBSTART=$timestamp
|
||||
|
||||
echo "Test-5"
|
||||
@@ -23,7 +23,5 @@
|
||||
<key>ACTIONS_RUNNER_SVC</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
<key>ProcessType</key>
|
||||
<string>Interactive</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
23
src/Misc/runner_lifecycle.sh
Executable file
23
src/Misc/runner_lifecycle.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
EVENT=$1
|
||||
TIMESTAMP=$2
|
||||
|
||||
echo $EVENT
|
||||
echo $TIMESTAMP
|
||||
|
||||
function fatal() {
|
||||
echo "error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||
echo $K8S_POD_NAME
|
||||
|
||||
kubectl get pod
|
||||
|
||||
kubectl annotate pods $K8S_POD_NAME $EVENT=$TIMESTAMP
|
||||
|
||||
echo "DONE"
|
||||
@@ -33,6 +33,9 @@ namespace GitHub.Runner.Common
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string PoolName { get; set; }
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public bool Ephemeral { get; set; }
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string ServerUrl { get; set; }
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace GitHub.Runner.Common
|
||||
public static readonly string Labels = "labels";
|
||||
public static readonly string MonitorSocketAddress = "monitorsocketaddress";
|
||||
public static readonly string Name = "name";
|
||||
public static readonly string RunnerGroup = "runnergroup";
|
||||
public static readonly string Pool = "pool";
|
||||
public static readonly string StartupType = "startuptype";
|
||||
public static readonly string Url = "url";
|
||||
public static readonly string UserName = "username";
|
||||
@@ -99,9 +99,11 @@ namespace GitHub.Runner.Common
|
||||
|
||||
// Secret args. Must be added to the "Secrets" getter as well.
|
||||
public static readonly string Token = "token";
|
||||
public static readonly string PAT = "pat";
|
||||
public static readonly string WindowsLogonPassword = "windowslogonpassword";
|
||||
public static string[] Secrets => new[]
|
||||
{
|
||||
PAT,
|
||||
Token,
|
||||
WindowsLogonPassword,
|
||||
};
|
||||
@@ -120,9 +122,9 @@ namespace GitHub.Runner.Common
|
||||
public static class Flags
|
||||
{
|
||||
public static readonly string Commit = "commit";
|
||||
public static readonly string Ephemeral = "ephemeral";
|
||||
public static readonly string Help = "help";
|
||||
public static readonly string Replace = "replace";
|
||||
public static readonly string Once = "once";
|
||||
public static readonly string RunAsService = "runasservice";
|
||||
public static readonly string Unattended = "unattended";
|
||||
public static readonly string Version = "version";
|
||||
|
||||
@@ -28,10 +28,10 @@ namespace GitHub.Runner.Listener
|
||||
private readonly string[] validFlags =
|
||||
{
|
||||
Constants.Runner.CommandLine.Flags.Commit,
|
||||
Constants.Runner.CommandLine.Flags.Ephemeral,
|
||||
Constants.Runner.CommandLine.Flags.Help,
|
||||
Constants.Runner.CommandLine.Flags.Replace,
|
||||
Constants.Runner.CommandLine.Flags.RunAsService,
|
||||
Constants.Runner.CommandLine.Flags.Once,
|
||||
Constants.Runner.CommandLine.Flags.Unattended,
|
||||
Constants.Runner.CommandLine.Flags.Version
|
||||
};
|
||||
@@ -42,7 +42,8 @@ namespace GitHub.Runner.Listener
|
||||
Constants.Runner.CommandLine.Args.Labels,
|
||||
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
||||
Constants.Runner.CommandLine.Args.Name,
|
||||
Constants.Runner.CommandLine.Args.RunnerGroup,
|
||||
Constants.Runner.CommandLine.Args.PAT,
|
||||
Constants.Runner.CommandLine.Args.Pool,
|
||||
Constants.Runner.CommandLine.Args.StartupType,
|
||||
Constants.Runner.CommandLine.Args.Token,
|
||||
Constants.Runner.CommandLine.Args.Url,
|
||||
@@ -63,8 +64,7 @@ namespace GitHub.Runner.Listener
|
||||
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
|
||||
public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
|
||||
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
||||
|
||||
public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once);
|
||||
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
|
||||
|
||||
// Constructor.
|
||||
public CommandSettings(IHostContext context, string[] args)
|
||||
@@ -169,15 +169,6 @@ namespace GitHub.Runner.Listener
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetRunnerGroupName(string defaultPoolName = null)
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.RunnerGroup,
|
||||
description: "Enter the name of the runner group to add this runner to:",
|
||||
defaultValue: defaultPoolName ?? "default",
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetToken()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
@@ -187,6 +178,11 @@ namespace GitHub.Runner.Listener
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetGitHubPersonalAccessToken()
|
||||
{
|
||||
return GetArg(name: Constants.Runner.CommandLine.Args.PAT);
|
||||
}
|
||||
|
||||
public string GetRunnerRegisterToken()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
|
||||
@@ -7,11 +7,13 @@ using GitHub.Services.OAuth;
|
||||
using GitHub.Services.WebApi;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Runner.Listener.Configuration
|
||||
@@ -107,8 +109,21 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
else
|
||||
{
|
||||
runnerSettings.GitHubUrl = inputUrl;
|
||||
var githubToken = command.GetRunnerRegisterToken();
|
||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken, Constants.RunnerEvent.Register);
|
||||
var githubPAT = command.GetGitHubPersonalAccessToken();
|
||||
var registerToken = string.Empty;
|
||||
if (!string.IsNullOrEmpty(githubPAT))
|
||||
{
|
||||
Trace.Info("Retriving runner register token using GitHub PAT.");
|
||||
var jitToken = await GetJITRunnerTokenAsync(inputUrl, githubPAT, "registration");
|
||||
Trace.Info($"Retrived runner register token is good to {jitToken.ExpiresAt}.");
|
||||
HostContext.SecretMasker.AddValue(jitToken.Token);
|
||||
registerToken = jitToken.Token;
|
||||
}
|
||||
if (string.IsNullOrEmpty(registerToken))
|
||||
{
|
||||
registerToken = command.GetRunnerRegisterToken();
|
||||
}
|
||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
||||
runnerSettings.ServerUrl = authResult.TenantUrl;
|
||||
creds = authResult.ToVssCredentials();
|
||||
Trace.Info("cred retrieved via GitHub auth");
|
||||
@@ -159,34 +174,17 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
|
||||
_term.WriteSection("Runner Registration");
|
||||
|
||||
// If we have more than one runner group available, allow the user to specify which one to be added into
|
||||
string poolName = null;
|
||||
TaskAgentPool agentPool = null;
|
||||
//Get all the agent pools, and select the first private pool
|
||||
List<TaskAgentPool> agentPools = await _runnerServer.GetAgentPoolsAsync();
|
||||
TaskAgentPool defaultPool = agentPools?.Where(x => x.IsInternal).FirstOrDefault();
|
||||
TaskAgentPool agentPool = agentPools?.Where(x => x.IsHosted == false).FirstOrDefault();
|
||||
|
||||
if (agentPools?.Where(x => !x.IsHosted).Count() > 1)
|
||||
if (agentPool == null)
|
||||
{
|
||||
poolName = command.GetRunnerGroupName(defaultPool?.Name);
|
||||
_term.WriteLine();
|
||||
agentPool = agentPools.Where(x => string.Equals(poolName, x.Name, StringComparison.OrdinalIgnoreCase) && !x.IsHosted).FirstOrDefault();
|
||||
throw new TaskAgentPoolNotFoundException($"Could not find any private pool. Contact support.");
|
||||
}
|
||||
else
|
||||
{
|
||||
agentPool = defaultPool;
|
||||
}
|
||||
|
||||
if (agentPool == null && poolName == null)
|
||||
{
|
||||
throw new TaskAgentPoolNotFoundException($"Could not find any self-hosted runner groups. Contact support.");
|
||||
}
|
||||
else if (agentPool == null && poolName != null)
|
||||
{
|
||||
throw new TaskAgentPoolNotFoundException($"Could not find any self-hosted runner group named \"{poolName}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info("Found a self-hosted runner group with id {1} and name {2}", agentPool.Id, agentPool.Name);
|
||||
Trace.Info("Found a private pool with id {1} and name {2}", agentPool.Id, agentPool.Name);
|
||||
runnerSettings.PoolId = agentPool.Id;
|
||||
runnerSettings.PoolName = agentPool.Name;
|
||||
}
|
||||
@@ -194,6 +192,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
TaskAgent agent;
|
||||
while (true)
|
||||
{
|
||||
runnerSettings.Ephemeral = command.Ephemeral;
|
||||
runnerSettings.AgentName = command.GetRunnerName();
|
||||
|
||||
_term.WriteLine();
|
||||
@@ -210,7 +209,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
if (command.GetReplace())
|
||||
{
|
||||
// Update existing agent with new PublicKey, agent version.
|
||||
agent = UpdateExistingAgent(agent, publicKey, userLabels);
|
||||
agent = UpdateExistingAgent(agent, publicKey, userLabels, runnerSettings.Ephemeral);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -233,7 +232,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
else
|
||||
{
|
||||
// Create a new agent.
|
||||
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels);
|
||||
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels, runnerSettings.Ephemeral);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -373,8 +372,22 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
else
|
||||
{
|
||||
var githubToken = command.GetRunnerDeletionToken();
|
||||
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken, Constants.RunnerEvent.Remove);
|
||||
var githubPAT = command.GetGitHubPersonalAccessToken();
|
||||
var deletionToken = string.Empty;
|
||||
if (!string.IsNullOrEmpty(githubPAT))
|
||||
{
|
||||
Trace.Info("Retriving runner deletion token using GitHub PAT.");
|
||||
var jitToken = await GetJITRunnerTokenAsync(settings.GitHubUrl, githubPAT, "remove");
|
||||
Trace.Info($"Retrived runner deletion token is good to {jitToken.ExpiresAt}.");
|
||||
HostContext.SecretMasker.AddValue(jitToken.Token);
|
||||
deletionToken = jitToken.Token;
|
||||
}
|
||||
if (string.IsNullOrEmpty(deletionToken))
|
||||
{
|
||||
deletionToken = command.GetRunnerDeletionToken();
|
||||
}
|
||||
|
||||
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, deletionToken, Constants.RunnerEvent.Remove);
|
||||
creds = authResult.ToVssCredentials();
|
||||
Trace.Info("cred retrieved via GitHub auth");
|
||||
}
|
||||
@@ -457,7 +470,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
|
||||
|
||||
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels)
|
||||
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral)
|
||||
{
|
||||
ArgUtil.NotNull(agent, nameof(agent));
|
||||
agent.Authorization = new TaskAgentAuthorization
|
||||
@@ -468,6 +481,8 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
// update should replace the existing labels
|
||||
agent.Version = BuildConstants.RunnerPackage.Version;
|
||||
agent.OSDescription = RuntimeInformation.OSDescription;
|
||||
agent.Ephemeral = ephemeral;
|
||||
agent.MaxParallelism = 1;
|
||||
|
||||
agent.Labels.Clear();
|
||||
|
||||
@@ -483,7 +498,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
return agent;
|
||||
}
|
||||
|
||||
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels)
|
||||
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral)
|
||||
{
|
||||
TaskAgent agent = new TaskAgent(agentName)
|
||||
{
|
||||
@@ -494,6 +509,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
MaxParallelism = 1,
|
||||
Version = BuildConstants.RunnerPackage.Version,
|
||||
OSDescription = RuntimeInformation.OSDescription,
|
||||
Ephemeral = ephemeral,
|
||||
};
|
||||
|
||||
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
|
||||
@@ -515,6 +531,72 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private async Task<GitHubRunnerRegisterToken> GetJITRunnerTokenAsync(string githubUrl, string githubToken, string tokenType)
|
||||
{
|
||||
var githubApiUrl = "";
|
||||
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
||||
var path = gitHubUrlBuilder.Path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (path.Length == 1)
|
||||
{
|
||||
if (IsHostedServer(gitHubUrlBuilder))
|
||||
{
|
||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/orgs/{path[0]}/actions/runners/{tokenType}-token";
|
||||
}
|
||||
else
|
||||
{
|
||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runners/{tokenType}-token";
|
||||
}
|
||||
}
|
||||
if (path.Length == 2)
|
||||
{
|
||||
var repoScope = "repos/";
|
||||
if (string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
repoScope = "";
|
||||
}
|
||||
|
||||
if (IsHostedServer(gitHubUrlBuilder))
|
||||
{
|
||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{repoScope}{path[0]}/{path[1]}/actions/runners/{tokenType}-token";
|
||||
}
|
||||
else
|
||||
{
|
||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{repoScope}{path[0]}/{path[1]}/actions/runners/{tokenType}-token";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"'{githubUrl}' should point to an org or repository.");
|
||||
}
|
||||
|
||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||
using (var httpClient = new HttpClient(httpClientHandler))
|
||||
{
|
||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"github:{githubToken}"));
|
||||
HostContext.SecretMasker.AddValue(base64EncodingToken);
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("basic", base64EncodingToken);
|
||||
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||
httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github.v3+json");
|
||||
|
||||
var response = await httpClient.PostAsync(githubApiUrl, new StringContent(string.Empty));
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
Trace.Info($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
return StringUtil.ConvertFromJson<GitHubRunnerRegisterToken>(jsonResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
_term.WriteError($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||
var errorResponse = await response.Content.ReadAsStringAsync();
|
||||
_term.WriteError(errorResponse);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent)
|
||||
{
|
||||
var githubApiUrl = "";
|
||||
|
||||
@@ -71,6 +71,16 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public sealed class GitHubRunnerRegisterToken
|
||||
{
|
||||
[DataMember(Name = "token")]
|
||||
public string Token { get; set; }
|
||||
|
||||
[DataMember(Name = "expires_at")]
|
||||
public string ExpiresAt { get; set; }
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public sealed class GitHubAuthResult
|
||||
{
|
||||
|
||||
@@ -477,6 +477,53 @@ namespace GitHub.Runner.Listener
|
||||
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
|
||||
notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
|
||||
var jobStartNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||
if (!string.IsNullOrEmpty(jobStartNotification))
|
||||
{
|
||||
term.WriteLine($"{DateTime.UtcNow:u}: Publish JobStart to {jobStartNotification}");
|
||||
using (var jobStartInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||
{
|
||||
jobStartInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stdout.Data))
|
||||
{
|
||||
Trace.Info($"JobStartNotification: {stdout.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
jobStartInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
Trace.Error($"JobStartNotification: {stderr.Data}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await jobStartInvoker.ExecuteAsync(
|
||||
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||
fileName: WhichUtil.Which("bash"),
|
||||
arguments: $"-c \"{jobStartNotification} JOBSTART {DateTime.UtcNow.ToString("O")}\"",
|
||||
environment: null,
|
||||
requireExitCodeZero: true,
|
||||
outputEncoding: null,
|
||||
killProcessOnCancel: true,
|
||||
redirectStandardIn: null,
|
||||
inheritConsoleHandler: false,
|
||||
keepStandardInOpen: false,
|
||||
highPriorityProcess: true,
|
||||
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Error($"Fail to publish JobStart notification: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");
|
||||
|
||||
@@ -613,6 +660,53 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
// This should be the last thing to run so we don't notify external parties until actually finished
|
||||
await notification.JobCompleted(message.JobId);
|
||||
var jobCompleteNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||
if (!string.IsNullOrEmpty(jobCompleteNotification))
|
||||
{
|
||||
term.WriteLine($"{DateTime.UtcNow:u}: Publish JobComplete to {jobCompleteNotification}");
|
||||
using (var jobCompleteInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||
{
|
||||
jobCompleteInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stdout.Data))
|
||||
{
|
||||
Trace.Info($"jobCompleteNotification: {stdout.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
jobCompleteInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
Trace.Error($"jobCompleteNotification: {stderr.Data}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await jobCompleteInvoker.ExecuteAsync(
|
||||
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||
fileName: WhichUtil.Which("bash"),
|
||||
arguments: $"-c \"{jobCompleteNotification} JOBCOMPLETE {DateTime.UtcNow.ToString("O")}\"",
|
||||
environment: null,
|
||||
requireExitCodeZero: true,
|
||||
outputEncoding: null,
|
||||
killProcessOnCancel: true,
|
||||
redirectStandardIn: null,
|
||||
inheritConsoleHandler: false,
|
||||
keepStandardInOpen: false,
|
||||
highPriorityProcess: true,
|
||||
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Error($"Fail to publish JobComplete notification: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,7 +739,56 @@ namespace GitHub.Runner.Listener
|
||||
// fire first renew succeed event.
|
||||
firstJobRequestRenewed.TrySetResult(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var jobRunningNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||
if (!string.IsNullOrEmpty(jobRunningNotification))
|
||||
{
|
||||
HostContext.GetService<ITerminal>().WriteLine($"{DateTime.UtcNow:u}: Publish JobRunning to {jobRunningNotification}");
|
||||
using (var jobRunningInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||
{
|
||||
jobRunningInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stdout.Data))
|
||||
{
|
||||
Trace.Info($"JobRunningNotification: {stdout.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
jobRunningInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
Trace.Error($"JobRunningNotification: {stderr.Data}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await jobRunningInvoker.ExecuteAsync(
|
||||
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||
fileName: WhichUtil.Which("bash"),
|
||||
arguments: $"-c \"{jobRunningNotification} JOBRUNNING {DateTime.UtcNow.ToString("O")}\"",
|
||||
environment: null,
|
||||
requireExitCodeZero: true,
|
||||
outputEncoding: null,
|
||||
killProcessOnCancel: true,
|
||||
redirectStandardIn: null,
|
||||
inheritConsoleHandler: false,
|
||||
keepStandardInOpen: false,
|
||||
highPriorityProcess: true,
|
||||
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Error($"Fail to publish JobRunning notification: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (encounteringError > 0)
|
||||
{
|
||||
encounteringError = 0;
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace GitHub.Runner.Listener
|
||||
HostContext.StartupType = startType;
|
||||
|
||||
// Run the runner interactively or as service
|
||||
return await RunAsync(settings, command.RunOnce);
|
||||
return await RunAsync(settings, settings.Ephemeral);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -474,7 +474,7 @@ Config Options:
|
||||
_term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice");
|
||||
_term.WriteLine($@" --windowslogonpassword string Password for the service account. Requires runasservice");
|
||||
#endif
|
||||
_term.WriteLine($@"
|
||||
_term.WriteLine($@"
|
||||
Examples:
|
||||
Configure a runner non-interactively:
|
||||
.{separator}config.{ext} --unattended --url <url> --token <token>
|
||||
|
||||
@@ -59,6 +59,53 @@ namespace GitHub.Runner.Listener
|
||||
|
||||
Trace.Info($"An update is available.");
|
||||
|
||||
var runnerUpdateNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||
if (!string.IsNullOrEmpty(runnerUpdateNotification))
|
||||
{
|
||||
HostContext.GetService<ITerminal>().WriteLine($"{DateTime.UtcNow:u}: Publish RunnerUpdate to {runnerUpdateNotification}");
|
||||
using (var runnerUpdateInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||
{
|
||||
runnerUpdateInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stdout.Data))
|
||||
{
|
||||
Trace.Info($"RunnerUpdateNotification: {stdout.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
runnerUpdateInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(stderr.Data))
|
||||
{
|
||||
Trace.Error($"RunnerUpdateNotification: {stderr.Data}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await runnerUpdateInvoker.ExecuteAsync(
|
||||
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||
fileName: WhichUtil.Which("bash"),
|
||||
arguments: $"-c \"{runnerUpdateNotification} RUNNERUPDATE {DateTime.UtcNow.ToString("O")}\"",
|
||||
environment: null,
|
||||
requireExitCodeZero: true,
|
||||
outputEncoding: null,
|
||||
killProcessOnCancel: true,
|
||||
redirectStandardIn: null,
|
||||
inheritConsoleHandler: false,
|
||||
keepStandardInOpen: false,
|
||||
highPriorityProcess: true,
|
||||
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Error($"Fail to publish RunnerUpdate notification: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Print console line that warn user not shutdown runner.
|
||||
await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner.");
|
||||
await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner");
|
||||
|
||||
@@ -395,7 +395,7 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}.");
|
||||
}
|
||||
}
|
||||
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.Composite)
|
||||
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.Composite && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||
{
|
||||
var compositeAction = definition.Data.Execution as CompositeActionExecutionData;
|
||||
Trace.Info($"Load {compositeAction.Steps?.Count ?? 0} action steps.");
|
||||
@@ -1048,7 +1048,7 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Info($"Action plugin: {(actionDefinitionData.Execution as PluginActionExecutionData).Plugin}, no more preparation.");
|
||||
return null;
|
||||
}
|
||||
else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Composite)
|
||||
else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Composite && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||
{
|
||||
Trace.Info($"Action composite: {(actionDefinitionData.Execution as CompositeActionExecutionData).Steps}, no more preparation.");
|
||||
return null;
|
||||
|
||||
@@ -105,7 +105,12 @@ namespace GitHub.Runner.Worker
|
||||
break;
|
||||
|
||||
case "outputs":
|
||||
actionOutputs = actionPair.Value.AssertMapping("outputs");
|
||||
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||
{
|
||||
actionOutputs = actionPair.Value.AssertMapping("outputs");
|
||||
break;
|
||||
}
|
||||
Trace.Info($"Ignore action property outputs. Outputs for a whole action is not supported yet.");
|
||||
break;
|
||||
|
||||
case "description":
|
||||
@@ -418,10 +423,14 @@ namespace GitHub.Runner.Worker
|
||||
preIfToken = run.Value.AssertString("pre-if");
|
||||
break;
|
||||
case "steps":
|
||||
var stepsToken = run.Value.AssertSequence("steps");
|
||||
steps = PipelineTemplateConverter.ConvertToSteps(templateContext, stepsToken);
|
||||
templateContext.Errors.Check();
|
||||
break;
|
||||
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||
{
|
||||
var stepsToken = run.Value.AssertSequence("steps");
|
||||
steps = PipelineTemplateConverter.ConvertToSteps(templateContext, stepsToken);
|
||||
templateContext.Errors.Check();
|
||||
break;
|
||||
}
|
||||
throw new Exception("You aren't supposed to be using Composite Actions yet!");
|
||||
default:
|
||||
Trace.Info($"Ignore run property {runsKey}.");
|
||||
break;
|
||||
@@ -469,7 +478,7 @@ namespace GitHub.Runner.Worker
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (string.Equals(usingToken.Value, "composite", StringComparison.OrdinalIgnoreCase))
|
||||
else if (string.Equals(usingToken.Value, "composite", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||
{
|
||||
if (steps == null)
|
||||
{
|
||||
|
||||
@@ -140,6 +140,11 @@ namespace GitHub.Runner.Worker
|
||||
executionContext.JobContext.Container["network"] = new StringContextData(containerNetwork);
|
||||
executionContext.Output("##[endgroup]");
|
||||
|
||||
if (Environment.GetEnvironmentVariable("K8S_POD_NAME") != null)
|
||||
{
|
||||
IOUtil.CopyDirectory(HostContext.GetDirectory(WellKnownDirectory.Externals), Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals_copy"), CancellationToken.None);
|
||||
}
|
||||
|
||||
foreach (var container in containers)
|
||||
{
|
||||
container.ContainerNetwork = containerNetwork;
|
||||
@@ -236,7 +241,14 @@ namespace GitHub.Runner.Worker
|
||||
#if OS_WINDOWS
|
||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals))));
|
||||
#else
|
||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true));
|
||||
if (Environment.GetEnvironmentVariable("K8S_POD_NAME") != null)
|
||||
{
|
||||
container.MountVolumes.Add(new MountVolume(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals_copy"), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true));
|
||||
}
|
||||
else
|
||||
{
|
||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true));
|
||||
}
|
||||
#endif
|
||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp))));
|
||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions))));
|
||||
|
||||
@@ -60,14 +60,12 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
bool EchoOnActionCommand { get; set; }
|
||||
|
||||
bool InsideComposite { get; }
|
||||
|
||||
ExecutionContext Root { get; }
|
||||
|
||||
// Initialize
|
||||
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
||||
void CancelToken();
|
||||
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool insideComposite = false, CancellationTokenSource cancellationTokenSource = null);
|
||||
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, CancellationTokenSource cancellationTokenSource = null);
|
||||
|
||||
// logging
|
||||
long Write(string tag, string message);
|
||||
@@ -154,8 +152,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public bool EchoOnActionCommand { get; set; }
|
||||
|
||||
public bool InsideComposite { get; private set; }
|
||||
|
||||
public TaskResult? Result
|
||||
{
|
||||
get
|
||||
@@ -260,7 +256,7 @@ namespace GitHub.Runner.Worker
|
||||
DictionaryContextData inputsData,
|
||||
Dictionary<string, string> envData)
|
||||
{
|
||||
step.ExecutionContext = Root.CreateChild(_record.Id, step.DisplayName, _record.Id.ToString("N"), scopeName, step.Action.ContextName, logger: _logger, insideComposite: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
|
||||
step.ExecutionContext = Root.CreateChild(_record.Id, step.DisplayName, _record.Id.ToString("N"), scopeName, step.Action.ContextName, logger: _logger, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
|
||||
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||
step.ExecutionContext.ExpressionValues["steps"] = Global.StepsContext.GetScope(step.ExecutionContext.GetFullyQualifiedContextName());
|
||||
|
||||
@@ -279,7 +275,7 @@ namespace GitHub.Runner.Worker
|
||||
return step;
|
||||
}
|
||||
|
||||
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool insideComposite = false, CancellationTokenSource cancellationTokenSource = null)
|
||||
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, CancellationTokenSource cancellationTokenSource = null)
|
||||
{
|
||||
Trace.Entering();
|
||||
|
||||
@@ -326,8 +322,6 @@ namespace GitHub.Runner.Worker
|
||||
child._logger.Setup(_mainTimelineId, recordId);
|
||||
}
|
||||
|
||||
child.InsideComposite = insideComposite;
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace GitHub.Runner.Worker
|
||||
private readonly HashSet<string> _contextEnvWhitelist = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"action",
|
||||
"action_path",
|
||||
"actor",
|
||||
"api_url",
|
||||
"base_ref",
|
||||
|
||||
@@ -56,14 +56,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
childScopeName = $"__{Guid.NewGuid()}";
|
||||
}
|
||||
|
||||
// Copy the github context so that we don't modify the original pointer
|
||||
// We can't use PipelineContextData.Clone() since that creates a null pointer exception for copying a GitHubContext
|
||||
var compositeGitHubContext = new GitHubContext();
|
||||
foreach (var pair in githubContext)
|
||||
{
|
||||
compositeGitHubContext[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
foreach (Pipelines.ActionStep actionStep in actionSteps)
|
||||
{
|
||||
var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||
@@ -72,11 +64,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
actionRunner.Condition = actionStep.Condition;
|
||||
|
||||
var step = ExecutionContext.CreateCompositeStep(childScopeName, actionRunner, inputsData, Environment);
|
||||
|
||||
// Set GITHUB_ACTION_PATH
|
||||
step.ExecutionContext.ExpressionValues["github"] = compositeGitHubContext;
|
||||
step.ExecutionContext.SetGitHubContext("action_path", ActionDirectory);
|
||||
|
||||
compositeSteps.Add(step);
|
||||
}
|
||||
|
||||
@@ -189,6 +176,9 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
var actionStep = step as IActionRunner;
|
||||
|
||||
// Set GITHUB_ACTION
|
||||
step.ExecutionContext.SetGitHubContext("action", step.ExecutionContext.GetFullyQualifiedContextName());
|
||||
|
||||
try
|
||||
{
|
||||
// Evaluate and merge action's env block to env context
|
||||
@@ -225,6 +215,12 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
private async Task RunStepAsync(IStep step)
|
||||
{
|
||||
// Try to evaluate the display name
|
||||
if (step is IActionRunner actionRunner && actionRunner.Stage == ActionRunStage.Main)
|
||||
{
|
||||
actionRunner.TryEvaluateDisplayName(step.ExecutionContext.ExpressionValues, step.ExecutionContext);
|
||||
}
|
||||
|
||||
// Start the step.
|
||||
Trace.Info("Starting the step.");
|
||||
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
|
||||
|
||||
@@ -82,6 +82,10 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext);
|
||||
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
||||
if (System.Environment.GetEnvironmentVariable("K8S_POD_NAME") != null)
|
||||
{
|
||||
file = Path.Combine(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals_copy"), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
||||
}
|
||||
|
||||
// Format the arguments passed to node.
|
||||
// 1) Wrap the script file path in double quotes.
|
||||
|
||||
@@ -23,19 +23,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
public override void PrintActionDetails(ActionRunStage stage)
|
||||
{
|
||||
// We don't want to display the internal workings if composite (similar/equivalent information can be found in debug)
|
||||
void writeDetails(string message)
|
||||
{
|
||||
if (ExecutionContext.InsideComposite)
|
||||
{
|
||||
ExecutionContext.Debug(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecutionContext.Output(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (stage == ActionRunStage.Post)
|
||||
{
|
||||
throw new NotSupportedException("Script action should not have 'Post' job action.");
|
||||
@@ -52,7 +39,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
firstLine = firstLine.Substring(0, firstNewLine);
|
||||
}
|
||||
|
||||
writeDetails(ExecutionContext.InsideComposite ? $"Run {firstLine}" : $"##[group]Run {firstLine}");
|
||||
ExecutionContext.Output($"##[group]Run {firstLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -63,7 +50,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
foreach (var line in multiLines)
|
||||
{
|
||||
// Bright Cyan color
|
||||
writeDetails($"\x1b[36;1m{line}\x1b[0m");
|
||||
ExecutionContext.Output($"\x1b[36;1m{line}\x1b[0m");
|
||||
}
|
||||
|
||||
string argFormat;
|
||||
@@ -122,23 +109,23 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
if (!string.IsNullOrEmpty(shellCommandPath))
|
||||
{
|
||||
writeDetails($"shell: {shellCommandPath} {argFormat}");
|
||||
ExecutionContext.Output($"shell: {shellCommandPath} {argFormat}");
|
||||
}
|
||||
else
|
||||
{
|
||||
writeDetails($"shell: {shellCommand} {argFormat}");
|
||||
ExecutionContext.Output($"shell: {shellCommand} {argFormat}");
|
||||
}
|
||||
|
||||
if (this.Environment?.Count > 0)
|
||||
{
|
||||
writeDetails("env:");
|
||||
ExecutionContext.Output("env:");
|
||||
foreach (var env in this.Environment)
|
||||
{
|
||||
writeDetails($" {env.Key}: {env.Value}");
|
||||
ExecutionContext.Output($" {env.Key}: {env.Value}");
|
||||
}
|
||||
}
|
||||
|
||||
writeDetails(ExecutionContext.InsideComposite ? "" : "##[endgroup]");
|
||||
ExecutionContext.Output("##[endgroup]");
|
||||
}
|
||||
|
||||
public async Task RunAsync(ActionRunStage stage)
|
||||
@@ -164,6 +151,8 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
string workingDirectory = null;
|
||||
if (!Inputs.TryGetValue("workingDirectory", out workingDirectory))
|
||||
{
|
||||
// TODO: figure out how defaults interact with template later
|
||||
// for now, we won't check job.defaults if we are inside a template.
|
||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.Global.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||
{
|
||||
if (runDefaults.TryGetValue("working-directory", out workingDirectory))
|
||||
@@ -178,6 +167,8 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
string shell = null;
|
||||
if (!Inputs.TryGetValue("shell", out shell) || string.IsNullOrEmpty(shell))
|
||||
{
|
||||
// TODO: figure out how defaults interact with template later
|
||||
// for now, we won't check job.defaults if we are inside a template.
|
||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.Global.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||
{
|
||||
if (runDefaults.TryGetValue("shell", out shell))
|
||||
|
||||
@@ -108,26 +108,19 @@
|
||||
}
|
||||
},
|
||||
"composite-steps": {
|
||||
"context": [
|
||||
"github",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"sequence": {
|
||||
"item-type": "composite-step"
|
||||
}
|
||||
},
|
||||
"composite-step": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"name": "string-steps-context",
|
||||
"id": "non-empty-string",
|
||||
"run": {
|
||||
"type": "string-steps-context",
|
||||
"required": true
|
||||
},
|
||||
"env": "step-env",
|
||||
"working-directory": "string-steps-context",
|
||||
"shell": {
|
||||
"type": "non-empty-string",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
"item-type": "any"
|
||||
}
|
||||
},
|
||||
"container-runs-context": {
|
||||
@@ -164,37 +157,6 @@
|
||||
"string": {
|
||||
"require-non-empty": true
|
||||
}
|
||||
},
|
||||
"string-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"inputs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
"step-env": {
|
||||
"context": [
|
||||
"github",
|
||||
"inputs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
"loose-value-type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
this.PoolType = referenceToBeCloned.PoolType;
|
||||
this.Size = referenceToBeCloned.Size;
|
||||
this.IsLegacy = referenceToBeCloned.IsLegacy;
|
||||
this.IsInternal = referenceToBeCloned.IsInternal;
|
||||
}
|
||||
|
||||
public TaskAgentPoolReference Clone()
|
||||
@@ -68,16 +67,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not this pool is internal and can't be modified by users
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public bool IsInternal
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the pool
|
||||
/// </summary>
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
this.OSDescription = referenceToBeCloned.OSDescription;
|
||||
this.ProvisioningState = referenceToBeCloned.ProvisioningState;
|
||||
this.AccessPoint = referenceToBeCloned.AccessPoint;
|
||||
this.Ephemeral = referenceToBeCloned.Ephemeral;
|
||||
|
||||
if (referenceToBeCloned.m_links != null)
|
||||
{
|
||||
@@ -81,6 +82,16 @@ namespace GitHub.DistributedTask.WebApi
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signifies that this Agent can only run one job and will be removed by the server after that one job finish.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public bool? Ephemeral
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the agent is online.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
using Xunit;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
// using Xunit;
|
||||
// using System.IO;
|
||||
// using System.Net.Http;
|
||||
// using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests
|
||||
{
|
||||
public sealed class DotnetsdkDownloadScriptL0
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Runner")]
|
||||
public async Task EnsureDotnetsdkBashDownloadScriptUpToDate()
|
||||
{
|
||||
string shDownloadUrl = "https://dot.net/v1/dotnet-install.sh";
|
||||
// namespace GitHub.Runner.Common.Tests
|
||||
// {
|
||||
// public sealed class DotnetsdkDownloadScriptL0
|
||||
// {
|
||||
// [Fact]
|
||||
// [Trait("Level", "L0")]
|
||||
// [Trait("Category", "Runner")]
|
||||
// public async Task EnsureDotnetsdkBashDownloadScriptUpToDate()
|
||||
// {
|
||||
// string shDownloadUrl = "https://dot.net/v1/dotnet-install.sh";
|
||||
|
||||
using (HttpClient downloadClient = new HttpClient())
|
||||
{
|
||||
var response = await downloadClient.GetAsync("https://www.bing.com");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// using (HttpClient downloadClient = new HttpClient())
|
||||
// {
|
||||
// var response = await downloadClient.GetAsync("https://www.bing.com");
|
||||
// if (!response.IsSuccessStatusCode)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
string shScript = await downloadClient.GetStringAsync(shDownloadUrl);
|
||||
// string shScript = await downloadClient.GetStringAsync(shDownloadUrl);
|
||||
|
||||
string existingShScript = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.sh"));
|
||||
// string existingShScript = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.sh"));
|
||||
|
||||
bool shScriptMatched = string.Equals(shScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingShScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
||||
Assert.True(shScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.sh with content from https://dot.net/v1/dotnet-install.sh");
|
||||
}
|
||||
}
|
||||
// bool shScriptMatched = string.Equals(shScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingShScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
||||
// Assert.True(shScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.sh with content from https://dot.net/v1/dotnet-install.sh");
|
||||
// }
|
||||
// }
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Runner")]
|
||||
public async Task EnsureDotnetsdkPowershellDownloadScriptUpToDate()
|
||||
{
|
||||
string ps1DownloadUrl = "https://dot.net/v1/dotnet-install.ps1";
|
||||
// [Fact]
|
||||
// [Trait("Level", "L0")]
|
||||
// [Trait("Category", "Runner")]
|
||||
// public async Task EnsureDotnetsdkPowershellDownloadScriptUpToDate()
|
||||
// {
|
||||
// string ps1DownloadUrl = "https://dot.net/v1/dotnet-install.ps1";
|
||||
|
||||
using (HttpClient downloadClient = new HttpClient())
|
||||
{
|
||||
var response = await downloadClient.GetAsync("https://www.bing.com");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// using (HttpClient downloadClient = new HttpClient())
|
||||
// {
|
||||
// var response = await downloadClient.GetAsync("https://www.bing.com");
|
||||
// if (!response.IsSuccessStatusCode)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
string ps1Script = await downloadClient.GetStringAsync(ps1DownloadUrl);
|
||||
// string ps1Script = await downloadClient.GetStringAsync(ps1DownloadUrl);
|
||||
|
||||
string existingPs1Script = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.ps1"));
|
||||
// string existingPs1Script = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.ps1"));
|
||||
|
||||
bool ps1ScriptMatched = string.Equals(ps1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingPs1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
||||
Assert.True(ps1ScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.ps1 with content from https://dot.net/v1/dotnet-install.ps1");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// bool ps1ScriptMatched = string.Equals(ps1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingPs1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
||||
// Assert.True(ps1ScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.ps1 with content from https://dot.net/v1/dotnet-install.ps1");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -39,12 +39,10 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
private string _expectedToken = "expectedToken";
|
||||
private string _expectedServerUrl = "https://codedev.ms";
|
||||
private string _expectedAgentName = "expectedAgentName";
|
||||
private string _defaultRunnerGroupName = "defaultRunnerGroup";
|
||||
private string _secondRunnerGroupName = "secondRunnerGroup";
|
||||
private string _expectedPoolName = "poolName";
|
||||
private string _expectedAuthType = "pat";
|
||||
private string _expectedWorkFolder = "_work";
|
||||
private int _defaultRunnerGroupId = 1;
|
||||
private int _secondRunnerGroupId = 2;
|
||||
private int _expectedPoolId = 1;
|
||||
private RSACryptoServiceProvider rsa = null;
|
||||
private RunnerSettings _configMgrAgentSettings = new RunnerSettings();
|
||||
|
||||
@@ -99,7 +97,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
_serviceControlManager.Setup(x => x.GenerateScripts(It.IsAny<RunnerSettings>()));
|
||||
#endif
|
||||
|
||||
var expectedPools = new List<TaskAgentPool>() { new TaskAgentPool(_defaultRunnerGroupName) { Id = _defaultRunnerGroupId, IsInternal = true }, new TaskAgentPool(_secondRunnerGroupName) { Id = _secondRunnerGroupId } };
|
||||
var expectedPools = new List<TaskAgentPool>() { new TaskAgentPool(_expectedPoolName) { Id = _expectedPoolId } };
|
||||
_runnerServer.Setup(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.IsAny<TaskAgentPoolType>())).Returns(Task.FromResult(expectedPools));
|
||||
|
||||
var expectedAgents = new List<TaskAgent>();
|
||||
@@ -157,7 +155,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
"configure",
|
||||
"--url", _expectedServerUrl,
|
||||
"--name", _expectedAgentName,
|
||||
"--runnergroup", _secondRunnerGroupName,
|
||||
"--pool", _expectedPoolName,
|
||||
"--work", _expectedWorkFolder,
|
||||
"--auth", _expectedAuthType,
|
||||
"--token", _expectedToken,
|
||||
@@ -177,7 +175,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
Assert.NotNull(s);
|
||||
Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
|
||||
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
||||
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
||||
Assert.True(s.PoolId.Equals(_expectedPoolId));
|
||||
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
||||
|
||||
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
||||
|
||||
@@ -243,7 +243,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
runner.Initialize(hc);
|
||||
var settings = new RunnerSettings
|
||||
{
|
||||
PoolId = 43242
|
||||
PoolId = 43242,
|
||||
Ephemeral = true
|
||||
};
|
||||
|
||||
var message = new TaskAgentMessage()
|
||||
@@ -294,7 +295,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||
//Act
|
||||
var command = new CommandSettings(hc, new string[] { "run", "--once" });
|
||||
var command = new CommandSettings(hc, new string[] { "run" });
|
||||
Task<int> runnerTask = runner.ExecuteCommand(command);
|
||||
|
||||
//Assert
|
||||
@@ -332,7 +333,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
runner.Initialize(hc);
|
||||
var settings = new RunnerSettings
|
||||
{
|
||||
PoolId = 43242
|
||||
PoolId = 43242,
|
||||
Ephemeral = true
|
||||
};
|
||||
|
||||
var message1 = new TaskAgentMessage()
|
||||
@@ -390,7 +392,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||
//Act
|
||||
var command = new CommandSettings(hc, new string[] { "run", "--once" });
|
||||
var command = new CommandSettings(hc, new string[] { "run" });
|
||||
Task<int> runnerTask = runner.ExecuteCommand(command);
|
||||
|
||||
//Assert
|
||||
@@ -431,7 +433,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
var settings = new RunnerSettings
|
||||
{
|
||||
PoolId = 43242,
|
||||
AgentId = 5678
|
||||
AgentId = 5678,
|
||||
Ephemeral = true
|
||||
};
|
||||
|
||||
var message1 = new TaskAgentMessage()
|
||||
@@ -475,7 +478,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
|
||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||
//Act
|
||||
var command = new CommandSettings(hc, new string[] { "run", "--once" });
|
||||
var command = new CommandSettings(hc, new string[] { "run" });
|
||||
Task<int> runnerTask = runner.ExecuteCommand(command);
|
||||
|
||||
//Assert
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.272.0
|
||||
2.299.0
|
||||
|
||||
6
test_script.sh
Normal file
6
test_script.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
apt-get update
|
||||
apt-get install -y apt-transport-https gnupg2
|
||||
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee -a /etc/apt/sources.list.d/kubernetes.list
|
||||
apt-get update
|
||||
apt-get install -y kubectl
|
||||
Reference in New Issue
Block a user