Skip to content

brokkr-agent::work_orders::build

Rust

Build handler for Shipwright Build integration.

This module handles the execution of build work orders using Shipwright:

  • Parsing Build and WorkOrder resources from YAML
  • Applying Build resources to the cluster
  • Creating BuildRun resources
  • Watching BuildRun status until completion
  • Extracting results (image digest, errors)

Shipwright Resources

  • Build: Reusable template defining git source, build strategy, and output
  • BuildRun: Execution instance created from a Build
  • ClusterBuildStrategy: Pre-installed strategy (e.g., buildah)

pub

async fn execute_build(
k8s_client: &K8sClient,
yaml_content: &str,
work_order_id: &str,
deadline: Instant,
) -> Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>

Executes a build using Shipwright.

This function:

  1. Parses the YAML content to find Build resources
  2. Applies Build resources to the cluster
  3. Creates a BuildRun
  4. Watches the BuildRun until completion
  5. Returns the image digest on success or error details on failure

Parameters:

Name Type Description
k8s_client &K8sClient Kubernetes client
yaml_content &str Multi-document YAML containing Build and WorkOrder
work_order_id &str Work order ID for labeling resources
deadline Instant When the agent stops watching the BuildRun (see build_watch_budget)

Returns:

Optional result message (image digest on success)

Source
pub async fn execute_build(
k8s_client: &K8sClient,
yaml_content: &str,
work_order_id: &str,
deadline: Instant,
) -> Result<Option<String>, Box<dyn std::error::Error + Send + Sync>> {
info!("Starting build execution for work order {}", work_order_id);
// Parse YAML documents
let docs: Vec<serde_yaml::Value> = serde_yaml::Deserializer::from_str(yaml_content)
.map(serde_yaml::Value::deserialize)
.collect::<Result<Vec<_>, _>>()?;
if docs.is_empty() {
return Err("No YAML documents found in work order content".into());
}
// Find Build resources and apply them
let mut build_name: Option<String> = None;
let mut build_namespace = String::from("default");
for doc in &docs {
let api_version = doc.get("apiVersion").and_then(|v| v.as_str());
let kind = doc.get("kind").and_then(|v| v.as_str());
match (api_version, kind) {
(Some(av), Some("Build")) if av.starts_with(SHIPWRIGHT_API_GROUP) => {
// Apply the Build resource
let metadata = doc.get("metadata");
if let Some(name) = metadata
.and_then(|m| m.get("name"))
.and_then(|n| n.as_str())
{
build_name = Some(name.to_string());
}
if let Some(ns) = metadata
.and_then(|m| m.get("namespace"))
.and_then(|n| n.as_str())
{
build_namespace = ns.to_string();
}
info!("Applying Shipwright Build resource");
apply_shipwright_resource(k8s_client, doc).await?;
}
(Some(av), Some("WorkOrder")) if av.starts_with("brokkr.io") => {
// Extract buildRef from WorkOrder if present
if let Some(spec) = doc.get("spec")
&& let Some(build_ref) = spec
.get("buildRef")
.and_then(|b| b.get("name"))
.and_then(|n| n.as_str())
&& build_name.is_none()
{
build_name = Some(build_ref.to_string());
}
debug!("Found brokkr WorkOrder resource, skipping apply (handled separately)");
}
_ => {
debug!("Skipping non-build resource: {:?}/{:?}", api_version, kind);
}
}
}
let build_name = build_name.ok_or("No Build resource or buildRef found in YAML content")?;
info!(
"Using Build '{}' in namespace '{}'",
build_name, build_namespace
);
// Create BuildRun
let buildrun_name = format!(
"{}-{}",
build_name,
&work_order_id[..8.min(work_order_id.len())]
);
info!("Creating BuildRun '{}'", buildrun_name);
let _buildrun = create_buildrun(
k8s_client,
&buildrun_name,
&build_name,
&build_namespace,
work_order_id,
)
.await?;
info!(
"BuildRun '{}' created, waiting for completion",
buildrun_name
);
// Watch BuildRun until completion
let result =
watch_buildrun_completion(k8s_client, &buildrun_name, &build_namespace, deadline).await?;
Ok(result)
}