diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dce011d..415117e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,111 +1,26 @@ -name: Rust +name: Build with rust and docker on: push: + workflow_dispatch: pull_request: schedule: # Fetch new base image updates every night at 1am - cron: '0 1 * * *' -env: - CARGO_TERM_COLOR: always - PROFILE: release - jobs: - pre-check: - name: Security, License Check - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: EmbarkStudios/cargo-deny-action@v1 - - build-rust: - name: Build (Rust) - runs-on: ubuntu-20.04 - - strategy: - matrix: - arch: - - amd64 - - arm64 - - steps: - - name: Set arch ${{ matrix.arch }} - env: - ARCH: ${{ matrix.arch }} - run: | - if [ "${ARCH}" == "arm64" ]; then - echo "rustarch=aarch64-unknown-linux-gnu" >> $GITHUB_ENV - elif [ "${ARCH}" == "amd64" ]; then - echo "rustarch=x86_64-unknown-linux-gnu" >> $GITHUB_ENV - else - exit 1 - fi - if [ "$(dpkg --print-architecture)" != "${ARCH}" ]; then - echo "Cross-compiling to ${ARCH}." - echo "is_cross=true" >> $GITHUB_ENV - else - echo "Natively compiling to ${ARCH}." - echo "is_cross=false" >> $GITHUB_ENV - fi - - name: Set profile ${{ env.PROFILE }} - env: - PROFILE: ${{ env.PROFILE }} - run: | - if [ "${PROFILE}" == "release" ]; then - echo "profilestr=--release" >> $GITHUB_ENV - elif [ "${PROFILE}" == "debug" ]; then - echo "profilestr=" >> $GITHUB_ENV - else - echo "profilestr=--profile $PROFILE" >> $GITHUB_ENV - fi - - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - target: ${{ env.rustarch }} - - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.arch }}-${{ env.PROFILE }} - - name: Build (${{ matrix.arch }}) - uses: actions-rs/cargo@v1 - with: - use-cross: ${{ env.is_cross }} - command: build - args: --target ${{ env.rustarch }} ${{ env.profilestr }} - - name: Upload Artifact - uses: actions/upload-artifact@v3 - with: - name: binaries-${{ matrix.arch }} - path: | - target/${{ env.rustarch }}/${{ env.PROFILE }}/bridgehead-monitoring - - - docker: - needs: [ build-rust, pre-check ] - if: github.ref_protected == true - - # This workflow defines how a maven package is built, tested and published. - # Visit: https://github.com/samply/github-workflows/blob/develop/.github/workflows/docker-ci.yml, for more information - uses: samply/github-workflows/.github/workflows/docker-ci.yml@main + build-with-samply: + uses: samply/github-workflows/.github/workflows/rust.yml@main with: - # The Docker Hub Repository you want eventually push to, e.g samply/share-client - image-name: "samply/bridgehead-monitoring" - # Define special prefixes for docker tags. They will prefix each images tag. - # image-tag-prefix: "foo" - # Define the build context of your image, typically default '.' will be enough - # build-context: '.' - # Define the Dockerfile of your image, typically default './Dockerfile' will be enough - build-file: './Dockerfile' - # NOTE: This doesn't work currently - # A list of build arguments, passed to the docker build - # Define the target platforms of the docker build (default "linux/amd64,linux/arm64/v8") - # build-platforms: "linux/amd64,linux/arm64" - # If your actions generate an artifact in a previous build step, you can tell this workflow to download it - artifact-name: '*' - # This passes the secrets from calling workflow to the called workflow + # For information on these variables, please refer to https://github.com/samply/github-workflows/tree/main/.github/workflows/rust.yml + # Docker Hub name will be {image-prefix}{component} + image-prefix: "samply/" + components: '[ "bridgehead-monitoring" ]' + #architectures: '[ "amd64" ]' + #profile: debug + #test-via-script: true + #features: '[ "", "sockets" ]' + push-to: ${{ (github.ref_protected == true || github.event_name == 'workflow_dispatch') && 'dockerhub' || 'ghcr' }} secrets: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index d61ba3d..44ddd19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] +resolver = "2" members = [ "bin", "lib", diff --git a/bin/src/checks.rs b/bin/src/checks.rs index 66ffe2d..d05ff26 100644 --- a/bin/src/checks.rs +++ b/bin/src/checks.rs @@ -1,43 +1,36 @@ use async_trait::async_trait; -use bridgehead_monitoring_lib::Check; +use bridgehead_monitoring_lib::{Check, CheckResult}; +use reqwest::StatusCode; use serde_json::Value; use crate::{CLIENT, CONFIG}; #[async_trait] pub trait CheckExecutor { - async fn execute(&self) -> String; + async fn execute(&self) -> Result; } #[async_trait] impl CheckExecutor for Check { - async fn execute(&self) -> String { + async fn execute(&self) -> Result { match self { Check::BlazeHealth => { - match CLIENT.get(format!("{}health", CONFIG.blaze_url)).send().await { - Ok(res) => { - res.status().to_string() - }, - Err(e) => e.to_string() + let res = CLIENT.get(format!("{}health", CONFIG.blaze_url)).send().await?; + if res.status() == StatusCode::OK { + Ok(CheckResult::Ok(res.status().to_string())) + } else { + Err(CheckResult::Err(format!("Unhealthy: {}", res.status()))) } }, Check::BlazeResources => { - match CLIENT.get(format!("{}fhir", CONFIG.blaze_url)).send().await { - Ok(res) => { - let json = &res.json::().await.unwrap_or(Value::Null)["total"]; - serde_json::to_string(json).unwrap_or_else(|e| e.to_string()) - }, - Err(e) => e.to_string() - } + let res = CLIENT.get(format!("{}fhir", CONFIG.blaze_url)).send().await?; + let json = &res.json::().await?["total"]; + Ok(CheckResult::Ok(serde_json::to_string(json)?)) }, Check::BlazeVersion => { - match CLIENT.get(format!("{}fhir/metadata", CONFIG.blaze_url)).send().await { - Ok(res) => { - let json = &res.json::().await.unwrap_or(Value::Null)["software"]["version"]; - serde_json::to_string(json).unwrap_or_else(|e| e.to_string()) - }, - Err(e) => e.to_string() - } + let res = CLIENT.get(format!("{}fhir/metadata", CONFIG.blaze_url)).send().await?; + let json = &res.json::().await?["software"]["version"]; + Ok(CheckResult::Ok(serde_json::to_string(json)?)) } } } diff --git a/bin/src/main.rs b/bin/src/main.rs index 9df3046..23d1627 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -4,8 +4,8 @@ use beam_lib::{MsgId, TaskRequest, TaskResult, WorkStatus, AppId}; use checks::CheckExecutor; use clap::Parser; use config::Config; -use bridgehead_monitoring_lib::Check; -use futures::future::join_all; +use bridgehead_monitoring_lib::{Check, CheckResult}; +use futures::{future::join_all, FutureExt}; use once_cell::sync::Lazy; use reqwest::{Client, StatusCode, header::{HeaderMap, AUTHORIZATION, HeaderValue, ACCEPT}}; use serde_json::Value; @@ -33,7 +33,11 @@ async fn main() { let Some((checks, task_id, sender)) = poll_checks().await else { break; }; - let results = join_all(checks.iter().map(CheckExecutor::execute)).await; + let results = join_all(checks + .iter() + .map(CheckExecutor::execute) + .map(|f| f.map(|res| res.unwrap_or_else(|e| e)))) + .await; send_results(results, task_id, sender).await; } } @@ -81,7 +85,7 @@ async fn poll_checks() -> Option<(Vec, MsgId, AppId)> { None } -async fn send_results(results: Vec, task_id: MsgId, sender: AppId) { +async fn send_results(results: Vec, task_id: MsgId, sender: AppId) { let url = CONFIG.beam_proxy_url.join(&format!("/v1/tasks/{task_id}/results/{}", CONFIG.beam_id)).unwrap(); let resp = CLIENT.put(url).json(&TaskResult { diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 9c43958..3b1cbc6 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, error::Error}; use serde::{Serialize, Deserialize}; @@ -20,3 +20,16 @@ impl fmt::Display for Check { f.write_str(name) } } + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum CheckResult { + Ok(String), + Err(String), + Unexpected(String) +} + +impl From for CheckResult { + fn from(value: E) -> Self { + Self::Err(value.to_string()) + } +}