Implement the simplest of runners using the step libraries
Using the following test pipeline file:
# This file is just a pretend primitive test pipeline with a bunch of steps
---
steps:
- symbol: sh
parameters:
script: 'ls -lah | tail -n 5'
- symbol: sh
parameters:
script: 'echo "Hello world from a script"'
- symbol: unknown
parameters:
message: 'this should fail'
And then invoking as such:
❯ STEPS_DIR=tmp ./target/debug/primitive-agent test-pipeline.yml
sh exists
sh exists
unknown/manifest.yml does not exist, step cannot execute
NORMALLY THIS WOULD ERROR BEFORE ANYTHING EXECUTES
---
entry: "tmp/sh/sh-step"
-rw-r--r-- 1 tyler users 1.1K Feb 20 2020 system.dot
-rw-r--r-- 1 tyler users 43K Feb 20 2020 system.png
drwxr-xr-x 7 tyler users 4.0K Oct 17 15:25 target
-rw-r--r-- 1 tyler users 304 Oct 18 15:04 test-pipeline.yml
drwxr-xr-x 4 tyler users 4.0K Oct 17 16:07 tmp
entry: "tmp/sh/sh-step"
Hello world from a script
So at a _very_ _very_ primitive level the concept is working 👏
This commit is contained in:
parent
3760508cb8
commit
a5de9294aa
|
@ -628,12 +628,13 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "primitive"
|
||||
name = "primitive-agent"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"osp 0.1.0",
|
||||
"serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_yaml 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "primitive"
|
||||
name = "primitive-agent"
|
||||
version = "0.1.0"
|
||||
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
|
||||
edition = "2018"
|
||||
|
@ -8,3 +8,4 @@ edition = "2018"
|
|||
serde_yaml = "~0.8.13"
|
||||
serde = {version = "~1.0.117", features = ["rc", "derive"]}
|
||||
osp = { path = "../../osp" }
|
||||
tempfile = "~3.1.0"
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde_yaml::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{stdout, stderr, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct Pipeline {
|
||||
|
@ -12,10 +17,82 @@ struct Pipeline {
|
|||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct Step {
|
||||
symbol: String,
|
||||
parameters: HashMap<String, String>,
|
||||
parameters: Value,
|
||||
}
|
||||
|
||||
fn run(steps_dir: &str, steps: &Vec<Step>) -> std::io::Result<()> {
|
||||
let dir = Path::new(steps_dir);
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
if ! dir.is_dir() {
|
||||
panic!("STEPS_DIR must be a directory! {:?}", dir);
|
||||
}
|
||||
|
||||
let mut manifests: HashMap<String, osp::Manifest> = HashMap::new();
|
||||
let mut m_paths: HashMap<String, PathBuf> = HashMap::new();
|
||||
|
||||
for step in steps.iter() {
|
||||
let manifest_file = dir.join(&step.symbol).join("manifest.yml");
|
||||
|
||||
if manifest_file.is_file() {
|
||||
println!("{} exists", step.symbol);
|
||||
|
||||
let file = File::open(manifest_file)?;
|
||||
// TODO: This is dumb and inefficient
|
||||
m_paths.insert(step.symbol.clone(), dir.join(&step.symbol).to_path_buf());
|
||||
manifests.insert(step.symbol.clone(),
|
||||
serde_yaml::from_reader::<File, osp::Manifest>(file).expect("Failed to parse manifest")
|
||||
);
|
||||
}
|
||||
else {
|
||||
println!("{}/manifest.yml does not exist, step cannot execute", step.symbol);
|
||||
println!("NORMALLY THIS WOULD ERROR BEFORE ANYTHING EXECUTES");
|
||||
}
|
||||
}
|
||||
println!("---");
|
||||
|
||||
// Now that things are valid and collected, let's executed
|
||||
for step in steps.iter() {
|
||||
if let Some(runner) = manifests.get(&step.symbol) {
|
||||
let m_path = m_paths.get(&step.symbol).expect("Failed to grab the step library path");
|
||||
let entrypoint = m_path.join(&runner.entrypoint.path);
|
||||
println!("entry: {:?}", entrypoint);
|
||||
|
||||
let mut file = NamedTempFile::new()?;
|
||||
let mut step_args = HashMap::new();
|
||||
step_args.insert("parameters", &step.parameters);
|
||||
|
||||
serde_yaml::to_writer(&mut file, &step_args)
|
||||
.expect("Failed to write temporary file for script");
|
||||
|
||||
let output = Command::new(entrypoint)
|
||||
.arg(file.path())
|
||||
.output()
|
||||
.expect("Failed to invoke the script");
|
||||
stdout().write_all(&output.stdout).unwrap();
|
||||
stderr().write_all(&output.stderr).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()>{
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let steps_dir = std::env::var("STEPS_DIR").expect("STEPS_DIR must be defined");
|
||||
|
||||
if args.len() != 2 {
|
||||
panic!("The sh step can only accept a single argument: the parameters file path");
|
||||
}
|
||||
|
||||
let file = File::open(&args[1])?;
|
||||
|
||||
match serde_yaml::from_reader::<File, Pipeline>(file) {
|
||||
Err(e) => {
|
||||
panic!("Failed to parse parameters file: {:#?}", e);
|
||||
}
|
||||
Ok(invoke) => {
|
||||
run(&steps_dir, &invoke.steps);
|
||||
},
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Manifest {
|
||||
symbol: String,
|
||||
description: String,
|
||||
includes: Vec<Include>,
|
||||
entrypoint: Entrypoint,
|
||||
parameters: Vec<Parameter>,
|
||||
pub symbol: String,
|
||||
pub description: String,
|
||||
pub includes: Vec<Include>,
|
||||
pub entrypoint: Entrypoint,
|
||||
pub parameters: Vec<Parameter>,
|
||||
}
|
||||
|
||||
impl Manifest {
|
||||
|
@ -52,21 +52,21 @@ impl Manifest {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct Include {
|
||||
pub struct Include {
|
||||
name: String,
|
||||
#[serde(default = "default_false")]
|
||||
flatten: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct Entrypoint {
|
||||
path: PathBuf,
|
||||
pub struct Entrypoint {
|
||||
pub path: PathBuf,
|
||||
#[serde(default = "default_false")]
|
||||
multiarch: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct Parameter {
|
||||
pub struct Parameter {
|
||||
name: String,
|
||||
required: bool,
|
||||
#[serde(rename = "type")]
|
||||
|
@ -75,7 +75,7 @@ struct Parameter {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
enum ParameterType {
|
||||
pub enum ParameterType {
|
||||
#[serde(rename = "string")]
|
||||
StringParameter,
|
||||
#[serde(rename = "boolean")]
|
||||
|
|
Loading…
Reference in New Issue