OXYGENHUB

Coverage / crates/hub/src/db/runner_updates.rs 91.0%

101 of 111 lines run, 29 of 62 functions · run #481 on main · run missed

1
//! Runner updates (oxygenhub#322): the policy at each level and the test
2
//! groups runners are put in.
3
4
use super::{now_ms, Pool};
5
use sqlx::FromRow;
6
7
/// One level's settings; `None` inherits from the next level out.
8
#[derive(Clone, Debug, Default, PartialEq, Eq, FromRow)]
9
pub struct Policy {
10
    pub scope: String,
11
    pub key: String,
12
    pub updates: Option<bool>,
13
    pub channel: Option<String>,
14
    pub window_from: Option<i64>,
15
    pub window_to: Option<i64>,
16
    pub dry_run: Option<bool>,
17
}
18
19
impl Policy {
20
    /// Nothing set: the level inherits everything.
218×
    pub fn is_empty(&self) -> bool {
228×
        self.updates.is_none() && self.channel.is_none() && self.window_from.is_none() && self.dry_run.is_none()
238×
    }
24
}
25
26
#[derive(Clone, Debug, PartialEq, Eq, FromRow)]
27
pub struct Group {
28
    pub id: i64,
29
    /// Empty: a group of the site's runners.
30
    pub owner: String,
31
    pub name: String,
32
    /// A share of the runners in reach, by a stable hash of their ids;
33
    /// `None`: only the chosen ones.
34
    pub percent: Option<i64>,
35
    pub created_ms: i64,
36
}
37
3862×
pub async fn policies(pool: &Pool) -> sqlx::Result<Vec<Policy>> {
3962×
    sqlx::query_as("SELECT * FROM runner_update_policies").fetch_all(pool).await
4062×
}
41
420×
pub async fn policy(pool: &Pool, scope: &str, key: &str) -> sqlx::Result<Policy> {
430×
    let p: Option<Policy> = sqlx::query_as("SELECT * FROM runner_update_policies WHERE scope = $1 AND key = $2")
440×
        .bind(scope)
450×
        .bind(key)
460×
        .fetch_optional(pool)
470×
        .await?;
480×
    Ok(p.unwrap_or(Policy { scope: scope.into(), key: key.into(), ..Policy::default() }))
490×
}
50
51
/// Set a level's settings; one with nothing set is removed.
528×
pub async fn set_policy(pool: &Pool, p: &Policy) -> sqlx::Result<()> {
538×
    sqlx::query("DELETE FROM runner_update_policies WHERE scope = $1 AND key = $2").bind(&p.scope).bind(&p.key).execute(pool).await?;
548×
    if p.is_empty() {
550×
        return Ok(());
568×
    }
578×
    sqlx::query(
588×
        "INSERT INTO runner_update_policies (scope, key, updates, channel, window_from, window_to, dry_run)
598×
         VALUES ($1, $2, $3, $4, $5, $6, $7)",
608×
    )
618×
    .bind(&p.scope)
628×
    .bind(&p.key)
638×
    .bind(p.updates)
648×
    .bind(&p.channel)
658×
    .bind(p.window_from)
668×
    .bind(p.window_to)
678×
    .bind(p.dry_run)
688×
    .execute(pool)
698×
    .await?;
708×
    Ok(())
718×
}
72
7366×
pub async fn groups(pool: &Pool) -> sqlx::Result<Vec<Group>> {
7466×
    sqlx::query_as("SELECT * FROM runner_groups ORDER BY owner, name").fetch_all(pool).await
7566×
}
76
777×
pub async fn group(pool: &Pool, id: i64) -> sqlx::Result<Option<Group>> {
787×
    sqlx::query_as("SELECT * FROM runner_groups WHERE id = $1").bind(id).fetch_optional(pool).await
797×
}
80
813×
pub async fn create_group(pool: &Pool, owner: &str, name: &str, percent: Option<i64>) -> sqlx::Result<Group> {
823×
    sqlx::query_as("INSERT INTO runner_groups (owner, name, percent, created_ms) VALUES ($1, $2, $3, $4) RETURNING *")
833×
        .bind(owner)
843×
        .bind(name)
853×
        .bind(percent)
863×
        .bind(now_ms())
873×
        .fetch_one(pool)
883×
        .await
893×
}
90
911×
pub async fn set_percent(pool: &Pool, id: i64, percent: Option<i64>) -> sqlx::Result<()> {
921×
    sqlx::query("UPDATE runner_groups SET percent = $1 WHERE id = $2").bind(percent).bind(id).execute(pool).await?;
931×
    Ok(())
941×
}
95
96
/// Remove a group, its members and its policy.
971×
pub async fn delete_group(pool: &Pool, id: i64) -> sqlx::Result<()> {
981×
    sqlx::query("DELETE FROM runner_group_members WHERE group_id = $1").bind(id).execute(pool).await?;
991×
    sqlx::query("DELETE FROM runner_update_policies WHERE scope = 'group' AND key = $1").bind(id.to_string()).execute(pool).await?;
1001×
    sqlx::query("DELETE FROM runner_groups WHERE id = $1").bind(id).execute(pool).await?;
1011×
    Ok(())
1021×
}
103
104
/// Every chosen member, as (group, runner).
10562×
pub async fn members(pool: &Pool) -> sqlx::Result<Vec<(i64, i64)>> {
10662×
    sqlx::query_as("SELECT group_id, runner_id FROM runner_group_members").fetch_all(pool).await
10762×
}
108
109
/// Make `runners` the group's chosen members.
1103×
pub async fn set_members(pool: &Pool, group: i64, runners: &[i64]) -> sqlx::Result<()> {
1113×
    sqlx::query("DELETE FROM runner_group_members WHERE group_id = $1").bind(group).execute(pool).await?;
1123×
    for r in runners {
1132×
        sqlx::query("INSERT INTO runner_group_members (group_id, runner_id) VALUES ($1, $2)").bind(group).bind(r).execute(pool).await?;
114
    }
1153×
    Ok(())
1163×
}
117
118
/// What a runner said about its update.
1192×
pub async fn report(pool: &Pool, runner: i64, note: &str) -> sqlx::Result<()> {
1202×
    sqlx::query("UPDATE flow_runners SET update_note = $1, update_ms = $2 WHERE id = $3")
1212×
        .bind(note)
1222×
        .bind(now_ms())
1232×
        .bind(runner)
1242×
        .execute(pool)
1252×
        .await?;
1262×
    Ok(())
1272×
}
128
129
/// A build held back from every runner (#326).
130
#[derive(Clone, Debug, PartialEq, Eq, FromRow)]
131
pub struct Hold {
132
    pub version: String,
133
    /// The runner it rolled back on.
134
    pub runner: String,
135
    pub why: String,
136
    pub at_ms: i64,
137
}
138
139
/// Hold `version` back, the first time it rolls back.
1401×
pub async fn hold(pool: &Pool, version: &str, runner: &str, why: &str) -> sqlx::Result<()> {
1411×
    sqlx::query("INSERT INTO runner_update_holds (version, runner, why, at_ms) VALUES ($1, $2, $3, $4) ON CONFLICT (version) DO NOTHING")
1421×
        .bind(version)
1431×
        .bind(runner)
1441×
        .bind(why)
1451×
        .bind(now_ms())
1461×
        .execute(pool)
1471×
        .await?;
1481×
    Ok(())
1491×
}
150
15119×
pub async fn holds(pool: &Pool) -> sqlx::Result<Vec<Hold>> {
15219×
    sqlx::query_as("SELECT * FROM runner_update_holds ORDER BY at_ms DESC").fetch_all(pool).await
15319×
}
154
15519×
pub async fn held(pool: &Pool, version: &str) -> sqlx::Result<Option<Hold>> {
15619×
    sqlx::query_as("SELECT * FROM runner_update_holds WHERE version = $1").bind(version).fetch_optional(pool).await
15719×
}
158
159
/// Lift the hold on `version`; whether there was one.
1603×
pub async fn lift(pool: &Pool, version: &str) -> sqlx::Result<bool> {
1613×
    Ok(sqlx::query("DELETE FROM runner_update_holds WHERE version = $1").bind(version).execute(pool).await?.rows_affected() > 0)
1623×
}