Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0571e64b1a
|
||
|
|
7921840604 | ||
|
|
45be4a9fd9
|
||
|
|
02ccd5b8d7 | ||
|
|
2edb7b2b82
|
||
|
|
d472a906ac | ||
|
|
0151b6faf6
|
||
|
|
713d9a922a | ||
|
|
537c620051
|
||
|
|
a7b62868b6 | ||
|
|
84aa607c86
|
||
|
|
57524be30f | ||
|
|
2ffc45c766
|
||
|
|
68c3771dc8 | ||
|
|
16054d78e3
|
||
|
|
5d3d2a94bd |
@@ -1,142 +1,36 @@
|
||||
---
|
||||
name: publish images
|
||||
name: publish controller image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- '.gitea/workflows/publish-images.yml'
|
||||
- 'config/**'
|
||||
- 'container/**'
|
||||
- 'container/controller.Dockerfile'
|
||||
- 'cmd/**'
|
||||
- 'internal/**'
|
||||
- 'scripts/**'
|
||||
- 'src/**'
|
||||
- 'scripts/publish-image'
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
- 'pyproject.toml'
|
||||
- 'README.md'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-images:
|
||||
name: publish-images
|
||||
runs-on: [self-hosted, vm]
|
||||
name: publish-controller
|
||||
runs-on: [self-hosted, pod]
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
PUSH_REGISTRY: zot-push.ad.ddupan.top
|
||||
PULL_REGISTRY: zot.ad.ddupan.top
|
||||
CONTROLLER_REPOSITORY: panxiao81/gitea-dynamic-runner-controller
|
||||
RUNNER_REPOSITORY: panxiao81/gitea-dynamic-runner-runner
|
||||
IMAGE_NAME: controller
|
||||
IMAGE_REPOSITORY: panxiao81/gitea-dynamic-runner-controller
|
||||
IMAGE_DOCKERFILE: container/controller.Dockerfile
|
||||
SPIRE_AGENT_SOCKET: /run/spire/agent-sockets/spire-agent.sock
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Test source
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
go test ./...
|
||||
go vet ./...
|
||||
python3 -m venv /tmp/test-venv
|
||||
source /tmp/test-venv/bin/activate
|
||||
python3 -m pip install -e '.[test]'
|
||||
pytest -q
|
||||
python3 -m compileall -q src tests
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes --no-install-recommends shellcheck
|
||||
shellcheck scripts/*
|
||||
|
||||
- name: Build and publish
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
set +x
|
||||
|
||||
# Bootstrap the image that first introduces automatic Docker setup.
|
||||
# Once deployed, the job-started hook makes this an idempotent no-op.
|
||||
sudo scripts/setup-job-docker
|
||||
|
||||
: "${GITHUB_SHA:?GITHUB_SHA is required}"
|
||||
image_tag="sha-${GITHUB_SHA}"
|
||||
docker_config=$(mktemp -d)
|
||||
jwt_file=$(mktemp)
|
||||
cleanup() {
|
||||
docker buildx rm ci-builder >/dev/null 2>&1 || true
|
||||
rm -rf -- "$docker_config" "$jwt_file"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
export DOCKER_CONFIG="$docker_config"
|
||||
|
||||
/opt/spire/bin/spire-agent api fetch jwt \
|
||||
-audience zot \
|
||||
-socketPath "$SPIRE_AGENT_SOCKET" \
|
||||
-output json >"$jwt_file"
|
||||
# shellcheck disable=SC2016
|
||||
jq -er '.[0].svids[0].svid' "$jwt_file" | \
|
||||
docker login "$PUSH_REGISTRY" --username zot --password-stdin
|
||||
|
||||
docker buildx create \
|
||||
--name ci-builder \
|
||||
--driver docker-container \
|
||||
--use
|
||||
|
||||
publish() {
|
||||
local repository=$1
|
||||
local dockerfile=$2
|
||||
local metadata=$3
|
||||
docker buildx build \
|
||||
--builder ci-builder \
|
||||
--platform linux/amd64 \
|
||||
--file "$dockerfile" \
|
||||
--tag "${PUSH_REGISTRY}/${repository}:${image_tag}" \
|
||||
--tag "${PUSH_REGISTRY}/${repository}:main" \
|
||||
--provenance=mode=max \
|
||||
--sbom=true \
|
||||
--metadata-file "$metadata" \
|
||||
--push \
|
||||
.
|
||||
}
|
||||
|
||||
publish \
|
||||
"$CONTROLLER_REPOSITORY" \
|
||||
container/controller.Dockerfile \
|
||||
controller-metadata.json
|
||||
publish \
|
||||
"$RUNNER_REPOSITORY" \
|
||||
container/runner.Dockerfile \
|
||||
runner-metadata.json
|
||||
|
||||
controller_digest=$(
|
||||
# shellcheck disable=SC2016
|
||||
jq -er '."containerimage.digest"' controller-metadata.json
|
||||
)
|
||||
runner_digest=$(
|
||||
# shellcheck disable=SC2016
|
||||
jq -er '."containerimage.digest"' runner-metadata.json
|
||||
)
|
||||
controller_ref="${PULL_REGISTRY}/${CONTROLLER_REPOSITORY}@${controller_digest}"
|
||||
runner_ref="${PULL_REGISTRY}/${RUNNER_REPOSITORY}@${runner_digest}"
|
||||
|
||||
printf 'controller=%s\nrunner=%s\n' "$controller_ref" "$runner_ref"
|
||||
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
||||
{
|
||||
printf '## Published images\n\n'
|
||||
# shellcheck disable=SC2016
|
||||
printf -- '- Controller: `%s`\n' "$controller_ref"
|
||||
# shellcheck disable=SC2016
|
||||
printf -- '- Runner: `%s`\n' "$runner_ref"
|
||||
# shellcheck disable=SC2016
|
||||
printf -- '- Source: `%s`\n' "$GITHUB_SHA"
|
||||
} >>"$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
run: scripts/publish-image
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: publish runner image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-images:
|
||||
name: publish-runner
|
||||
runs-on: [self-hosted, pod]
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
PUSH_REGISTRY: zot-push.ad.ddupan.top
|
||||
PULL_REGISTRY: zot.ad.ddupan.top
|
||||
IMAGE_NAME: runner
|
||||
IMAGE_REPOSITORY: panxiao81/gitea-dynamic-runner-runner
|
||||
IMAGE_DOCKERFILE: container/runner.Dockerfile
|
||||
SPIRE_AGENT_SOCKET: /run/spire/agent-sockets/spire-agent.sock
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build and publish
|
||||
shell: bash
|
||||
run: scripts/publish-image
|
||||
@@ -5,29 +5,20 @@ on:
|
||||
|
||||
jobs:
|
||||
jwt-svid:
|
||||
runs-on: self-hosted
|
||||
container:
|
||||
volumes:
|
||||
- /run/spire/agent-sockets:/run/spire/agent-sockets:ro
|
||||
runs-on: [self-hosted, pod]
|
||||
steps:
|
||||
- name: Fetch pinned SPIRE CLI
|
||||
- name: Verify bundled SPIRE CLI
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
archive=/tmp/spire.tar.gz
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "$archive" \
|
||||
https://github.com/spiffe/spire/releases/download/v1.15.3/spire-1.15.3-linux-amd64-musl.tar.gz
|
||||
printf '%s %s\n' \
|
||||
ca1a4d1155317bdd2afc7f36663828a10410c7c840e54725b90b4064b0a301c7 \
|
||||
"$archive" | sha256sum --check --status
|
||||
tar -xzf "$archive" -C /tmp spire-1.15.3/bin/spire-agent
|
||||
command -v spire-agent
|
||||
spire-agent -version
|
||||
|
||||
- name: Fetch short-lived zot JWT-SVID
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
/tmp/spire-1.15.3/bin/spire-agent api fetch jwt \
|
||||
spire-agent api fetch jwt \
|
||||
-audience zot \
|
||||
-socketPath /run/spire/agent-sockets/spire-agent.sock \
|
||||
>/dev/null
|
||||
|
||||
@@ -21,11 +21,12 @@ RUN groupadd --gid 2000 runner \
|
||||
&& useradd --uid 2000 --gid 2000 --groups docker --create-home --shell /bin/bash runner \
|
||||
&& printf 'runner ALL=(ALL) NOPASSWD:ALL\n' >/etc/sudoers.d/runner \
|
||||
&& chmod 0440 /etc/sudoers.d/runner \
|
||||
&& install -d -o 2000 -g 2000 /data
|
||||
&& install -d -o 2000 -g 2000 /data /workspace
|
||||
|
||||
COPY --from=runner /usr/local/bin/gitea-runner /usr/local/bin/gitea-runner
|
||||
COPY --from=controller /out/gitea-dynamic-runner /usr/local/bin/gitea-dynamic-runner
|
||||
COPY --from=spire /opt/spire/bin/spire-agent /opt/spire/bin/spire-agent
|
||||
RUN ln -s /opt/spire/bin/spire-agent /usr/local/bin/spire-agent
|
||||
COPY config/runner.yaml /etc/gitea-runner/config.yaml
|
||||
COPY --chmod=0755 scripts/gitea-job-started /usr/local/libexec/gitea-job-started
|
||||
COPY --chmod=0755 scripts/gitea-opensandbox-runner /usr/local/libexec/gitea-opensandbox-runner
|
||||
|
||||
@@ -22,6 +22,7 @@ type ExecutorConfig struct {
|
||||
FacadeSPIFFEID string
|
||||
WorkloadAPIAddr string
|
||||
RunnerBinary string
|
||||
RunnerConfig string
|
||||
ListenAddress string
|
||||
WorkDir string
|
||||
Stdout *os.File
|
||||
@@ -35,6 +36,9 @@ func RunExecutor(ctx context.Context, config ExecutorConfig) error {
|
||||
if config.RunnerBinary == "" {
|
||||
config.RunnerBinary = "gitea-runner"
|
||||
}
|
||||
if config.RunnerConfig == "" {
|
||||
config.RunnerConfig = "/etc/gitea-runner/config.yaml"
|
||||
}
|
||||
if config.ListenAddress == "" {
|
||||
config.ListenAddress = "127.0.0.1:0"
|
||||
}
|
||||
@@ -93,7 +97,7 @@ func RunExecutor(ctx context.Context, config ExecutorConfig) error {
|
||||
return errors.Join(readyErr, shutdownErr, serverErr)
|
||||
}
|
||||
|
||||
command := exec.CommandContext(ctx, config.RunnerBinary, "daemon", "--once")
|
||||
command := exec.CommandContext(ctx, config.RunnerBinary, runnerArguments(config.RunnerConfig)...)
|
||||
command.Dir = workDir
|
||||
command.Stdout = config.Stdout
|
||||
command.Stderr = config.Stderr
|
||||
@@ -108,6 +112,10 @@ func RunExecutor(ctx context.Context, config ExecutorConfig) error {
|
||||
return errors.Join(runnerErr, shutdownErr, serverErr)
|
||||
}
|
||||
|
||||
func runnerArguments(configFile string) []string {
|
||||
return []string{"daemon", "--config", configFile, "--once"}
|
||||
}
|
||||
|
||||
func waitForFacade(ctx context.Context, endpoint string) error {
|
||||
client := &http.Client{Timeout: 2 * time.Second}
|
||||
ticker := time.NewTicker(250 * time.Millisecond)
|
||||
@@ -143,8 +151,9 @@ func ExecutorConfigFromEnvironment() (ExecutorConfig, error) {
|
||||
config := ExecutorConfig{
|
||||
AssignmentID: os.Getenv(EnvAssignmentID), Capability: os.Getenv(EnvCapability),
|
||||
Backend: backend, FacadeURL: os.Getenv(EnvFacadeURL), FacadeSPIFFEID: os.Getenv(EnvFacadeID),
|
||||
RunnerBinary: os.Getenv("GITEA_RUNNER_BINARY"), ListenAddress: "127.0.0.1:0",
|
||||
Stdout: os.Stdout, Stderr: os.Stderr,
|
||||
RunnerBinary: os.Getenv("GITEA_RUNNER_BINARY"), RunnerConfig: os.Getenv("GITEA_RUNNER_CONFIG_FILE"),
|
||||
ListenAddress: "127.0.0.1:0",
|
||||
Stdout: os.Stdout, Stderr: os.Stderr,
|
||||
}
|
||||
if config.AssignmentID == "" || config.Capability == "" || config.FacadeURL == "" || config.FacadeSPIFFEID == "" {
|
||||
return ExecutorConfig{}, errors.New("complete runner assignment and facade environment is required")
|
||||
|
||||
@@ -4,11 +4,19 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRunnerArgumentsLoadJobHooksConfig(t *testing.T) {
|
||||
want := []string{"daemon", "--config", "/etc/gitea-runner/config.yaml", "--once"}
|
||||
if got := runnerArguments("/etc/gitea-runner/config.yaml"); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("runner arguments = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForFacadeRetriesTransientGatewayFailure(t *testing.T) {
|
||||
var requests atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
|
||||
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
set +x
|
||||
|
||||
: "${GITHUB_SHA:?GITHUB_SHA is required}"
|
||||
: "${IMAGE_NAME:?IMAGE_NAME is required}"
|
||||
: "${IMAGE_REPOSITORY:?IMAGE_REPOSITORY is required}"
|
||||
: "${IMAGE_DOCKERFILE:?IMAGE_DOCKERFILE is required}"
|
||||
: "${PUSH_REGISTRY:?PUSH_REGISTRY is required}"
|
||||
: "${PULL_REGISTRY:?PULL_REGISTRY is required}"
|
||||
: "${SPIRE_AGENT_SOCKET:?SPIRE_AGENT_SOCKET is required}"
|
||||
|
||||
# Bootstrap the image that first introduces automatic Docker setup. Once that
|
||||
# runner is deployed, the job-started hook makes this an idempotent no-op.
|
||||
sudo scripts/setup-job-docker
|
||||
|
||||
image_tag="sha-${GITHUB_SHA}"
|
||||
metadata="${IMAGE_NAME}-metadata.json"
|
||||
docker_config=$(mktemp -d)
|
||||
jwt_file=$(mktemp)
|
||||
cleanup() {
|
||||
docker buildx rm ci-builder >/dev/null 2>&1 || true
|
||||
rm -rf -- "$docker_config" "$jwt_file"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
export DOCKER_CONFIG="$docker_config"
|
||||
|
||||
/opt/spire/bin/spire-agent api fetch jwt \
|
||||
-audience zot \
|
||||
-socketPath "$SPIRE_AGENT_SOCKET" \
|
||||
-output json >"$jwt_file"
|
||||
# shellcheck disable=SC2016
|
||||
jq -er '.[0].svids[0].svid' "$jwt_file" | \
|
||||
docker login "$PUSH_REGISTRY" --username zot --password-stdin
|
||||
|
||||
docker buildx create \
|
||||
--name ci-builder \
|
||||
--driver docker-container \
|
||||
--use
|
||||
|
||||
docker buildx build \
|
||||
--builder ci-builder \
|
||||
--platform linux/amd64 \
|
||||
--file "$IMAGE_DOCKERFILE" \
|
||||
--tag "${PUSH_REGISTRY}/${IMAGE_REPOSITORY}:${image_tag}" \
|
||||
--tag "${PUSH_REGISTRY}/${IMAGE_REPOSITORY}:main" \
|
||||
--provenance=mode=max \
|
||||
--sbom=true \
|
||||
--metadata-file "$metadata" \
|
||||
--push \
|
||||
.
|
||||
|
||||
image_digest=$(
|
||||
# shellcheck disable=SC2016
|
||||
jq -er '."containerimage.digest"' "$metadata"
|
||||
)
|
||||
image_ref="${PULL_REGISTRY}/${IMAGE_REPOSITORY}@${image_digest}"
|
||||
|
||||
printf '%s=%s\n' "$IMAGE_NAME" "$image_ref"
|
||||
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
||||
{
|
||||
printf '## Published image\n\n'
|
||||
# shellcheck disable=SC2016
|
||||
printf -- '- %s: `%s`\n' "$IMAGE_NAME" "$image_ref"
|
||||
# shellcheck disable=SC2016
|
||||
printf -- '- Source: `%s`\n' "$GITHUB_SHA"
|
||||
} >>"$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
@@ -36,15 +36,15 @@ if [[ -f /sys/fs/cgroup/cgroup.controllers ]]; then
|
||||
printf "%s\n" "$pid" \
|
||||
>/sys/fs/cgroup/init/cgroup.procs 2>/dev/null || true
|
||||
done </sys/fs/cgroup/cgroup.procs
|
||||
sed -e "s/ / +/g" -e "s/^/+" \
|
||||
sed -e "s/ / +/g" -e "s/^/+/" \
|
||||
/sys/fs/cgroup/cgroup.controllers \
|
||||
>/sys/fs/cgroup/cgroup.subtree_control
|
||||
'
|
||||
fi
|
||||
|
||||
sudo sh -c 'exec nohup dockerd "$@" >/tmp/dockerd.log 2>&1' sh \
|
||||
sudo sh -c 'nohup dockerd "$@" </dev/null >/tmp/dockerd.log 2>&1 &' sh \
|
||||
--host=unix:///var/run/docker.sock \
|
||||
--storage-driver="$storage_driver" &
|
||||
--storage-driver="$storage_driver"
|
||||
printf '%s\n' 'waiting for job Docker daemon'
|
||||
for ((attempt = 0; attempt < wait_seconds; attempt++)); do
|
||||
if docker info >/dev/null 2>&1; then
|
||||
|
||||
Reference in New Issue
Block a user