canic-host 0.68.0

Host-side build, install, deployment, and fleet-template library for Canic workspaces
Documentation
use super::super::executor::{
    validate_deployment_execution_preflight, validate_deployment_execution_preflight_for_check,
};
use super::super::{
    ArtifactPromotionPlanV1, DEPLOYMENT_TRUTH_SCHEMA_VERSION, DeploymentCheckV1,
    PromotionArtifactIdentityReportV1, PromotionReadinessStatusV1, PromotionReadinessV1,
    PromotionTargetExecutionLineageV1, SafetyFindingV1,
};
use super::digest::{artifact_promotion_plan_digest, promotion_target_execution_lineage_digest};
use super::ensure::{
    ensure_artifact_promotion_plan_field, ensure_artifact_promotion_plan_sha256,
    ensure_target_execution_lineage_field, ensure_target_execution_lineage_sha256,
};
use super::error::{ArtifactPromotionPlanError, PromotionTargetExecutionLineageError};
use super::request::{ArtifactPromotionPlanRequest, PromotionTargetExecutionLineageRequest};

pub fn artifact_promotion_plan(
    request: ArtifactPromotionPlanRequest,
) -> Result<ArtifactPromotionPlanV1, ArtifactPromotionPlanError> {
    ensure_artifact_promotion_plan_field("plan_id", &request.plan_id)?;
    ensure_artifact_promotion_plan_field("generated_at", &request.generated_at)?;
    super::validate_promotion_readiness(&request.readiness)?;
    super::validate_promotion_artifact_identity_report(&request.artifact_identity_report)?;
    super::validate_promotion_plan_transform(&request.transform)?;
    if let Some(lineage) = &request.target_execution_lineage {
        validate_promotion_target_execution_lineage(lineage)?;
    }

    let blockers =
        artifact_promotion_plan_blockers(&request.readiness, &request.artifact_identity_report);
    let status = if blockers.is_empty() {
        PromotionReadinessStatusV1::Ready
    } else {
        PromotionReadinessStatusV1::Blocked
    };
    let mut plan = ArtifactPromotionPlanV1 {
        schema_version: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
        plan_id: request.plan_id,
        artifact_promotion_plan_digest: String::new(),
        generated_at: request.generated_at,
        status,
        target_plan_id: request.transform.target_plan_id.clone(),
        promoted_plan_id: request.transform.promoted_plan_id.clone(),
        promotion_plan_lineage_digest: request.transform.promotion_plan_lineage_digest.clone(),
        readiness: request.readiness,
        artifact_identity_report: request.artifact_identity_report,
        transform: request.transform,
        target_execution_lineage: request.target_execution_lineage,
        blockers,
    };
    plan.artifact_promotion_plan_digest = artifact_promotion_plan_digest(&plan);
    validate_artifact_promotion_plan(&plan)?;
    Ok(plan)
}

pub fn promotion_target_execution_lineage(
    request: PromotionTargetExecutionLineageRequest,
) -> Result<PromotionTargetExecutionLineageV1, PromotionTargetExecutionLineageError> {
    ensure_target_execution_lineage_field("lineage_id", &request.lineage_id)?;
    ensure_target_execution_lineage_field("generated_at", &request.generated_at)?;
    super::validate_promotion_plan_transform(&request.transform)?;
    validate_deployment_execution_preflight(&request.execution_preflight)?;

    let target_execution_lineage_digest = promotion_target_execution_lineage_digest(
        &request.transform,
        &request.execution_preflight,
        false,
    );
    let lineage = PromotionTargetExecutionLineageV1 {
        schema_version: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
        lineage_id: request.lineage_id,
        generated_at: request.generated_at,
        target_execution_lineage_digest,
        transform: request.transform,
        execution_preflight: request.execution_preflight,
        execution_attempted: false,
    };
    validate_promotion_target_execution_lineage(&lineage)?;
    Ok(lineage)
}

pub fn validate_artifact_promotion_plan(
    plan: &ArtifactPromotionPlanV1,
) -> Result<(), ArtifactPromotionPlanError> {
    if plan.schema_version != DEPLOYMENT_TRUTH_SCHEMA_VERSION {
        return Err(ArtifactPromotionPlanError::SchemaVersionMismatch {
            expected: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
            found: plan.schema_version,
        });
    }
    ensure_artifact_promotion_plan_field("plan_id", &plan.plan_id)?;
    ensure_artifact_promotion_plan_sha256(
        "artifact_promotion_plan_digest",
        &plan.artifact_promotion_plan_digest,
    )?;
    ensure_artifact_promotion_plan_field("generated_at", &plan.generated_at)?;
    ensure_artifact_promotion_plan_field("target_plan_id", &plan.target_plan_id)?;
    ensure_artifact_promotion_plan_field("promoted_plan_id", &plan.promoted_plan_id)?;
    ensure_artifact_promotion_plan_field(
        "promotion_plan_lineage_digest",
        &plan.promotion_plan_lineage_digest,
    )?;
    ensure_artifact_promotion_status_matches_blockers(plan)?;
    super::validate_promotion_readiness(&plan.readiness)?;
    super::validate_promotion_artifact_identity_report(&plan.artifact_identity_report)?;
    super::validate_promotion_plan_transform(&plan.transform)?;
    ensure_artifact_promotion_plan_linkage(plan)?;
    if let Some(lineage) = &plan.target_execution_lineage {
        validate_promotion_target_execution_lineage(lineage)?;
        if lineage.transform != plan.transform {
            return Err(ArtifactPromotionPlanError::LinkageMismatch {
                field: "target_execution_lineage.transform",
            });
        }
    }
    if plan.artifact_promotion_plan_digest != artifact_promotion_plan_digest(plan) {
        return Err(ArtifactPromotionPlanError::LinkageMismatch {
            field: "artifact_promotion_plan_digest",
        });
    }
    Ok(())
}

pub fn validate_artifact_promotion_plan_for_check(
    plan: &ArtifactPromotionPlanV1,
    target_check: &DeploymentCheckV1,
) -> Result<(), ArtifactPromotionPlanError> {
    validate_artifact_promotion_plan(plan)?;
    if target_check.plan != plan.transform.promoted_plan {
        return Err(ArtifactPromotionPlanError::LinkageMismatch {
            field: "target_check.plan",
        });
    }
    let Some(lineage) = &plan.target_execution_lineage else {
        return Err(ArtifactPromotionPlanError::MissingTargetExecutionLineage);
    };
    validate_deployment_execution_preflight_for_check(target_check, &lineage.execution_preflight)
        .map_err(ArtifactPromotionPlanError::TargetCheck)?;
    Ok(())
}

pub fn validate_promotion_target_execution_lineage(
    lineage: &PromotionTargetExecutionLineageV1,
) -> Result<(), PromotionTargetExecutionLineageError> {
    if lineage.schema_version != DEPLOYMENT_TRUTH_SCHEMA_VERSION {
        return Err(
            PromotionTargetExecutionLineageError::SchemaVersionMismatch {
                expected: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
                found: lineage.schema_version,
            },
        );
    }
    ensure_target_execution_lineage_field("lineage_id", &lineage.lineage_id)?;
    ensure_target_execution_lineage_field("generated_at", &lineage.generated_at)?;
    ensure_target_execution_lineage_sha256(
        "target_execution_lineage_digest",
        &lineage.target_execution_lineage_digest,
    )?;
    super::validate_promotion_plan_transform(&lineage.transform)?;
    validate_deployment_execution_preflight(&lineage.execution_preflight)?;
    if lineage.execution_attempted {
        return Err(PromotionTargetExecutionLineageError::ExecutionAttempted);
    }
    if lineage.execution_preflight.plan_id != lineage.transform.promoted_plan_id {
        return Err(PromotionTargetExecutionLineageError::LinkageMismatch {
            field: "execution_preflight.plan_id",
        });
    }
    let expected = promotion_target_execution_lineage_digest(
        &lineage.transform,
        &lineage.execution_preflight,
        lineage.execution_attempted,
    );
    if expected != lineage.target_execution_lineage_digest {
        return Err(PromotionTargetExecutionLineageError::LinkageMismatch {
            field: "target_execution_lineage_digest",
        });
    }
    Ok(())
}

fn artifact_promotion_plan_blockers(
    readiness: &PromotionReadinessV1,
    artifact_identity_report: &PromotionArtifactIdentityReportV1,
) -> Vec<SafetyFindingV1> {
    let mut blockers =
        Vec::with_capacity(readiness.blockers.len() + artifact_identity_report.blockers.len());
    blockers.extend(readiness.blockers.clone());
    blockers.extend(artifact_identity_report.blockers.clone());
    blockers
}

const fn ensure_artifact_promotion_status_matches_blockers(
    plan: &ArtifactPromotionPlanV1,
) -> Result<(), ArtifactPromotionPlanError> {
    match (plan.status, plan.blockers.is_empty()) {
        (PromotionReadinessStatusV1::Ready, false)
        | (PromotionReadinessStatusV1::Blocked, true) => {
            Err(ArtifactPromotionPlanError::StatusBlockerMismatch {
                status: plan.status,
                blocker_count: plan.blockers.len(),
            })
        }
        _ => Ok(()),
    }
}

fn ensure_artifact_promotion_plan_linkage(
    plan: &ArtifactPromotionPlanV1,
) -> Result<(), ArtifactPromotionPlanError> {
    let expected_blockers =
        artifact_promotion_plan_blockers(&plan.readiness, &plan.artifact_identity_report);
    if expected_blockers != plan.blockers {
        return Err(ArtifactPromotionPlanError::LinkageMismatch { field: "blockers" });
    }
    if plan.readiness.target_plan_id != plan.target_plan_id {
        return Err(ArtifactPromotionPlanError::LinkageMismatch {
            field: "readiness.target_plan_id",
        });
    }
    if plan.transform.target_plan_id != plan.target_plan_id {
        return Err(ArtifactPromotionPlanError::LinkageMismatch {
            field: "transform.target_plan_id",
        });
    }
    if plan.transform.promoted_plan_id != plan.promoted_plan_id {
        return Err(ArtifactPromotionPlanError::LinkageMismatch {
            field: "transform.promoted_plan_id",
        });
    }
    if plan.transform.promotion_plan_lineage_digest != plan.promotion_plan_lineage_digest {
        return Err(ArtifactPromotionPlanError::LinkageMismatch {
            field: "promotion_plan_lineage_digest",
        });
    }
    Ok(())
}