feat: add admin argo app, act-runner, and gitea configuration

This commit is contained in:
Seth Call 2026-03-09 16:44:27 -05:00
parent 47cf042722
commit d7bda1aeb1
6 changed files with 497 additions and 0 deletions

View File

@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: admin
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://bitbucket.org/jamkazam/video-iac.git
targetRevision: HEAD
path: k8s/jam-cloud
directory:
include: 'admin.yaml'
destination:
server: https://kubernetes.default.svc
namespace: jam-cloud
syncPolicy:
automated:
prune: true
selfHeal: true

View File

@ -0,0 +1,73 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: act-runner-config
namespace: jam-cloud-infra
data:
config.yaml: |
log:
level: info
runner:
capacity: 1
timeout: 3h
container:
network: ""
# Give the job container access to the Docker daemon so Dagger can spin up its engine
options: "-v /var/run/docker.sock:/var/run/docker.sock"
valid_volumes:
- "**"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: act-runner
namespace: jam-cloud-infra
labels:
app: act-runner
spec:
replicas: 1
selector:
matchLabels:
app: act-runner
template:
metadata:
labels:
app: act-runner
spec:
containers:
- name: runner
image: gitea/act_runner:latest
env:
- name: CONFIG_FILE
value: /etc/act_runner/config.yaml
- name: GITEA_INSTANCE_URL
value: http://gitea.jam-cloud-infra.svc.cluster.local:80
- name: GITEA_RUNNER_REGISTRATION_TOKEN
value: "UL6SkV1E8cN6M017vNrmN3X2PPGxmcIDjsbbUvuq"
- name: GITEA_RUNNER_NAME
value: "k8s-runner"
- name: GITEA_RUNNER_LABELS
value: "ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,dagger:docker://nixpkgs/nix:latest"
securityContext:
privileged: true
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
- name: config
mountPath: /etc/act_runner
- name: dind
image: docker:23.0.5-dind
env:
- name: DOCKER_TLS_CERTDIR
value: ""
securityContext:
privileged: true
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: docker-sock
emptyDir: {}
- name: config
configMap:
name: act-runner-config

View File

@ -0,0 +1,194 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitea-data
namespace: jam-cloud-infra
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: linode-block-storage-retain
---
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-config
namespace: jam-cloud-infra
data:
app.ini: |
APP_NAME = Gitea: Git with a cup of tea
RUN_MODE = prod
WORK_PATH = /data/gitea
[repository]
ROOT = /data/git/repositories
ALLOWED_SCHEMES = http,https,ssh,git
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[server]
APP_DATA_PATH = /data/gitea
DOMAIN = git.staging.jamkazam.com
SSH_DOMAIN = localhost
HTTP_PORT = 3000
ROOT_URL = https://git.staging.jamkazam.com/
DISABLE_SSH = false
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = false
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = sqlite3
HOST = localhost:3306
NAME = gitea
USER = root
PASSWD =
LOG_SQL = false
[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
[session]
PROVIDER_CONFIG = /data/gitea/sessions
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
[attachment]
PATH = /data/gitea/attachments
[log]
MODE = console
LEVEL = info
ROOT_PATH = /data/gitea/log
[security]
INSTALL_LOCK = true
SECRET_KEY =
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3NzMwMDMyODN9.c3kDP5f7-lo3yR-Z8mPiaAsSEsohWc-LxZksIqTcPWw
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
[lfs]
PATH = /data/git/lfs
[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = true
[oauth2]
ENABLE = true
JWT_SECRET = HDi5Td6dRBC240L6ryMI4eMnowcwQVpfKrmIPNrEeAI
[actions]
ENABLED = true
[migrations]
ALLOW_SSH_MIGRATIONS = true
ALLOWED_DOMAINS = *
ALLOW_LOCALNETWORKS = true
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea
namespace: jam-cloud-infra
labels:
app: gitea
spec:
replicas: 1
selector:
matchLabels:
app: gitea
template:
metadata:
labels:
app: gitea
spec:
containers:
- name: gitea
image: gitea/gitea:1.21.7
ports:
- containerPort: 3000
name: http
- containerPort: 22
name: ssh
env:
- name: GITEA_CUSTOM
value: /etc/gitea
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /etc/gitea/conf/
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "sleep 5; /sbin/su-exec git gitea admin user create --admin --username seth --password changeme123 --email seth@jamkazam.com --must-change-password=false || true"]
volumes:
- name: data
persistentVolumeClaim:
claimName: gitea-data
- name: config
configMap:
name: gitea-config
---
apiVersion: v1
kind: Service
metadata:
name: gitea
namespace: jam-cloud-infra
spec:
selector:
app: gitea
ports:
- port: 80
targetPort: 3000
name: http
- port: 22
targetPort: 22
name: ssh
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea
namespace: jam-cloud-infra
annotations:
cert-manager.io/cluster-issuer: letsencrypt-nginx-production
nginx.ingress.kubernetes.io/proxy-body-size: "512m"
spec:
ingressClassName: nginx
tls:
- secretName: gitea-tls
hosts:
- git.staging.jamkazam.com
- idp.staging.jamkazam.com
- console.staging.jamkazam.com
rules:
- host: git.staging.jamkazam.com
http: &gitea_path
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea
port:
number: 80
- host: idp.staging.jamkazam.com
http: *gitea_path
- host: console.staging.jamkazam.com
http: *gitea_path

69
k8s/jam-cloud/admin.yaml Normal file
View File

@ -0,0 +1,69 @@
apiVersion: v1
kind: Namespace
metadata:
name: jam-cloud
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: admin
namespace: jam-cloud
spec:
replicas: 1
selector:
matchLabels:
app: admin
template:
metadata:
labels:
app: admin
spec:
imagePullSecrets:
- name: gitea-registry
containers:
- name: web
# This will be replaced by the Dagger build pipeline on first run
image: git.staging.jamkazam.com/seth/jam-cloud-admin:latest
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
value: "postgres://jam:jam@72.14.176.182:5432/jam"
---
apiVersion: v1
kind: Service
metadata:
name: admin
namespace: jam-cloud
spec:
selector:
app: admin
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: admin
namespace: jam-cloud
annotations:
cert-manager.io/cluster-issuer: letsencrypt-nginx-production
spec:
ingressClassName: nginx
tls:
- secretName: admin-tls
hosts:
- admin.staging.jamkazam.com
rules:
- host: admin.staging.jamkazam.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin
port:
number: 80

5
scripts/gitea-repos.yaml Normal file
View File

@ -0,0 +1,5 @@
repositories:
- name: "jam-cloud"
url: "https://bitbucket.org/jamkazam/jam-cloud.git"
- name: "video-iac"
url: "https://bitbucket.org/jamkazam/video-iac.git"

134
scripts/sync-gitea-repos.rb Normal file
View File

@ -0,0 +1,134 @@
#!/usr/bin/env ruby
require 'net/http'
require 'json'
require 'uri'
require 'yaml'
require 'optparse'
# Configuration mapping
ENV_CONFIG = {
'stg' => {
url: "https://git.staging.jamkazam.com/api/v1",
token_var: "GITEA_TOKEN_STG"
},
'prd' => {
url: "https://git.jamkazam.com/api/v1",
token_var: "GITEA_TOKEN_PRD"
}
}
options = { env: 'stg' }
OptionParser.new do |opts|
opts.banner = "Usage: ruby sync-gitea-repos.rb [options] <bitbucket_user> <bitbucket_pass>"
opts.on("-e", "--env ENV", "Environment (stg or prd, default stg)") { |v| options[:env] = v }
end.parse!
if ARGV.length < 2
puts "❌ Error: Missing Bitbucket credentials."
puts "Usage: ruby sync-gitea-repos.rb --env [stg|prd] <bb_user> <bb_pass>"
exit 1
end
BB_USER = ARGV[0]
BB_PASS = ARGV[1]
# Select config
config = ENV_CONFIG[options[:env]]
if config.nil?
puts "❌ Error: Invalid environment '#{options[:env]}'. Use 'stg' or 'prd'."
exit 1
end
# Resolve Token
GITEA_TOKEN = ENV[config[:token_var]] || ENV['GITEA_TOKEN']
GITEA_URL = config[:url]
GITEA_OWNER = "seth"
MANIFEST_FILE = File.expand_path('gitea-repos.yaml', __dir__)
if GITEA_TOKEN.nil? || GITEA_TOKEN.empty?
puts "❌ Error: API Token environment variable '#{config[:token_var]}' is not set."
exit 1
end
def gitea_request(method, path, payload = nil)
uri = URI.parse("#{GITEA_URL}#{path}")
header = {
'Content-Type' => 'application/json',
'Authorization' => "token #{GITEA_TOKEN}"
}
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 300
case method
when :get then req = Net::HTTP::Get.new(uri.request_uri, header)
when :post then req = Net::HTTP::Post.new(uri.request_uri, header); req.body = payload.to_json if payload
when :delete then req = Net::HTTP::Delete.new(uri.request_uri, header)
end
http.request(req)
end
puts "🚀 Reconciling Gitea mirrors for #{options[:env].upcase}..."
puts "🔗 API: #{GITEA_URL}"
# 1. Load Manifest
manifest = YAML.load_file(MANIFEST_FILE)
desired_repos = manifest['repositories']
desired_names = desired_repos.map { |r| r['name'] }
# 2. Get Current State from Gitea
puts "🔍 Fetching current repositories..."
resp = gitea_request(:get, "/users/#{GITEA_OWNER}/repos")
if resp.code != "200"
puts "❌ Failed to fetch repos: #{resp.code} #{resp.body}"
exit 1
end
current_repos = JSON.parse(resp.body)
current_names = current_repos.map { |r| r['name'] }
# 3. Reconcile: Delete repos not in manifest
repos_to_delete = current_names - desired_names
repos_to_delete.each do |name|
puts "🗑️ Deleting repository '#{name}' (not in manifest)..."
gitea_request(:delete, "/repos/#{GITEA_OWNER}/#{name}")
end
# 4. Reconcile: Create/Update mirrors
desired_repos.each do |repo|
name = repo['name']
url = repo['url']
if current_names.include?(name)
repo_info = current_repos.find { |r| r['name'] == name }
if repo_info['empty']
puts "⚠️ Mirror '#{name}' exists but is EMPTY. Recreating..."
gitea_request(:delete, "/repos/#{GITEA_OWNER}/#{name}")
else
puts "✅ Mirror '#{name}' already exists. Skipping."
next
end
end
puts "🚀 Creating mirror for '#{name}'..."
payload = {
"clone_addr" => url,
"auth_username" => BB_USER,
"auth_password" => BB_PASS,
"repo_name" => name,
"mirror" => true,
"mirror_interval" => "10m",
"service" => "bitbucket",
"repo_owner" => GITEA_OWNER,
"lfs" => true,
"releases" => true
}
res = gitea_request(:post, "/repos/migrate", payload)
if res.code == "201"
puts "✨ Successfully created mirror for '#{name}'."
else
puts "❌ Failed to create mirror for '#{name}': #{res.code} #{res.body}"
end
end
puts "\n🎯 Reconcile complete for #{options[:env].upcase}."