fn find_update(
    graph: client::Graph,
    booted_depl: Release,
    denylisted_depls: BTreeSet<Release>,
    allow_downgrade: bool,
) -> Result<Option<Release>, CincinnatiError> {
    GRAPH_NODES.set(graph.nodes.len() as i64);
    GRAPH_EDGES.set(graph.edges.len() as i64);
    log::trace!(
        "got an update graph with {} nodes and {} edges",
        graph.nodes.len(),
        graph.edges.len()
    );

    // Find booted deployment in graph.
    let (cur_position, cur_node) = match graph
        .nodes
        .iter()
        .enumerate()
        .find(|(_, node)| is_same_checksum(node, &booted_depl.checksum))
    {
        Some(current) => current,
        None => return Ok(None),
    };
    drop(booted_depl);
    let cur_release = Release::from_cincinnati(cur_node.clone())
        .map_err(|e| CincinnatiError::FailedNodeParsing(e.to_string()))?;

    if let Err(e) = refresh_deadend_status(cur_node) {
        log::warn!("failed to refresh dead-end status: {}", e);
    }
    // Evaluate and record whether booted OS is a dead-end release.
    // TODO(lucab): consider exposing this information in more places
    // (e.g. logs, motd, env/json file in a well-known location).
    let is_deadend: i64 = evaluate_deadend(cur_node).is_some().into();
    BOOTED_DEADEND.set(is_deadend);

    // Try to find all denylisted deployments in the graph too.
    let denylisted_releases = find_denylisted_releases(&graph, denylisted_depls);

    // Find all possible update targets from booted deployment.
    let targets: Vec<_> = graph
        .edges
        .iter()
        .filter_map(|(src, dst)| {
            if *src == cur_position as u64 {
                Some(*dst as usize)
            } else {
                None
            }
        })
        .collect();
    let mut updates = BTreeSet::new();
    for pos in targets {
        let node = match graph.nodes.get(pos) {
            Some(n) => n.clone(),
            None => {
                let msg = format!("target node '{}' not present in graph", pos);
                return Err(CincinnatiError::FailedNodeLookup(msg));
            }
        };
        let release = Release::from_cincinnati(node)
            .map_err(|e| CincinnatiError::FailedNodeParsing(e.to_string()))?;
        updates.insert(release);
    }

    // Exclude targets in denylist.
    let new_updates = updates.difference(&denylisted_releases);

    // Log that we will avoid updating to denylisted releases.
    let prev_deployed_excluded = updates.intersection(&denylisted_releases).count();
    if prev_deployed_excluded > 0 {
        log::debug!(
            "Found {} possible update target{} present in denylist; ignoring",
            prev_deployed_excluded,
            if prev_deployed_excluded > 1 { "s" } else { "" }
        );
    }
    UPDATE_TARGETS_IGNORED.set(prev_deployed_excluded as i64);

    // Pick highest available updates target (based on age-index).
    let next = match new_updates.last().cloned() {
        Some(rel) => rel,
        None => return Ok(None),
    };

    // Check for downgrades.
    if next <= cur_release {
        log::warn!("downgrade hint towards target release '{}'", next.version);
        if !allow_downgrade {
            log::warn!("update hint rejected, downgrades are not allowed by configuration");
            return Ok(None);
        }
    }

    Ok(Some(next))
}
/// Walk the graph, looking for an update reachable from the given digest.
Walk the graph, looking for an update reachable from the given digest.
Walk the graph looking for an update reachable from the given digest .
fn find_update(
    graph: client::Graph,
    booted_depl: Release,
    denylisted_depls: BTreeSet<Release>,
    allow_downgrade: bool,
) -> Result<Option<Release>, CincinnatiError> {
    GRAPH_NODES.set(graph.nodes.len() as i64);
    GRAPH_EDGES.set(graph.edges.len() as i64);
    log::trace!(
        "got an update graph with {} nodes and {} edges",
        graph.nodes.len(),
        graph.edges.len()
    );
    // Find booted deployment in graph.
    let (cur_position, cur_node) = match graph
        .nodes
        .iter()
        .enumerate()
        .find(|(_, node)| is_same_checksum(node, &booted_depl.checksum))
    {
        Some(current) => current,
        None => return Ok(None),
    };
    drop(booted_depl);
    let cur_release = Release::from_cincinnati(cur_node.clone())
        .map_err(|e| CincinnatiError::FailedNodeParsing(e.to_string()))?;
    if let Err(e) = refresh_deadend_status(cur_node) {
        log::warn!("failed to refresh dead-end status: {}", e);
    }
    // Evaluate and record whether booted OS is a dead-end release.
    // TODO(lucab): consider exposing this information in more places
    // (e.g. logs, motd, env/json file in a well-known location).
    let is_deadend: i64 = evaluate_deadend(cur_node).is_some().into();
    BOOTED_DEADEND.set(is_deadend);
    // Try to find all denylisted deployments in the graph too.
    let denylisted_releases = find_denylisted_releases(&graph, denylisted_depls);
    // Find all possible update targets from booted deployment.
    let targets: Vec<_> = graph
        .edges
        .iter()
        .filter_map(|(src, dst)| {
            if *src == cur_position as u64 {
                Some(*dst as usize)
            } else {
                None
            }
        })
        .collect();
    let mut updates = BTreeSet::new();
    for pos in targets {
        let node = match graph.nodes.get(pos) {
            Some(n) => n.clone(),
            None => {
                let msg = format!("target node '{}' not present in graph", pos);
                return Err(CincinnatiError::FailedNodeLookup(msg));
            }
        };
        let release = Release::from_cincinnati(node)
            .map_err(|e| CincinnatiError::FailedNodeParsing(e.to_string()))?;
        updates.insert(release);
    }
    // Exclude targets in denylist.
    let new_updates = updates.difference(&denylisted_releases);
    // Log that we will avoid updating to denylisted releases.
    let prev_deployed_excluded = updates.intersection(&denylisted_releases).count();
    if prev_deployed_excluded > 0 {
        log::debug!(
            "Found {} possible update target{} present in denylist; ignoring",
            prev_deployed_excluded,
            if prev_deployed_excluded > 1 { "s" } else { "" }
        );
    }
    UPDATE_TARGETS_IGNORED.set(prev_deployed_excluded as i64);
    // Pick highest available updates target (based on age-index).
    let next = match new_updates.last().cloned() {
        Some(rel) => rel,
        None => return Ok(None),
    };
    // Check for downgrades.
    if next <= cur_release {
        log::warn!("downgrade hint towards target release '{}'", next.version);
        if !allow_downgrade {
            log::warn!("update hint rejected, downgrades are not allowed by configuration");
            return Ok(None);
        }
    }
    Ok(Some(next))
}
fn find_update ( graph : client :: Graph , booted_depl : Release , denylisted_depls : BTreeSet < Release > , allow_downgrade : bool , ) -> Result < Option < Release > , CincinnatiError > { GRAPH_NODES . set ( graph . nodes . len ( ) as i64 ) ; GRAPH_EDGES . set ( graph . edges . len ( ) as i64 ) ; log :: trace ! ( " " graph nodes len ( ) graph edges len ( ) ) ; // Find booted deployment in graph. let ( cur_position , cur_node ) = match graph . nodes . iter ( ) . enumerate ( ) . find ( | ( _ , node ) | is_same_checksum ( node , & booted_depl . checksum ) ) { Some ( current ) => current , None => return Ok ( None ) , } ; drop ( booted_depl ) ; let cur_release = Release :: from_cincinnati ( cur_node . clone ( ) ) . map_err ( | e | CincinnatiError :: FailedNodeParsing ( e . to_string ( ) ) ) ? ; if let Err ( e ) = refresh_deadend_status ( cur_node ) { log :: warn ! ( " " e ) ; } // Evaluate and record whether booted OS is a dead-end release. // TODO(lucab): consider exposing this information in more places // (e.g. logs, motd, env/json file in a well-known location). let is_deadend : i64 = evaluate_deadend ( cur_node ) . is_some ( ) . into ( ) ; BOOTED_DEADEND . set ( is_deadend ) ; // Try to find all denylisted deployments in the graph too. let denylisted_releases = find_denylisted_releases ( & graph , denylisted_depls ) ; // Find all possible update targets from booted deployment. let targets : Vec < _ > = graph . edges . iter ( ) . filter_map ( | ( src , dst ) | { if * src == cur_position as u64 { Some ( * dst as usize ) } else { None } } ) . collect ( ) ; let mut updates = BTreeSet :: new ( ) ; for pos in targets { let node = match graph . nodes . get ( pos ) { Some ( n ) => n . clone ( ) , None => { let msg = format ! ( " " pos ) ; return Err ( CincinnatiError :: FailedNodeLookup ( msg ) ) ; } } ; let release = Release :: from_cincinnati ( node ) . map_err ( | e | CincinnatiError :: FailedNodeParsing ( e . to_string ( ) ) ) ? ; updates . insert ( release ) ; } // Exclude targets in denylist. let new_updates = updates . difference ( & denylisted_releases ) ; // Log that we will avoid updating to denylisted releases. let prev_deployed_excluded = updates . intersection ( & denylisted_releases ) . count ( ) ; if prev_deployed_excluded > 0 { log :: debug ! ( " " prev_deployed_excluded if prev_deployed_excluded 1 { " " } else { " " } ) ; } UPDATE_TARGETS_IGNORED . set ( prev_deployed_excluded as i64 ) ; // Pick highest available updates target (based on age-index). let next = match new_updates . last ( ) . cloned ( ) { Some ( rel ) => rel , None => return Ok ( None ) , } ; // Check for downgrades. if next <= cur_release { log :: warn ! ( " " next version ) ; if ! allow_downgrade { log :: warn ! ( " " ) ; return Ok ( None ) ; } } Ok ( Some ( next ) ) }
Walk the graph, looking for an update reachable from the given digest.
Walk the graph looking for an update reachable from the given digest .
graph
client::Graph
booted_depl
Release
denylisted_depls
BTreeSet<Release>
allow_downgrade
bool