-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: support containerized plugins
Signed-off-by: Aisha M <[email protected]>
- Loading branch information
Showing
9 changed files
with
274 additions
and
9 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "activity-container" | ||
version = "0.4.0" | ||
license = "Apache-2.0" | ||
edition = "2021" | ||
publish = false | ||
|
||
[dependencies] | ||
clap = { version = "4.5.27", features = ["derive"] } | ||
hipcheck-sdk = { path = "../../sdk/rust", features = [ | ||
"macros", | ||
] } | ||
jiff = { version = "0.1.16", features = ["serde"] } | ||
log = "0.4.22" | ||
schemars = { version = "0.8.21", features = ["url"] } | ||
serde = { version = "1.0.215", features = ["derive", "rc"] } | ||
serde_json = "1.0.134" | ||
tokio = { version = "1.43.0", features = ["rt"] } | ||
|
||
[dev-dependencies] | ||
hipcheck-sdk = { path = "../../sdk/rust", features = [ | ||
"mock_engine", | ||
] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
FROM debian:bookworm-slim | ||
|
||
WORKDIR /app | ||
|
||
COPY ../../target/debug/activity /app/activity | ||
|
||
RUN chmod +x /app/activity | ||
|
||
EXPOSE 50051 | ||
|
||
ENTRYPOINT ["/app/activity", "--port", "50051"] |
32 changes: 32 additions & 0 deletions
32
test-plugins/activity-container/activity-container-deploy.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#!/bin/bash | ||
|
||
# Default values | ||
IMAGE_TAR="./test-plugins/activity-container/activity-image.tar" | ||
IMAGE_NAME="activity-image" | ||
PORT=8888 | ||
|
||
while [[ $# -gt 0 ]]; do | ||
if [[ "$1" == "--port" && -n "$2" && "$2" =~ ^[0-9]+$ ]]; then | ||
PORT="$2" | ||
shift 2 | ||
else | ||
echo "Unknown or invalid argument: $1" | ||
exit 1 | ||
fi | ||
done | ||
|
||
if [[ ! -f "$IMAGE_TAR" ]]; then | ||
echo "Error: Image tar file '$IMAGE_TAR' not found!" | ||
exit 1 | ||
fi | ||
|
||
|
||
# Check if the image is already loaded | ||
if ! docker images | grep -q "$IMAGE_NAME"; then | ||
echo "Image '$IMAGE_NAME' not found. Loading the image..." | ||
docker load -i "$IMAGE_TAR" > /dev/null 2>&1 | ||
fi | ||
# Otherwise, the image is already loaded | ||
|
||
# Format the run statement for container port mapping | ||
docker run --init -p "$PORT":50051 activity-image |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
publisher "mitre" | ||
name "activity-container" | ||
version "0.0.0" | ||
license "Apache-2.0" | ||
|
||
entrypoint { | ||
on arch="aarch64-apple-darwin" "activity-container-deploy.sh" | ||
on arch="x86_64-apple-darwin" "activity-container-deploy.sh" | ||
on arch="x86_64-unknown-linux-gnu" "activity-container-deploy.sh" | ||
on arch="x86_64-pc-windows-msvc" "activity-container-deploy.sh" | ||
} | ||
|
||
dependencies { | ||
plugin "mitre/git" version="0.0.0" manifest="./plugins/git/local-plugin.kdl" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//! Plugin for querying how long it has been since a commit was last made to a repo | ||
use clap::Parser; | ||
use hipcheck_sdk::{prelude::*, types::Target}; | ||
use jiff::Timestamp; | ||
use serde::Deserialize; | ||
use std::{result::Result as StdResult, sync::OnceLock}; | ||
|
||
#[derive(Deserialize)] | ||
struct Config { | ||
weeks: Option<u16>, | ||
} | ||
|
||
static CONFIG: OnceLock<Config> = OnceLock::new(); | ||
|
||
/// Returns the span of time since the most recent commit to a Git repo as `jiff:Span` displayed as a String | ||
/// (Which means that anything expecting a `Span` must parse the output of this query appropriately) | ||
#[query(default)] | ||
async fn activity(engine: &mut PluginEngine, target: Target) -> Result<String> { | ||
log::debug!("running activity query"); | ||
|
||
let repo = target.local; | ||
|
||
// Get today's date | ||
let today = Timestamp::now(); | ||
|
||
// Get the date of the most recent commit. | ||
let value = engine | ||
.query("mitre/git/last_commit_date", repo) | ||
.await | ||
.map_err(|e| { | ||
log::error!("failed to get last commit date for activity metric: {}", e); | ||
Error::UnspecifiedQueryState | ||
})?; | ||
|
||
let Value::String(date_string) = value else { | ||
return Err(Error::UnexpectedPluginQueryInputFormat); | ||
}; | ||
let last_commit_date: Timestamp = date_string.parse().map_err(|e| { | ||
log::error!("{}", e); | ||
Error::UnspecifiedQueryState | ||
})?; | ||
|
||
// Get the time between the most recent commit and today. | ||
let time_since_last_commit = today.since(last_commit_date).map_err(|e| { | ||
log::error!("{}", e); | ||
Error::UnspecifiedQueryState | ||
})?; | ||
|
||
Ok(time_since_last_commit.to_string()) | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
struct ActivityPlugin; | ||
|
||
impl Plugin for ActivityPlugin { | ||
const PUBLISHER: &'static str = "mitre"; | ||
|
||
const NAME: &'static str = "activity"; | ||
|
||
fn set_config(&self, config: Value) -> StdResult<(), ConfigError> { | ||
let conf = | ||
serde_json::from_value::<Config>(config).map_err(|e| ConfigError::Unspecified { | ||
message: e.to_string(), | ||
})?; | ||
CONFIG.set(conf).map_err(|_e| ConfigError::Unspecified { | ||
message: "config was already set".to_owned(), | ||
}) | ||
} | ||
|
||
fn default_policy_expr(&self) -> Result<String> { | ||
let Some(conf) = CONFIG.get() else { | ||
log::error!("tried to access config before set by Hipcheck core!"); | ||
return Err(Error::UnspecifiedQueryState); | ||
}; | ||
|
||
Ok(format!("(lte $ P{}w)", conf.weeks.unwrap_or(71))) | ||
} | ||
|
||
fn explain_default_query(&self) -> Result<Option<String>> { | ||
Ok(Some( | ||
"span of time that has elapsed since last activity in repo".to_string(), | ||
)) | ||
} | ||
|
||
queries! {} | ||
} | ||
|
||
#[derive(Parser, Debug)] | ||
struct Args { | ||
#[arg(long)] | ||
p: u16, | ||
} | ||
|
||
#[tokio::main(flavor = "current_thread")] | ||
async fn main() -> Result<()> { | ||
let args = Args::try_parse().unwrap(); | ||
log::info!("Activity container plugin is registering {:?}", args); | ||
PluginServer::register(ActivityPlugin {}) | ||
.listen(args.p) | ||
.await | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
use hipcheck_sdk::types::LocalGitRepo; | ||
use jiff::{Span, SpanRound, Unit}; | ||
use std::result::Result as StdResult; | ||
|
||
fn repo() -> LocalGitRepo { | ||
LocalGitRepo { | ||
path: "/home/users/me/.cache/hipcheck/clones/github/expressjs/express/".to_string(), | ||
git_ref: "main".to_string(), | ||
} | ||
} | ||
|
||
fn mock_responses() -> StdResult<MockResponses, Error> { | ||
let repo = repo(); | ||
let output = "2024-06-19T19:22:45Z".to_string(); | ||
|
||
// when calling into query, the input repo gets passed to `last_commit_date`, lets assume it returns the datetime `output` | ||
let mut mock_responses = MockResponses::new(); | ||
mock_responses.insert("mitre/git/last_commit_date", repo, Ok(output))?; | ||
Ok(mock_responses) | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_activity() { | ||
let repo = repo(); | ||
let target = Target { | ||
specifier: "express".to_string(), | ||
local: repo, | ||
remote: None, | ||
package: None, | ||
}; | ||
|
||
let mut engine = PluginEngine::mock(mock_responses().unwrap()); | ||
let output = activity(&mut engine, target).await.unwrap(); | ||
let span: Span = output.parse().unwrap(); | ||
let result = span.round(SpanRound::new().smallest(Unit::Day)).unwrap(); | ||
|
||
let today = Timestamp::now(); | ||
let last_commit: Timestamp = "2024-06-19T19:22:45Z".parse().unwrap(); | ||
let expected = today | ||
.since(last_commit) | ||
.unwrap() | ||
.round(SpanRound::new().smallest(Unit::Day)) | ||
.unwrap(); | ||
|
||
assert_eq!(result, expected); | ||
} | ||
} |