Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-server/src/services/plugins.rs

//! **`Plugins`** — the operator's "Update plugins" ([`crate::plugins`]). Thin: it checks the
//! operator, queues the job on the server's jera lane and answers at once; `Status` reads
//! where a job stands.

use std::sync::Arc;

use monetize_proto as pb;
use monetize_proto::plugins_server::Plugins;
use tonic::{Request, Response, Status};

use crate::plugins::JobView;
use crate::state::AppState;

pub struct PluginsService(pub Arc<AppState>);

/// The operator's alone: the operator marker, never a site's own bearer.
fn operator_only<T>(req: &Request<T>) -> Result<(), Status> {
    if req.extensions().get::<crate::auth::Operator>().is_some()
        && req
            .extensions()
            .get::<crate::auth::CallerEstate>()
            .is_none()
    {
        Ok(())
    } else {
        Err(Status::permission_denied(
            "[plugins-operator] updating this box's plugins is the operator's alone",
        ))
    }
}

/// Pure: a job on the wire.
pub fn job_out(v: JobView) -> pb::PluginUpdateJob {
    pb::PluginUpdateJob {
        job: v.job,
        state: v.state,
        apply: v.apply,
        rows: v
            .rows
            .into_iter()
            .map(|r| pb::PluginRow {
                name: r.name,
                role: r.role,
                outcome: r.outcome,
                detail: r.detail,
                blake3: r.blake3,
            })
            .collect(),
        error: v.error,
    }
}

#[tonic::async_trait]
impl Plugins for PluginsService {
    async fn update(
        &self,
        req: Request<pb::PluginUpdateRequest>,
    ) -> Result<Response<pb::PluginUpdateJob>, Status> {
        operator_only(&req)?;
        let apply = req.into_inner().apply;
        let v = self
            .0
            .plugin_jobs
            .submit(&self.0, apply)
            .map_err(Status::unavailable)?;
        Ok(Response::new(job_out(v)))
    }

    async fn status(
        &self,
        req: Request<pb::PluginUpdateStatusRequest>,
    ) -> Result<Response<pb::PluginUpdateJob>, Status> {
        operator_only(&req)?;
        let job = req.into_inner().job;
        let v = self.0.plugin_jobs.status(&job).ok_or_else(|| {
            Status::not_found(format!(
                "[plugins-no-job] no plugin update {}",
                if job.trim().is_empty() {
                    "has run on this server yet".to_string()
                } else {
                    format!("`{}`", job.trim())
                }
            ))
        })?;
        Ok(Response::new(job_out(v)))
    }
}