mirror of
https://codeberg.org/Toasterson/solstice-ci.git
synced 2026-04-10 21:30:41 +00:00
Add public runner URL configuration and enhance log streaming support
- Introduce options for specifying public runner base URLs (`SOLSTICE_RUNNER_BASE_URL`) and orchestrator contact addresses (`ORCH_CONTACT_ADDR`). - Update `.env.sample` and `compose.yml` with new configuration fields for external log streaming and runner binary serving. - Refactor runner URL handling and generation logic for improved flexibility. - Enhance `cloud-init` templates with updated runner URL environment variables (`RUNNER_SINGLE` and `RUNNER_URLS`). - Add unit tests for runner URL generation to verify various input cases. Signed-off-by: Till Wegmueller <toasterson@gmail.com>
This commit is contained in:
parent
1e48b1de66
commit
930efe547f
4 changed files with 84 additions and 19 deletions
|
|
@ -49,6 +49,16 @@ struct Opts {
|
|||
#[arg(long, env = "GRPC_ADDR", default_value = "0.0.0.0:50051")]
|
||||
grpc_addr: String,
|
||||
|
||||
/// Public contact address for runners to stream logs to (host:port). Overrides detection.
|
||||
#[arg(long = "orch-contact-addr", env = "ORCH_CONTACT_ADDR")]
|
||||
orch_contact_addr: Option<String>,
|
||||
|
||||
/// Public base URL where runner binaries are served (preferred). Example: https://runner.svc.domain
|
||||
/// The orchestrator will append /runners/{filename} to construct full URLs.
|
||||
#[arg(long = "runner-base-url", env = "SOLSTICE_RUNNER_BASE_URL")]
|
||||
runner_base_url: Option<String>,
|
||||
|
||||
|
||||
/// Postgres connection string (if empty, persistence is disabled)
|
||||
#[arg(
|
||||
long,
|
||||
|
|
@ -152,9 +162,9 @@ async fn main() -> Result<()> {
|
|||
});
|
||||
|
||||
// Orchestrator contact address for runner to dial back (auto-detect if not provided)
|
||||
let orch_contact = match std::env::var("ORCH_CONTACT_ADDR") {
|
||||
Ok(v) => v,
|
||||
Err(_) => detect_contact_addr(&opts),
|
||||
let orch_contact = match &opts.orch_contact_addr {
|
||||
Some(v) if !v.trim().is_empty() => v.clone(),
|
||||
_ => detect_contact_addr(&opts),
|
||||
};
|
||||
info!(contact = %orch_contact, "orchestrator contact address determined");
|
||||
|
||||
|
|
@ -167,12 +177,14 @@ async fn main() -> Result<()> {
|
|||
.rsplit(':')
|
||||
.next()
|
||||
.unwrap_or("8081");
|
||||
let base = format!("http://{}:{}/runners", http_host, http_port);
|
||||
let linux_url = format!("{}/{}", base, "solstice-runner-linux");
|
||||
let illumos_url = format!("{}/{}", base, "solstice-runner-illumos");
|
||||
let single_url = format!("{}/{}", base, "solstice-runner");
|
||||
let base = format!("http://{}:{}", http_host, http_port);
|
||||
let (single_url, multi_urls) = build_runner_urls(&base);
|
||||
// Log concrete OS URLs for local serving
|
||||
let mut parts = multi_urls.split_whitespace();
|
||||
let linux_url = parts.next().unwrap_or("");
|
||||
let illumos_url = parts.next().unwrap_or("");
|
||||
info!(linux = %linux_url, illumos = %illumos_url, "serving runner binaries via orchestrator HTTP");
|
||||
(single_url, format!("{} {}", linux_url, illumos_url))
|
||||
(single_url, multi_urls)
|
||||
} else {
|
||||
(String::new(), String::new())
|
||||
};
|
||||
|
|
@ -195,9 +207,20 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
});
|
||||
|
||||
// Determine runner URLs to inject into cloud-init
|
||||
let runner_url_env = std::env::var("SOLSTICE_RUNNER_URL").unwrap_or_else(|_| runner_url_default.clone());
|
||||
let runner_urls_env = std::env::var("SOLSTICE_RUNNER_URLS").unwrap_or_else(|_| runner_urls_default.clone());
|
||||
// Determine runner URLs to inject into cloud-init (prefer base URL)
|
||||
let (runner_url_env, runner_urls_env) = if let Some(base) = opts
|
||||
.runner_base_url
|
||||
.as_ref()
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
{
|
||||
let (u1, u2) = build_runner_urls(base);
|
||||
info!(base = %base, url = %u1, urls = %u2, "using public runner base URL");
|
||||
(u1, u2)
|
||||
} else {
|
||||
// Fall back to URLs served by this orchestrator's HTTP (when RUNNER_DIR configured)
|
||||
(runner_url_default.clone(), runner_urls_default.clone())
|
||||
};
|
||||
|
||||
// Consumer: enqueue and ack-on-accept
|
||||
let cfg_clone = cfg.clone();
|
||||
|
|
@ -435,6 +458,20 @@ fn extract_attr_value<'a>(tag: &'a str, key: &'a str) -> Option<&'a str> {
|
|||
None
|
||||
}
|
||||
|
||||
fn build_runner_urls(base: &str) -> (String, String) {
|
||||
// Normalize base and ensure it ends with /runners
|
||||
let trimmed = base.trim_end_matches('/');
|
||||
let base = if trimmed.ends_with("/runners") {
|
||||
trimmed.to_string()
|
||||
} else {
|
||||
format!("{}/runners", trimmed)
|
||||
};
|
||||
let single = format!("{}/{}", base, "solstice-runner");
|
||||
let linux = format!("{}/{}", base, "solstice-runner-linux");
|
||||
let illumos = format!("{}/{}", base, "solstice-runner-illumos");
|
||||
(single, format!("{} {}", linux, illumos))
|
||||
}
|
||||
|
||||
fn make_cloud_init_userdata(
|
||||
repo_url: &str,
|
||||
commit_sha: &str,
|
||||
|
|
@ -461,8 +498,8 @@ write_files:
|
|||
echo "Solstice: bootstrapping workflow runner for {sha}" | tee /dev/console
|
||||
RUNNER="/usr/local/bin/solstice-runner"
|
||||
# Runner URL(s) provided by orchestrator (local dev) if set
|
||||
export SOLSTICE_RUNNER_URL='{runner_url}'
|
||||
export SOLSTICE_RUNNER_URLS='{runner_urls}'
|
||||
RUNNER_SINGLE='{runner_url}'
|
||||
RUNNER_URLS='{runner_urls}'
|
||||
if [ ! -x "$RUNNER" ]; then
|
||||
mkdir -p /usr/local/bin
|
||||
# Helper to download from a URL to $RUNNER
|
||||
|
|
@ -481,12 +518,12 @@ write_files:
|
|||
}}
|
||||
OS=$(uname -s 2>/dev/null || echo unknown)
|
||||
# Prefer single URL if provided
|
||||
if [ -n "$SOLSTICE_RUNNER_URL" ]; then
|
||||
fetch_runner "$SOLSTICE_RUNNER_URL" || true
|
||||
if [ -n "$RUNNER_SINGLE" ]; then
|
||||
fetch_runner "$RUNNER_SINGLE" || true
|
||||
fi
|
||||
# If still missing, iterate URLs with a basic OS-based preference
|
||||
if [ ! -x "$RUNNER" ] && [ -n "$SOLSTICE_RUNNER_URLS" ]; then
|
||||
for U in $SOLSTICE_RUNNER_URLS; do
|
||||
if [ ! -x "$RUNNER" ] && [ -n "$RUNNER_URLS" ]; then
|
||||
for U in $RUNNER_URLS; do
|
||||
case "$OS" in
|
||||
Linux)
|
||||
echo "$U" | grep -qi linux || continue ;;
|
||||
|
|
@ -498,8 +535,8 @@ write_files:
|
|||
done
|
||||
fi
|
||||
# As a final fallback, try all URLs regardless of OS tag
|
||||
if [ ! -x "$RUNNER" ] && [ -n "$SOLSTICE_RUNNER_URLS" ]; then
|
||||
for U in $SOLSTICE_RUNNER_URLS; do
|
||||
if [ ! -x "$RUNNER" ] && [ -n "$RUNNER_URLS" ]; then
|
||||
for U in $RUNNER_URLS; do
|
||||
fetch_runner "$U" && break || true
|
||||
done
|
||||
fi
|
||||
|
|
@ -549,6 +586,19 @@ mod tests {
|
|||
assert!(m.get("other").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_runner_urls_variants() {
|
||||
let (s, m) = super::build_runner_urls("https://runner.svc.example");
|
||||
assert_eq!(s, "https://runner.svc.example/runners/solstice-runner");
|
||||
assert_eq!(m, "https://runner.svc.example/runners/solstice-runner-linux https://runner.svc.example/runners/solstice-runner-illumos");
|
||||
let (s2, m2) = super::build_runner_urls("https://runner.svc.example/");
|
||||
assert_eq!(s2, s);
|
||||
assert_eq!(m2, m);
|
||||
let (s3, m3) = super::build_runner_urls("https://runner.svc.example/runners");
|
||||
assert_eq!(s3, s);
|
||||
assert_eq!(m3, m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_make_cloud_init_userdata_includes_fields() {
|
||||
let req_id = uuid::Uuid::new_v4();
|
||||
|
|
|
|||
|
|
@ -418,6 +418,7 @@ mod tests {
|
|||
.send(SchedItem {
|
||||
spec: make_spec("b"),
|
||||
ctx: make_ctx(),
|
||||
original: None,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,16 @@ ORCH_WORK_DIR=/var/lib/solstice-ci
|
|||
# Default points to the workspace target/runners where mise tasks may place built artifacts.
|
||||
RUNNER_DIR_HOST=../../target/runners
|
||||
|
||||
# When orchestrator runs behind NAT or in containers, set the public contact address
|
||||
# that VMs can reach for gRPC log streaming (host:port). This overrides autodetection.
|
||||
# Example: grpc.${ENV}.${DOMAIN}:443 (when terminated by Traefik) or a public IP:port
|
||||
ORCH_CONTACT_ADDR=
|
||||
|
||||
# Preferred: Provide a public base URL for runner binaries; the orchestrator will construct
|
||||
# full URLs like ${SOLSTICE_RUNNER_BASE_URL}/runners/solstice-runner(-linux|-illumos)
|
||||
# Example: https://runner.svc.${DOMAIN}
|
||||
SOLSTICE_RUNNER_BASE_URL=
|
||||
|
||||
# Forge Integration secrets (set per deployment)
|
||||
# Shared secret used to validate Forgejo/Gitea webhooks (X-Gitea-Signature HMAC-SHA256)
|
||||
WEBHOOK_SECRET=
|
||||
|
|
|
|||
|
|
@ -195,6 +195,10 @@ services:
|
|||
# Libvirt configuration for Linux/KVM
|
||||
LIBVIRT_URI: ${LIBVIRT_URI:-qemu:///system}
|
||||
LIBVIRT_NETWORK: ${LIBVIRT_NETWORK:-default}
|
||||
# Public contact address for runners to stream logs to (host:port); overrides autodetection
|
||||
ORCH_CONTACT_ADDR: ${ORCH_CONTACT_ADDR}
|
||||
# Preferred: public base URL for runner binaries
|
||||
SOLSTICE_RUNNER_BASE_URL: ${SOLSTICE_RUNNER_BASE_URL}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue